Quelles sont les caractéristiques d'un algorithme de complexité temporelle

19

Parfois, il est facile d'identifier la complexité temporelle d'un algorithme en l'examinant attentivement. Les algorithmes avec deux boucles imbriquées de sont évidemment . Les algorithmes qui explorent toutes les combinaisons possibles de groupes de deux valeurs sont évidemment .N 2 N 2 NNN2N2N

Cependant, je ne sais pas comment "repérer" un algorithme avec une complexité . Une implémentation récursive de fusion, par exemple, en est une. Quelles sont les caractéristiques communes du mergesort ou d'autres algorithmes qui me donneraient un indice si j'en analysais un?Θ ( N log N )Θ(NlogN)Θ(NlogN)

Je suis sûr qu'il existe plus d'une façon dont un algorithme peut être de complexité , donc toutes les réponses sont appréciées. BTW Je cherche des caractéristiques générales et des conseils, pas des preuves rigoureuses.Θ(NlogN)

Barry Fruitman
la source
6
O(logn) signifie arbre.
Pratik Deoghare
2
@PratikDeoghare: Pas nécessairement.
Raphael
3
@Raphael je voulais dire surtout! :)
Pratik Deoghare

Réponses:

17

Votre archétype est un algorithme de division et de conquête , qui divise (et recombine) le travail en temps linéaire et se reproduit sur les pièces. Le tri par fusion fonctionne de cette façon: passez O ( n ) temps à diviser l'entrée en deux morceaux à peu près égaux, triez récursivement chaque morceau et passez Θ ( n ) temps à combiner les deux moitiés triées.Θ(nlogn)O(n)Θ(n)

Intuitivement, poursuivant l'idée de diviser pour mieux régner, chaque étape de division prend un temps linéaire au total, car l'augmentation du nombre de pièces à diviser correspond exactement à la diminution de la taille des pièces, car le temps pris par la division est linéaire. Le temps de fonctionnement total est le produit du coût total d'une étape de division multiplié par le nombre d'étapes de division. Étant donné que la taille des pièces est divisée par deux à chaque fois, il existe étapes de division log 2 ( n ) , de sorte que le temps de fonctionnement total est n log ( n ) . (Jusqu'à une constante multiplicative, la base du logarithme n'est pas pertinente.)log2(n)nlog(n)

En le mettant dans les équations (), une façon d'estimer le temps d'exécution d'un tel algorithme est de l'exprimer récursivement: T ( n ) = 2 T ( n / 2 ) + Θ ( n ) . Il est clair que cet algorithme prend plus que du temps linéaire, et nous pouvons voir combien plus en divisant par n : T ( n )T(n)T(n)=2T(n/2)+Θ(n)n

T(n)n=T(n/2)n/2+Θ(1)
Lorsquendouble,T(n)/naugmente d'une quantité constante:T ( n ) = Θ ( n log n )T(n)/n augmente logarithmiquement, ou en d'autres termes, .T(n)=Θ(nlogn)

Ceci est une instance d'un modèle plus général: le théorème maître . Pour tout algorithme récursif qui divise son entrée de taille dans des morceaux de taille et prend un temps pour effectuer la division et de recombinaison, le temps d' exécution satisfait . Cela conduit à une forme fermée qui dépend des valeurs de et et de la forme de . Si et , le théorème maître déclare que .a n / b f ( n ) T ( n ) = a T ( n / b ) + f ( n ) a b f a = b f ( n ) = Θ ( n ) T ( n ) = Θ ( n log n )nan/bf(n)T(n)=aT(n/b)+f(n)abfa=bf(n)=Θ(n)T(n)=Θ(nlogn)

Gilles 'SO- arrête d'être méchant'
la source
1
Pour résumer: les algorithmes qui suppriment des fractions constantes de l'espace de recherche à la fois présenteront des termes logarithmiques. Les autres facteurs dépendent du temps nécessaire pour effectuer la suppression.
Raphael
1
exemple: quicksort cas moyen O(nlogn)
vzn
11

Deux autres catégories d'algorithmes qui prennent du temps :Θ(nlogn)

Algorithmes où chaque élément est traité tour à tour, et il faut du temps logarithmique pour traiter chaque élément (par exemple HeapSort ou de nombreux algorithmes de géométrie de calcul de balayage plan).

Algorithmes où le temps d'exécution est dominé par une étape de prétraitement de tri. (Par exemple, dans l'algorithme de Kruskal pour un arbre couvrant minimum, nous pouvons trier les bords par poids comme première étape).

Joe
la source
A voté pour le premier paragraphe. Le second semble redondant, car les algorithmes de tri linéaires que nous connaissons sont soit diviser pour mieux régner, soit pour trier. Un exemple pour la première catégorie est la recherche de objets dans un arbre de recherche binaire (ou tableau trié) de taille n ; à un niveau plus abstrait, cela peut aussi être vu comme diviser pour mieux régner. nn
Raphael
@Raphael Je sais que le tri est redondant avec les catégories de temps d'exécution déjà données. Le fait est que le tri est parfois le "goulot d'étranglement" et les algorithmes où au premier abord ne se posent pas la question du tri peuvent toujours avoir le temps d'exécution car le tri est nécessaire.
Joe
9

Autre catégorie: les algorithmes dans lesquels la sortie a la taille , et donc le temps d'exécution Θ ( n log n ) est linéaire dans la taille de la sortie .Θ(nlogn)Θ(nlogn)

Bien que les détails de ces algorithmes utilisent souvent des techniques de division et de conquête, ils ne doivent pas nécessairement le faire. Le temps d'exécution vient fondamentalement de la question posée, et je pense donc qu'il vaut la peine d'être mentionné séparément.

Cela apparaît dans les structures de données qui sont basées sur un arbre de recherche binaire augmenté, où chaque nœud stocke une structure de données de taille linéaire pour rechercher sur les feuilles dans le sous-arbre de ce nœud. De telles structures de données apparaissent souvent dans la recherche par plage géométrique et sont souvent basées sur un schéma de décomposition . Voir le sondage d'Agarwal .

Pour un exemple concret, considérons l' arborescence , conçue pour répondre aux requêtes de plages orthogonales bidimensionnelles. Bien que l'espace ait ensuite été réduit à l'aide de certaines techniques de compression pour regrouper plusieurs objets en un seul mot, la version de manuel (et la plus intuitive) de la structure de données nécessite un espace (chaque feuille est stockée dans une structure auxiliaire à chaque nœud sur le chemin de la feuille à la racine, ou en O ( log n ) ), et l'algorithme de construction prend du temps linéaire dans l'espace requis .O(nlogn)O(logn)

Joe
la source
Un exemple serait de trouver le nième nombre à l'aide d'un tamis, car vous auriez besoin d'un tamis de taille . θ(nlogn)
gnasher729
6

Une complexité de provient des algorithmes de division et de conquête qui divisent leur entrée en k morceaux de taille à peu près égale dans le temps O ( n ) , opèrent sur ces morceaux de manière récursive, puis les combinent dans le temps O ( n ) . Si vous n'opérez que sur certaines pièces, le temps d'exécution passe à O ( n ) .O(nlogn)kO(n)O(n)O(n)

Yuval Filmus
la source
5

Ce sont généralement des algorithmes de la variété "diviser pour mieux régner", où le coût de la division et de la combinaison des sous-solutions n'est pas "trop ​​important". Jetez un œil à cette FAQ pour voir quels types de récidives provoquent ce comportement.

vonbrand
la source
3

En règle générale, les algorithmes de division et de conquête généreront une complexité O (N log N).

Brent Hronik
la source
-1

O(nlogn)

for (i = 0; i < constant; i++){
    for(j = 0; j < n; j++){
        // Do some O(1) stuff on the input
    }
}

// Alternative Variant:
for (i = 0; i < constant; i++){
    for(j = n; j < constant; j++){
        // Do some O(1) stuff on the input
    }
}

O(n2)

Je ne peux pas donner un exemple concret d'un algorithme qui utilise cette boucle, mais il revient souvent lors du codage d'algorithmes personnalisés.

Nicolas Miari
la source
Oui, ce sont les deux O(nJournaln)mais pour la raison triviale que la limite n'est pas serrée. Le premier estΘ(n) et le second est Θ(1) (or Θ(|n|) if n can be negative).
David Richerby
I thought it useful to mention, since it does come up in practice but every search for "O(nlogn)" is mostly "divide-and-conquer" examples like e.g. merge sort.
Nicolas Miari
additionally, the original question didn't ask for big-theta.
Nicolas Miari
1
Sure but any algorithm that runs in linear or constant time is O(nlogn), as well as being O(22n) and O(almost anything else). Your answer has nothing to do with logarithms. If you want to make the point that the question should be asking about Θ(nlogn) rather than O(nlogn) then either make that point explicitly as comment to the question or, even better, edit the question.
David Richerby
I wanted to make the point that I actually searched the internet for O(nlogn) to see if the loop in my answer, that I had to come up with for an actual answer to an actual programming problem that was required to run in O(nlogn), was actually O(nlogn) or not. And most information around deals with divide-and-conquer algorithms, so it would be worth mentioning. I do not pretend my answer to be the archetypical O(nlogn) algorithm.
Nicolas Miari