Supposons que j'ai deux algorithmes:
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
//do something in constant time
}
}
C'est naturellement O(n^2)
. Supposons que j'ai également:
for (int i = 0; i < 100; i++) {
for (int j = 0; j < n; j++) {
//do something in constant time
}
}
C'est O(n) + O(n) + O(n) + O(n) + ... O(n) + = O(n)
Il semble que même si mon deuxième algorithme est O(n)
, cela prendra plus de temps. Quelqu'un peut-il développer cela? J'en parle parce que je vois souvent des algorithmes où ils effectueront, par exemple, une étape de tri en premier ou quelque chose comme ça, et lors de la détermination de la complexité totale, ce n'est que l'élément le plus complexe qui délimite l'algorithme.
La complexité asymptotique (qui représente à la fois big-O et big-Theta) ignore complètement les facteurs constants impliqués - elle est uniquement destinée à donner une indication de la façon dont le temps d'exécution changera à mesure que la taille de l'entrée augmentera.
Il est donc certainement possible qu'un algorithme Θ(n)
prenne plus de temps qu'un Θ(n2)
un pour certains n
- qui n
cela se produira vraiment dépendent des algorithmes impliqués - pour votre exemple spécifique, ce sera le cas pour n < 100
, en ignorant la possibilité d'optimisations différentes entre les deux.
Pour deux algorithmes donnés prenant Θ(n)
et Θ(n2)
respectivement, ce que vous verrez probablement c'est que:
Θ(n)
est plus lent lorsque n
est petit, puis Θ(n2)
devient plus lent lorsque n
augmenteΘ(n)
est plus complexe, c'est-à-dire a des facteurs constants plus élevés), ouΘ(n2)
est toujours plus lente.Bien qu'il soit certainement possible que l'algorithme Θ(n)
puisse être plus lent, puis Θ(n2)
one, puis Θ(n)
une fois de plus, et ainsi de suite lorsque n
augmente, jusqu'à ce que n
devienne très grand, à partir de ce moment la Θ(n2)
one sera toujours plus lente, bien que cela ne se produise pas.
Disons que l'algorithme Θ(n2)
prend des opérations cn2
Pour certains c
.
Et l'algorithme Θ(n)
prend dn
opérations pour certains d
.
Ceci est conforme à la définition formelle puisque nous pouvons supposer que cela vaut pour n
supérieur à 0 (c'est-à-dire pour tous n
) et que les deux fonctions entre lesquelles le le temps de course est le mensonge est le même.
Conformément à votre exemple, si vous deviez dire c = 1
Et d = 100
, L'algorithme Θ(n)
serait plus lent jusqu'à n = 100
, Point auquel le Θ(n2)
l'algorithme deviendrait plus lent.
(avec l'aimable autorisation de WolframAlpha ).
Techniquement, big-O n'est qu'une limite supérieure, ce qui signifie que vous pouvez dire qu'un algorithme O(1)
(ou vraiment n'importe quel algorithme prenant O(n2)
ou moins de temps) prend également O(n2)
. J'ai donc plutôt utilisé la notation big-Theta (Θ), qui est juste une limite étroite. Voir les définitions formelles pour plus d'informations.
Le Big-O est souvent traité de manière informelle ou est enseigné comme une limite stricte, donc vous avez peut-être déjà utilisé essentiellement du big-Theta sans le savoir.
Si nous parlons uniquement d'une limite supérieure (selon la définition formelle de big-O), ce serait plutôt une situation "tout va bien" - la fonction O(n)
peut être plus rapide, la fonction O(n2)
on peut être plus rapide ou prendre le même temps (asymptotiquement) - on ne peut généralement pas tirer de conclusions particulièrement significatives en ce qui concerne la comparaison le big-O des algorithmes, on peut seulement dire que, étant donné le big-O d'un algorithme, que cet algorithme ne prendra pas plus de temps (asymptotiquement).
Oui, un algorithme O(n) peut dépasser un O (n2) algorithme en termes de durée de fonctionnement. Ceci se produit lorsque le facteur constant (que nous omettons dans la grande notation O) est grand. Par exemple, dans votre code ci-dessus, l'algorithme O(n) aura un grand facteur constant. Il fonctionnera donc moins bien qu'un algorithme qui s'exécute en O (n2) pour n <10.
Ici, n = 100 est le point de croisement. Ainsi, lorsqu'une tâche peut être effectuée à la fois O(n) et O (n2) et le facteur constant de l'algorithme linéaire est supérieur à celui d'un algorithme quadratique, alors nous avons tendance à préférer l'algorithme avec le pire temps de fonctionnement. Par exemple, lors du tri d'un tableau, nous passons au tri par insertion pour les tableaux plus petits, même lorsque le tri par fusion ou le tri rapide s'exécutent asymptotiquement plus rapidement. En effet, le tri par insertion a un facteur constant plus petit que le tri par fusion/tri rapide et s'exécute plus rapidement.
Les grandes O(n)
ne sont pas destinées à comparer la vitesse relative de différents algorithmes. Ils sont destinés à mesurer la vitesse à laquelle le temps de fonctionnement augmente lorsque la taille de l'entrée augmente. Par exemple,
O(n)
signifie que si n
est multiplié par 1000, le temps d'exécution est approximativement multiplié par 1000.O(n^2)
signifie que si n
est multiplié par 1000, la course est approximativement multipliée par 1000000.Ainsi, lorsque n
est suffisamment grand, tout algorithme O(n)
battra un algorithme O(n^2)
. Cela ne signifie rien pour un n
fixe.
Pour faire court, oui, c'est possible. La définition de O
est basée sur le fait que O(f(x)) < O(g(x))
implique que g(x)
mettra définitivement plus de temps à exécuter que f(x)
étant donné un assez grand x
.
Par exemple, est un fait connu que pour les petites valeurs, le tri par fusion est plus performant que le tri par insertion (si je me souviens bien, cela devrait être vrai pour n
plus petit que 31
)
Oui. Le O() ne signifie que la complexité asymptotique. L'algorithme linéaire peut être plus lent que le quadratique, s'il a la même constante de ralentissement linéaire suffisamment grande (par exemple, si le cœur de la boucle fonctionne 10 fois). plus long, il sera plus lent que sa version quadratique).
La notation O () n'est qu'une extrapolation, bien que très bonne.
La seule garantie que vous obtenez est que, quels que soient les facteurs constants, pour un assez grand n
, l'algorithme O(n) sera passe moins d'opérations que celle O (n ^ 2).
À titre d'exemple, comptons les opérations dans l'exemple soigné des OP. Ses deux algorithmes diffèrent sur une seule ligne:
for (int i = 0; i < n; i++) { (* A, the O(n*n) algorithm. *)
vs.
for (int i = 0; i < 100; i++) { (* B, the O(n) algorithm. *)
Puisque le reste de ses programmes sont les mêmes, la différence de temps de fonctionnement réel sera décidée par ces deux lignes.
Bien sûr, la taille n doit être grande pour que l'algorithme O(n) soit plus rapide) dépend du facteur constant. Si vous modifiez la constante 100
à 1000
en B, le seuil passe également à 1000.