Appelons de façon générale a le début de 2n
c'est à dire :
a×10m ≤ 2n < (a+1)×10m
En prenant le logarithme décimal :
log(a) + m ≤ n log(2) < log(a+1) + m, ou encore puisque log(2) > 0 :
log(a) / log(2) + m / log(2) ≤ n < log(a+1) / log(2) + m / log(2) [1] |
C'est à dire qu'il faut trouver le plus petit entier qui soit entre des termes de même rang de ces deux progressions arithmétiques.
L'irrationalité de la raison 1 / log(2) ≈ 3.3219280948873... rend le problème "un peu chaotique"...
Donnons a = 7 pour fixer les idées
log(7) / log(2) + m / log(2) ≤ n < log(8) / log(2) + m / log(2)
soit 2.8073 + 3.3219 m ≤ n < 3 + 3.3219 m
A cause du "< 3" strict, la solution m = 0 ne convient bien sûr pas !
L'écart entre ces deux valeurs est constant et égal à d = (log(8) - log(7)) / log(2) ≈ 0.1926
On cherche donc le plus petit m tel que la partie fractionnaire v de log(7) / log(2) + m / log(2) soit
v ≤ 1 < v + d ou encore v > 1 - d
Comme 0 ≤ v < 1 par définition, la condition d'égalité sur v ≤ 1 s'écrit en fait v = 0 et finalement :
soit v = frac(log(a) / log(2) + m / log(2)) et d = frac(log(1 + 1/a) / log(2))
v > 1 - d ou v = 0 Cette condition détermine m, on en déduit alors : n = ceil(log(a) / log(2) + m / log(2)) (le plus petit entier ≥ ...) |
On peut représenter cela sur un cercle, avec des angles en tours, c'est à dire faire des calculs "modulo 1".
Considérons donc les multiples de τ = frac(1 / log(2)) ≈ 0.3219
m = 1 : 0.3219...
m = 2 : 0.6438...
m = 3 : 0.9657...
m = 4 : 1.2876... > 1
Mais on peut considérer m = 3 comme un recul de 0.0342
En d'autres termes, chaque fois qu'on ajoute 3 fois τ, on recule
de τ1 ≈ 0.0342 sur notre cadran.
Au départ (m = 0) on est à v = frac(log(7) / log(2)) = 0.8073 = - 0.1926 "modulo 1"
On veut arriver à -d < v ≤ 0, c'est à dire -0.1926 < v ≤ 0.
On n'y est pas, à cause du "< v" strict.
L'avance de τ (m = 1) fait passer à v = +0.1292 > 0
Il faut alors reculer d'autant de fois trois que
0.1292 / 0.0342 = 3.77 soit 4 fois 3 à partir de 1, soit m = 13 arrivant à v = -0.0075 et c'est gagné.
n est alors le plus petit entier ≥ log(7)/log(2) + 13 / log(2) = 45.9924
246 commence par 7 |
253 commence par 9 |
2196 commence par 100... |
log(1000) / log(2) + m / log(2) ≤ n < log(1001) / log(2) + m / log(2)
d = log(1001) / log(2) - log(1000) / log(2) = 0.00144
v = log(1000) / log(2) = 9.96578 = -0.03421 (mod 1)
m = 1 donne -0.03421 + τ = 0.28771 (mod 1)
0.28771 / τ1 = 8.408... donc 3×9 = 27 de plus, soit m = 28
Mais ici on arrive à -0.020229 (mod 1) < -0.00144
Il faut ici un découpage "plus fin" de notre cadran pour continuer et repartir dans l'autre sens.
Nous avons vu que m = 3 donne un recul de τ1 ≈ 0.0342
Pour obtenir des précisions meilleures, exprimons 1/log(2) en fractions continues :
1/log(2) = [3,3,9,2,2,4,6,2,1...] et les réduites
3 /1, 10 / 3, 93 / 28, 196 / 59,
485 / 146, 2136 / 643,
13301 / 4004, 28738 / 8651, 42039 / 12655...
Ceci nous donne les "restes"
1 / log(2) = τ = 0.3219280948873626
3 / log(2) = τ1 = -0.034215715337911234
28 / log(2) = τ2 = +0.013986656846157075
59 / log(2) = τ3 = -0.0062424016456077425
146 / log(2) = τ4 = +0.0015018535549415901
643 / log(2) = τ5 = -0.00023498742575611686
4004 / log(2) = τ6 = +0.00009192899960908107
8651 / log(2) = τ7 = -0.000051129427447449416
12655 / log(2) = τ8 = +0.000040799575799610466
...
L'explication théorique est que |1/log(2) - P/Q| < 1/Q² ou encore |Q/log(2) - P| < 1/Q
et en passant modulo 1 : |Q/log(2)| < 1/Q
(à condition de définir Q/log(2) modulo 1 entre -0.5 et +0.5, avec Q > 2)
L'erreur τ = Q/log(2) modulo 1 est alors décroissante en valeur absolue, alternativement >0 et <0
Les réduites étant les "meilleures" approximation de 1/log(2), ceci veut dire qu'il n'y a pas de fraction plus simple et plus précise.
Ceci garantit ainsi que le procédé suivant donne la plus petite valeur de m.
Nous en sommes à m = 28 et au point v = -0.020229
Chaque fois qu'on ajoute 28 à m, on avance de τ2 = +0.013986
(0.020229 - 0.00144) / 0.013986 = 1.34 ajoute encore 2×28 et donne m = 84 et la position +0.00774
On poursuit ainsi successivement avec les restes partiels et les dénominateurs des réduites de 1/log(2) :
0.00774 / 0.006242 = 1.24 soit 2×59 de plus, m = 202 et -0.00474
(0.00474 - 0.00144) / 0.0015018 = 2.197 et 3×146 de plus, soit m = 640 et la position -0.00023,
enfin dans les limites cherchées et donc m = 640 et n = ceil((log(1000)+640)/log(2)) = 2136.
22136 commence par 1000... |
Tous ces calculs avec la décomposition en fraction continue de 1/log(2) nous ont
permis d'obtenir le résultat avec "quelques" divisions seulement (sans avoir besoin de boucler sur les 640
valeurs de m comme avec un programme par force brute) et une simple calculette.
On peut ainsi programmer la méthode précédente pour chercher de façon efficace
la plus petite puissance de 2 qui commence par 123456789.
Le script suivant effectue le calcul pour n'importe quelle valeur de a
(raisonnable, car les calculs sont fait en Javascript, donc avec une précision limitée).
Les restes τi sont ramenés "modulo 1" entre -0.5 et +0.5
Soit v la valeur courante = frac((log(a) + m) / log(2)), 0≤ v < 1
Soit Q le dénominateur de la réduite, et τ = Q/log(2) modulo 1 le reste courant signé. -0.5 < τ < +0.5
si τ > 0, v≠0 et v + d ≤ 1 : m est augmenté de Q×(floor((1 - v - d) / τ) + 1)
si τ < 0, v≠0 et v + d ≤ 1 : m est augmenté de Q×(floor(v / |τ|) + 1)
si v = 0 ou v < 1 < v + d, c'est fini, sinon on prend le terme suivant de la
fraction continue.
Nota : floor(x) + 1 est non pas ceil(x) car si x = 0, on veut ajouter 1.
En prime la base (ici 2) est au choix dans ce script, tout ce qui précéde étant immédiatement adaptable avec log(base) au lieu de log(2).
Lors de la décomposition de 1/log(2) en fraction continue, avec les réduites P/Q, la précision est dépassée
quand 1/Q² devient < 10-16, ou ce qui revient au même τ = 0.
On peut aussi dépasser la précision de calcul dans le test "v = 0". Si v est en fait > 0, l'algoritme s'arrête prématurément.
Notons aussi que le calcul direct et l'affichage des premiers chiffres de 2n, n grand, est impossible
car les nombres représentables sont < 10308 (21024) environ en virgule flottante "standard" utilisée ici.
On le divise alors par 10m+m'-1, m' = nombre de chiffres de a, pour se ramener à un nombre inférieur à 10 et afficher l'exposant de 10 à part.
Les logarithmes en Javascript étant en natif les logarithmes néperiens (ln), on peut écrire :
2n = exp(n ln(2) - (m + m' - 1)ln(10)) × 10m + m' - 1
Si n < 50, le calcul de 2n est exact et le nombre entier correspondant est affiché directement.
Sinon, on applique la formule précédente.
Estimons la précision de ce calcul, c'est à dire le nombre de chiffres exact qu'il fournit.
Le calcul des ln( ) étant estimé à 10-16 près, le calcul de la différence est entaché d'une erreur
n fois plus importante et donc avec 16 - log(n) chiffres exacts seulement.
Ainsi avec a = 123456789, n = 62759188, on n'a plus que 16 - 8 = 8 chiffres exacts !
En fait les erreurs se compensent (différence) et la précision effective est meilleure.
Il n'empêche...
Le script suivant estime la précision de ce calcul en ajoutant et retranchant des erreurs volontaires ε
sur ln(2) et ln(10) :
On voit ainsi que ε = 10-16 correspond bien à la précision du Javascript, et que ...
la valeur affichée est fantaisiste puisque la seule chose qu'on peut affirmer est
12345678822... < 2n < 12345679006...
Le dernier "89" de 123456789 n'est même pas garanti !
Le même calcul effectué à la main avec la calculette de Windows et ε = 10-25 donne
123456789991991542... < 262759188 < 123456789991991544... |