J'ai une question dans la conception d'algorithmes sur la complexité. Dans cette question, un morceau de code est donné et je devrais calculer la complexité de ce code. Le pseudo-code est:
for(i=1;i<=n;i++){
j=i
do{
k=j;
j = j / 2;
}while(k is even);
}
J'ai essayé cet algorithme pour certains nombres. et j'ai obtenu des résultats différents. par exemple si n = 6 cette sortie d'algorithme est comme ci-dessous
i = 1 -> executes 1 time
i = 2 -> executes 2 times
i = 3 -> executes 1 time
i = 4 -> executes 3 times
i = 5 -> executes 1 time
i = 6 -> executes 2 times
Il n'y a pas de thème régulier, comment devrais-je calculer cela?
La limite supérieure donnée par les autres réponses est en réalité trop élevée. Cet algorithme a une exécution O(n), qui est une limite supérieure plus étroite que O (n * logn).
Preuve: Comptons le nombre total d'itérations effectuées par la boucle interne.
La boucle externe exécute n
fois. La boucle interne s'exécute au moins une fois pour chacun de ceux-ci.
i
, la boucle interne s'exécute au moins deux fois. Cela se produit n/2
fois.i
divisible par 4, la boucle interne s'exécute au moins trois fois. Cela se produit n/4
fois.i
divisible par 8, la boucle interne s'exécute au moins quatre fois. Cela se produit n/8
fois.Ainsi, le nombre total de fois que la boucle interne est exécutée est:
n + n/2 + n/4 + n/8 + n/16 + ... <= 2n
Le nombre total d’itérations de la boucle interne est compris entre n
et 2n
, c’est-à-dire que c’est (n).
Vous supposez toujours que vous obtenez le pire scénario dans chaque niveau.
maintenant, vous parcourez un tableau avec N éléments, nous commençons donc par O(N)
.
Maintenant, supposons que votre i
soit toujours égale à X
et que X
soit toujours égale (rappelez-vous, dans le pire des cas à chaque fois).
combien de fois vous devez diviser X
par 2
pour obtenir 1
? (qui est la seule condition pour que les nombres pairs arrêtent la division lorsqu'ils atteignent 1).
En d'autres termes, nous devons résoudre l'équation X/2^k = 1
qui est X=2^k
et k=log<2>(X)
Cela oblige notre algorithme à prendre des étapes O(n log<2>(X))
, qui peuvent facilement être écrites sous la forme O(nlog(n))
.
Pour une telle boucle, nous ne pouvons pas séparer le nombre de boucles interne et externe -> les variables sont restreintes!
Nous devons donc compter toutes les étapes.
En fait, pour chaque itération de la boucle externe (sur i
), nous aurons
1 + v_2(i) steps
où v_2
est la valeur d'évaluation 2-adic (voir par exemple: http://planetmath.org/padicvaluation ) qui correspond à la puissance de 2
dans la décomposition en facteur premier de i
.
Donc, si nous ajoutons des étapes pour tout i
, nous obtenons un nombre total d'étapes de:
n_steps = \sum_{i=1}^{n} (1 + v_2(i))
= n + v_2(n!) // since v_2(i) + v_2(j) = v_2(i*j)
= 2n - s_2(n) // from Legendre formula (see http://en.wikipedia.org/wiki/Legendre%27s_formula with `p = 2`)
Nous voyons alors que le nombre d'étapes est exactement:
n_steps = 2n - s_2(n)
Comme s_2(n)
est la somme des chiffres de n
dans le 2
de base, il est négligeable (au plus log_2(n)
puisque digit dans la base 2
est égal à 0 ou 1 et comme il existe au plus log_2(n)
chiffres) par rapport à n
.
Donc, la complexité de votre algorithme est équivalente à n
:
n_steps = O(n)
qui est not le O(nlog(n))
indiqué dans de nombreuses autres solutions, mais en plus petite quantité!
commençons par le pire des cas:
si vous continuez à diviser avec 2 (intégrale), vous n'avez pas besoin de vous arrêter tant que vous n'avez pas atteint la valeur 1. en rendant le nombre d'étapes dépendant de la largeur de bit,. si la partie interne est log n ..__, la partie externe est évidemment n, donc N log N
total.
Une boucle do
divise en deux j
jusqu'à ce que k
devienne impair. k
est initialement une copie de j
qui est une copie de i
, donc do
exécute 1 + puissance de 2
qui divise i
:
do
,Cela fait au plus 1 + log (i) do
exécutions (logarithme avec base 2).
La boucle for
itère i
de 1 à n
; la limite supérieure correspond donc à n
fois (1 + log n), ce qui correspond à O (n log n).