Le "Hacker" au nom du test suggère que nous essayons de trouver une solution informatique.
Commençons donc par un programme d'énumération par force brute de (a) les cas "favorables" où un entier est le double de l'autre et (b) tous les cas possibles. La réponse serait alors leur ratio. J'ai codé une solution générale. Son entrée est un entier positif n
et sa sortie est la probabilité.
n=100
all=favorable=0
for i=1 to n
for j=1 to n
if (i != j) all=all+1 {1}
if (j == 2*i) favorable = favorable+1 {2}
if (i == 2*j) favorable = favorable+1 {3}
return(favorable / all)
(La preuve d'exactitude repose sur le fait que pour tout nombre positif .)i≠2ii
Ce programme nécessite tests et jusqu'à incréments pour chaque itération de la boucle interne. Il faut donc entre et calculs à chaque exécution de la boucle interne, soit à total. C'est la performance : OK pour les petits comme , mais terribles une fois que dépasse environ.333n6n3n26n2O(n2)nn=100n10000
En tant que hacker, l'une des premières choses que vous voudrez faire est d'éliminer les performances quadratiques en simplifiant la boucle interne (si possible). À cette fin, parcourez systématiquement les lignes de la boucle intérieure (numérotées) et notez les points suivants:
La ligne 1 est exécutée une seule fois pour chaque valeur de i
et all
est donc incrémentée fois. Par conséquent, pour le calcul de , la boucle peut être remplacée par incrémentation de .n−1all
j
all
n-1
La ligne 2 est exécutée exactement une fois lorsque et sinon pas du tout. Par conséquent, il peut être remplacé par incrémentation de chaque fois que .2i≤nall
12i≤n
La ligne 3 est exécutée une fois fournie i
est paire.
Voici le programme transformé.
n=100
all=favorable=0
for i=1 to n
all = all + (n-1) {1'}
if (2*i <= n) favorable = favorable+1 {2'}
if (even(i)) favorable = favorable+1 {3'}
return(favorable / all)
Pouvons-nous aller plus loin et éliminer sa boucle?
La ligne 1 'est exécutée fois. Par conséquent est incrémenté de .nall
n*(n-1)
La ligne 2 'n'est exécutée que lorsque . Une façon de compter cela est (le plus grand entier inférieur ou égal à ).2i≤n⌊n/2⌋n/2
La ligne 3 'est exécutée uniquement pour les valeurs paires de . Encore une fois, cela se produit times.i⌊n/2⌋
La deuxième transformation du programme est:
n=100
all=favorable=0 {0}
all = all + n * (n-1) {1''}
favorable = favorable + floor(n/2) {2''}
favorable = favorable + floor(n/2) {3''}
return(favorable / all)
C'est déjà un accomplissement formidable: un algorithme a été réduit à un algorithme (qui peut être considéré comme une "formule fermée" pour la réponse).O(n2)O(1)
Enfin, il y a quelques transformations algébriques simples que nous pouvons faire en roulant l'initialisation (ligne 0) dans la première utilisation de chaque variable et en combinant les lignes 2 '' et 3 '':
n=100
all = n * (n-1)
favorable = 2 * floor(n/2)
return(favorable / all)
À ce stade, un humain pourrait exécuter le programme. Faisons-le avec :n=100
all = 100 * (100-1) = 100*99
favorable = 2 * floor(100/2) = 2*50 = 100
favorable/all = 100 / (100*99) = 1/99
La sortie est donc .1/99
Pour résumer, un algorithme de force brute peut être transformé systématiquement en utilisant des règles de réécriture de programme simples en un programme élégant et élégant .O(1)