III. Calcul rapide de décimales
Q8. Exponentiation binaire
| > |
expbin:=proc(m,k) local q,r,rec; if m=0 then 1 else q:=iquo(m,2,r); rec:=expbin(q,k)^2 mod k; if r=0 then rec else 16*rec mod k fi fi end; |
| > |
seq(expbin(i,37),i=1..10); |
 |
(3.1.2) |
| > |
seq(16^i mod 37,i=1..10); |
 |
(3.1.3) |
L'efficacité de expbin est handicapée par le fait que l'execution mène à de nombreux calculs sur des petits entiers, et donc coûte cher à un langage interprêté comme Maple. Il vaut mieux recourir à une version écrite dans le noyau (donc en C), pour la suite :
| > |
seq(`&^`(16,i) mod 37,i=1..10); |
 |
(3.1.4) |
Q9. Les sommes
| > |
S1:=proc(N,q,p,k) local s, i, modulo, res; if q=0 then 0 else s:=1./eval(p,k=N);for i from 0 to N-1 do modulo:=eval(p,k=i); s:=s+(`&^`(16,N-i) mod modulo)/modulo od; res:=frac(q*s); if res<0 then 1+res else res fi fi end; |
| > |
S2:=proc(N,q,p,k) local s,i; evalf(Sum(q/16^(i-N)/subs(k=i,p),i=N+1..infinity)) end; |
vérification
 |
(3.2.3) |
 |
(3.2.4) |
 |
(3.2.5) |
 |
(3.2.6) |
 |
(3.2.7) |
 |
(3.2.8) |
| > |
evalf(listvals[2]-%-%%); |
 |
(3.2.9) |
Q10. La procédure finale
| > |
somme:=proc(N,B) local j, k; frac(add(S1(N,B[j],8*k+j,k)+S2(N,B[j],8*k+j,k),j=1..8)) end; |
| > |
listpi:=[4,0,0,-2,-1,-1,0,0]; |
![[4, 0, 0, -2, -1, -1, 0, 0]](images/tp7_141.gif) |
(3.3.2) |
 |
(3.3.3) |
 |
(3.3.4) |
| > |
evalf(Pi-somme(0,listpi)); |

 |
(3.3.5) |
Donc ça fonctionne bien quand on part du tout début.
Voici la valeur du reste de π à partir du 10ième chiffre en base 16 :

 |
(3.3.6) |

 |
(3.3.7) |
 |
(3.3.8) |
| > |
sommefinale:=proc(N,B,k) local bk; Digits:=k*(ilog10(16)+1)+max(ilog2(N),1);bk:=somme(N,B); convert(trunc(16^k*bk),base,16) end; |
Les 20 premières à partir de 0 (il faut lire en partant de la fin) :
| > |
sommefinale(0,listpi,20); |
![[9, 1, 3, 1, 3, 13, 8, 0, 3, 10, 5, 8, 8, 8, 10, 6, 15, 3, 4, 2]](images/tp7_157.gif) |
(3.3.10) |
les 10 premières :
| > |
sommefinale(0,listpi,10); |
![[5, 8, 8, 8, 10, 6, 15, 3, 4, 2]](images/tp7_158.gif) |
(3.3.11) |
les 10 suivantes :
| > |
sommefinale(10,listpi,10); |
![[9, 1, 3, 1, 3, 13, 8, 0, 3, 10]](images/tp7_159.gif) |
(3.3.12) |
![[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]](images/tp7_160.gif) |
(3.3.13) |
Quelques tests de temps : les 5 premières décimales à partir de la 1e, 10e, 100e,...
| > |
for i to 6 do st:=time(); print(i,sommefinale(10^i,listpi,5),time()-st) od: |
À la fin, en un peu plus de 30 secondes de calcul, des décimales au-delà de la millionième ont été obtenues.
Pour le calcul de π, où les dénominateurs sont des
, il est possible d'améliorer un peu les temps de calculs en utilisant add au lieu d'une boucle :
| > |
S1:=proc(N,q,p,k) local s, i, modulo, ini, res; if q = 0 then 0 else ini:=eval(p,k=0);s:=add(1.*(`&^`(16,N-i) mod (8*i+ini))/(8*i+ini),i=0..N);res:=frac(q*s); if res<0 then res+1 else res fi fi end; |
| > |
st:=time():sommefinale(10^6,listpi,5),time()-st; |
![[14, 5, 6, 12, 6], 23.135](images/tp7_179.gif) |
(3.3.16) |
Q11. Complexité
La complexité est approximativement linéaire en
, et elle est reflétée par les temps de calcul qui doublent à peu près en doublant
. Plus précisément, chaque exponentiation binaire prend
étapes, chaque étape étant une opération modulo un entier de taille
. Au total, le temps de calcul est donc dominé par
, où
est le temps utilisé pour multiplier deux entiers de taille
C'est donc du
avec une multiplication naïve, ou du
avec de la FFT.
Ainsi, cette méthode n'est pas théoriquement meilleure que les meilleurs algorithmes pour
à base de moyenne arithmético-géométrique et de FFT, qui calculent toutes les
premières décimales.
Par contre, la consommation mémoire de la méthode présentée ici est négligeable, et une multiplication rapide n'est pas nécessaire pour obtenir des résultats.