A commencé à étudier la complexité, je me bats avec celui-ci:
void what(int n) {
int i;
for (i = 1; i <= n; i++) {
int x = n;
while (x > 0)
x -= i;
}
}
Eh bien, la première boucle for est clairement O(n)
. La première itération est O(n)
, la seconde est O(n/2)
.. et comme ça log(n)
fois je suppose? Ce qui signifie O(n) * O(log(n)) = O(n * log(n)) complexity
. Ai-je bien compris?
Edit: (pas un doublon) Je sais ce qu'est Big O. J'ai demandé l'évaluation correcte dans un cas spécifique.
La boucle externe s'exécute n
fois.
Pour chaque itération, les boucles internes s'exécutent n / i
Fois.
Le nombre total de runs est:
n + n/2 + n/3 + ... + n/n
De manière asymptotique (en ignorant l'arrondi arithmétique entier), cela se simplifie comme
n * (1 + 1/2 + 1/3 + ... + 1/n)
Cette série converge librement vers n * log(n)
.
La complexité est donc O (N.log (N)) comme vous vous y attendiez.
La première boucle interne s'exécute n
fois
La deuxième boucle interne s'exécute n/2
Fois
La troisième boucle intérieure s'exécute n/3
Fois
.. Le dernier s'exécute une fois
Donc n + n/2 + n/3 + ... + 1 = n(1+1/2+1/3+1/4 + ... +1/n)
.
Ceci est n multiplié par la somme des séries harmoniques, qui n'a pas de représentation de forme fermée de Nice. Mais comme indiqué ici c'est O(log(n))
. Donc, globalement, l'algorithme est O(n log(n))
Comme alternative, utilisez une substitution de variable y = n - x
Suivie de la notation Sigma pour analyser le nombre d'itérations de la boucle interne while
de votre algorithme.
Ce qui précède surestime, pour chaque boucle while interne, le nombre d'itérations par 1
Pour les cas où n-1
N'est pas un multiple de i
, c'est-à-dire où (n-1) % i != 0
. Au fur et à mesure, nous supposerons que (n-1)/i
Est un entier pour toutes les valeurs de i
, surestimant ainsi le nombre total d'itérations dans la boucle interne while
, y compris ensuite le signe inférieur ou égal à (i)
ci-dessous. Nous procédons à l'analyse de la notation Sigma:
où nous, à (ii)
, avons approximé le n
: th numéro harmonique par l'intégrale associée. Par conséquent, votre algorithme s'exécute dans O(n·ln(n))
, asymptotiquement.
En laissant un comportement asymptotique et en étudiant la croissance réelle de l'algorithme, nous pouvons utiliser les exemples de données Nice de paires exactes de (n,cnt)
(Où cnt
est le nombre d'itérations internes) par @chux (se référer à sa réponse) et comparez avec le nombre estimé d'itérations ci-dessus, c'est-à-dire n(1+ln(n))-ln(n)
. Il est évident que l'estimation s'harmonise parfaitement avec le nombre réel, voir les graphiques ci-dessous ou cet extrait pour les nombres réels .
Enfin, notez que si nous laissons n->∞
Dans la somme de 1/i
Ci-dessus, la série résultante est la série harmonique infinie , ce qui est, curieusement, divergent. La preuve de ce dernier fait usage du fait que, dans une somme infinie de termes non nuls, il est naturellement infini lui-même. En pratique, cependant, pour un n suffisamment grand mais pas infini , ln(n)
est une approximation appropriée de la somme, et cette divergence n'est pas pertinent pour notre analyse asymptotique ici.
Tenter cela par des tests et des graphiques. Le graphique log2 vs log2 semble assez linéaire. Quelque chose entre plus que linéaire O(n) et moins que O (n * log (n)). "Dessinez" votre propre conclusion.
[Modifier]
En utilisant les formules dérivées mathématiques, le O() calculé est une limite supérieure de O (n * log (n)). Il utilise des "fractions d'itérations de boucle" qui augmentent le nombre d'une fraction et non 1. Par exemple, lorsque n
est 6, le nombre d'itérations est 6 + 3 + 2 + 1,5 + 1,2 + 1 = 14,7 boucles plutôt que 6 + 3 + 2 + 2 + 2 + 1 = 16. la différence est relativement moins importante à mesure que n
augmente, donc la croissance légèrement inférieure à O (n * log (n)). Donc, en n'ignorant pas le code utilisant des mathématiques entières, nous avons une question beaucoup plus difficile.
unsigned long long what(int n) {
unsigned long long cnt = 0;
int i;
for (i = 1; i <= n; i++) {
int x = n;
while (x > 0) {
x -= i;
cnt++;
}
}
return cnt;
}
void wtest(int n) {
unsigned long long cnt = what(n);
printf("%d %llu\n", n, cnt);
fflush(stdout);
}
void wtests(void) {
int i = INT_MAX/2 + 1;
while (i > 0) {
wtest(i);
i /= 2;
}
}
int main(void) {
wtests();
return 0;
}
Sortie
1073741824 23567395117
536870912 11411566988
268435456 5519718329
134217728 2666826555
67108864 1286897093
33554432 620190504
16777216 298466265
8388608 143418602
4194304 68802063
2097152 32947406
1048576 15746897
524288 7510048
262144 3573331
131072 1695816
65536 802493
32768 378537
16384 177921
8192 83286
4096 38803
2048 17973
1024 8275
512 3782
256 1713
128 765
64 337
32 145
16 61
8 24
4 9
2 3
1 1