Je peux donc imaginer ce qu’est un algorithme qui a une complexité de n ^ c, juste le nombre de boucles for imbriquées.
for (var i = 0; i < dataset.len; i++ {
for (var j = 0; j < dataset.len; j++) {
//do stuff with i and j
}
}
Le journal est quelque chose qui scinde le jeu de données en deux à chaque fois, la recherche binaire le fait (vous ne savez pas exactement à quoi ressemble le code correspondant).
Mais qu'est-ce qu'un exemple simple d'un algorithme qui est c ^ n ou plus précisément 2 ^ n. Est-ce que O (2 ^ n) est basé sur des boucles de données? Ou comment les données sont fractionnées? Ou quelque chose d'autre entièrement?
Les algorithmes avec le temps d'exécution O (2 ^ N) sont souvent des algorithmes récursifs qui résolvent un problème de taille N en résolvant de manière récursive deux problèmes plus petits de taille N-1.
Ce programme imprime par exemple tous les mouvements nécessaires pour résoudre le fameux problème "Tours de Hanoi" pour N disques en pseudo-code
void solve_hanoi(int N, string from_peg, string to_peg, string spare_peg)
{
if (N<1) {
return;
}
if (N>1) {
solve_hanoi(N-1, from_peg, spare_peg, to_peg);
}
print "move from " + from_peg + " to " + to_peg;
if (N>1) {
solve_hanoi(N-1, spare_peg, to_peg, from_peg);
}
}
Soit T(N) le temps nécessaire pour N disques.
On a:
T(1) = O(1)
and
T(N) = O(1) + 2*T(N-1) when N>1
Si vous développez à plusieurs reprises le dernier terme, vous obtenez:
T(N) = 3*O(1) + 4*T(N-2)
T(N) = 7*O(1) + 8*T(N-3)
...
T(N) = (2^(N-1)-1)*O(1) + (2^(N-1))*T(1)
T(N) = (2^N - 1)*O(1)
T(N) = O(2^N)
Pour comprendre cela, il suffit de savoir que certains modèles de la relation de récurrence conduisent à des résultats exponentiels. Généralement, T(N) = ... + C*T(N-1)
avec C > 1
signifie O (x ^ N). Voir:
Pensez par exemple itérer sur tous les sous-ensembles possibles d'un ensemble. Ce type d'algorithme est utilisé par exemple pour un problème généralisé de sac à dos.
Si vous avez du mal à comprendre comment itérer sur des sous-ensembles se traduit par O (2 ^ n), imaginez un ensemble de n commutateurs, chacun correspondant à un élément d'un ensemble. Chacun des commutateurs peut maintenant être activé ou désactivé. Pensez à "sur" comme étant dans le sous-ensemble. Remarque, combien de combinaisons sont possibles: 2 ^ n.
Si vous voulez voir un exemple dans le code, il est généralement plus facile de penser à la récursion ici, mais je ne peux pas penser od à tout autre exemple agréable et compréhensible pour le moment.
int Fibonacci(int number)
{
if (number <= 1) return number;
return Fibonacci(number - 2) + Fibonacci(number - 1);
}
La croissance double avec chaque ajout à l'ensemble de données en entrée. La courbe de croissance d’une fonction O(2N) est exponentielle - elle commence très peu profondément, puis monte de façon météorique . Mon exemple de grand O (2 ^ n), mais est bien mieux:
public void solve(int n, String start, String auxiliary, String end) {
if (n == 1) {
System.out.println(start + " -> " + end);
} else {
solve(n - 1, start, end, auxiliary);
System.out.println(start + " -> " + end);
solve(n - 1, auxiliary, start, end);
}
Dans cette méthode, le programme imprime tous les mouvements pour résoudre le problème "Tour de Hanoi" . Les deux exemples utilisent un problème récursif pour résoudre le problème et ont un temps de traitement important (2 ^ n).
c ^ N = Toutes les combinaisons d'éléments n
à partir d'un alphabet de taille c
.
Plus spécifiquement, 2 ^ N est tous les nombres pouvant être représentés avec N bits.
Les cas courants sont implémentés de manière récursive, quelque chose comme:
vector<int> bits;
int N
void find_solution(int pos) {
if (pos == N) {
check_solution();
return;
}
bits[pos] = 0;
find_solution(pos + 1);
bits[pos] = 1;
find_solution(pos + 1);
}
Voici un clip de code qui calcule la somme des valeurs de chaque combinaison de valeurs dans un tableau de produits (et value
est une variable de tableau global):
fun boom(idx: Int, pre: Int, include: Boolean) {
if (idx < 0) return
boom(idx - 1, pre + if (include) values[idx] else 0, true)
boom(idx - 1, pre + if (include) values[idx] else 0, false)
println(pre + if (include) values[idx] else 0)
}
Comme vous pouvez le voir, c'est récursif. Nous pouvons insérer des boucles pour obtenir la complexité Polynomial
et utiliser récursif pour obtenir la complexité Exponential
.
Voici deux exemples simples en python avec Big O/Landau (2 ^ N):
#fibonacci
def fib(num):
if num==0 or num==1:
return num
else:
return fib(num-1)+fib(num-2)
num=10
for i in range(0,num):
print(fib(i))
#tower of Hanoi
def move(disk , from, to, aux):
if disk >= 1:
# from twoer , auxilart
move(disk-1, from, aux, to)
print ("Move disk", disk, "from rod", from_rod, "to rod", to_rod)
move(disk-1, aux, to, from)
n = 3
move(n, 'A', 'B', 'C')