Dans ma classe Algorithmes et structures de données, un premier divide-and-conquer algorithm
à savoir merge sort
a été présenté.
Lors de la mise en œuvre d'un algorithme pour une affectation, quelques questions me sont venues à l'esprit.
Est-ce qu'un algorithme implémenté avec l'utilisation du paradigme diviser pour régner a une complexité temporelle de O (nlogn)?
Est-ce que la partie récursive de l'approche a le pouvoir de condenser un algorithme qui s'exécute en O (n ^ 2) en O (nlogn)?
Qu'est-ce qui fait qu'un tel algorithme s'exécute dans O(nlogn) en premier lieu?
Pour (3), je suppose que cela a quelque chose à voir avec les arbres de récursivité et le nombre possible de récursions. Quelqu'un pourrait-il probablement montrer, avec un simple algorithme de division et de conquête qui s'exécute en O (nlogn), comment la complexité est réellement calculée?
À la vôtre, Andrew
Je pense que toutes les réponses à votre question pourraient provenir du Master Theorem Il vous dit en quelque sorte quelle serait votre complexité pour presque toutes les solutions de division et de conquête que vous avez, et oui, il doit tout faire avec arbres de récursivité, en jouant avec les paramètres, vous constaterez que certaines solutions de division et de conquête n'auront pas de complexité O(nlogn), en fait il y a des algorithmes de division et de conquête qui ont O(n) complexité .
En ce qui concerne la question 2, il n'est pas toujours possible, en fait, il y a certains problèmes qui sont considérés comme impossibles à résoudre plus rapidement que O (n ^ 2), cela dépend de la nature du problème.
Un exemple d'algorithme qui s'exécute en O(nlogn) et qui, selon moi, a une analyse très simple, claire et éducative du temps d'exécution est MergeSort . Il peut être saisi à partir de l'image suivante:
Donc, à chaque étape récursive, l'entrée est divisée en deux parties, puis la partie conquête prend O (n), donc chaque niveau de l'arbre coûte O (n), la partie délicate pourrait être de savoir comment est-il possible que le nombre de niveaux récursifs ( hauteur de l'arbre) est logn. C'est plus ou moins simple. Donc, à chaque étape, nous divisons l'entrée en 2 parties de n/2 éléments chacune, et répétons récursivement, jusqu'à ce que nous ayons une entrée de taille constante. Donc, au premier niveau, nous divisons n/2, sur le n/4 suivant, puis n/8, jusqu'à ce que nous atteignions une entrée de taille constante qui sera une feuille de l'arbre et la dernière étape récursive.
Donc, à la i-ème étape récursive, nous divisons n/2 ^ i, nous allons donc trouver la valeur de i à la dernière étape. Nous avons besoin de n/2 ^ i = O (1), ceci est réalisé lorsque 2 ^ i = cn, pour une constante c, nous prenons donc le logarithme de base 2 des deux côtés et obtenons que i = clogn. Ainsi, la dernière étape récursive sera l'étape de reconnaissance, et donc l'arbre a une hauteur de reconnaissance.
Ainsi, le coût total de MergeSort sera cn pour chacun des niveaux (arborescents) récursifs de clogn, ce qui donne la complexité O(nlogn)).
En général, vous pouvez être sûr que votre algorithme aura une complexité O(nlogn) tant que l'étape récursive aura une complexité O(n), et yo diviser en b problèmes de taille n/b, ou encore plus général, si les parties sont des fractions linéaires de n qui s'additionnent à n. Dans une situation différente, il est très probable que vous aurez un runtime différent.
Pour revenir à la question 2, dans le cas de QuickSort, on peut passer de O (n ^ 2) à\Theta (nlogn) précisément parce que le cas aléatoire moyen atteint une partition Nice, bien que l'analyse d'exécution soit encore plus complexe que cela.
Non, diviser pour mieux régner ne garantit pas les performances de O(nlogn). Tout dépend de la façon dont le problème est simplifié à chaque récursivité.
Dans l'algorithme de tri par fusion, le problème d'origine est divisé en deux moitiés. Ensuite, une opération O(n) est effectuée sur les résultats. C'est de là que vient le O(n...).
Chacune des deux sous-opérations a maintenant son propre n
qui est la moitié de la taille de l'original. Chaque fois que vous récusez, vous divisez à nouveau le problème en deux. Cela signifie que le nombre de récursions sera log2 (n). C'est de là que vient le O(...logn).
Est-ce qu'un algorithme implémenté avec l'utilisation du paradigme diviser pour régner a une complexité temporelle de O (nlogn)?
En moyenne, Quicksort et Mergesort ont une complexité temporelle de O (n log (n)), mais ce n'est pas toujours nécessairement ainsi. Big O Cheat Sheet
Est-ce que la partie récursive de l'approche a le pouvoir de condenser un algorithme qui s'exécute comme O (n ^ 2) en O (nlogn)?
Il y a plus que ce qui paraît à l'oeil, cela dépendra d'autres choses, comme le nombre d'opérations par rapport à l'entrée pour chaque appel récursif.
Je recommande fortement ceci vidéo où vous pouvez voir pourquoi MergeSort est O (n log (n)).
Ce qui fait qu'un tel algorithme s'exécute dans O(nlogn) en premier lieu.
Encore une fois, c'est seulement et un indicateur du temps qu'un algorithme consomme par rapport à la taille de l'entrée, donc dire qu'un algorithme a une complexité temporelle de O (n log (n)) ne donne aucune information sur la façon dont le algorithme est implémenté, il dit simplement que lorsque l'entrée commence à augmenter beaucoup, le temps utilisé n'augmentera pas directement proportionnel, mais cela prendra plus de temps et plus.
Est-ce qu'un algorithme implémenté avec l'utilisation du paradigme diviser pour régner a une complexité temporelle de O (nlogn)?
Non, la formule générale de diviser pour mieux régner est:
2 est le nombre d'opérations à l'intérieur de chaque appel récursif, est l'appel récursif à la division avec des sous-problèmes, est le nombre linéaire d'opérations de conquête
Qu'est-ce qui fait qu'un tel algorithme fonctionne en O(nlogn) en premier lieu?
Un bon exemple du temps log-linéaire est l'algorithme de tri par fusion m:
Est-ce que la partie récursive de l'approche a le pouvoir de condenser un algorithme qui fonctionne comme O (n ^ 2) en O (nlogn)?
Le Théorème maître est utilisé pour déterminer le temps d'exécution des algorithmes de division et de conquête
Si la récurrence est sous cette forme
Puis
Exemple
a = 2
b = 4
d = 1/2
puisque 2 = 4 ^ 1/2 le cas 2 s'applique