Si a et b sont des entiers ou des entiers de Gauss :
iquo(a,b) renvoie le quotient q de la division euclidienne de a par b et
irem(a,b) renvoie le reste r de la division euclidienne de a par b.
q et r vérifient :
si a et b sont entiers a=b*q+r avec 0 ≤ r<b
si a et b sont des entiers de Gauss a=b*q+r avec |r|2) ≤ |b|2/2.
Par exemple si a=−2+6*i et si b=1+3*i on a :
q=2+i et r=−1−i
Si a et b sont des entiers relatifs smod(a,b) renvoie le reste
symétrique rs de la division euclidienne de a par b.
q et rs vérifient :
a=b*q+rs avec −b/2<rs ≤ b/2
Exemples :
smod(7,4)=-1
smod(-10,4)=-2
smod(10,4)=2
Remarque
mod (ou %) est une fonction infixée et désigne un élément
de Z/nZ.
On a : 7 mod 4=-1%4 désigne un élément de Z/4Z mais
smod(7,4)=(7%4)%0=-1 désigne un entier.
| = |
| + |
| + |
|
Démontrer que :
q>0 |
| < |
| ≤ |
|
| − |
| = |
|
u=a(q+1) |
Démontrer que :
v≤ b<a<u |
Si r=0, vérifier que :
| = |
|
Puis, on recommence :
on divise a1 par b1.
On trouve un quotient q1 et un reste r1.Si r1 est nul, vérifier
que :
| = |
| + |
|
Si r1 n’est pas nul, on recommence.
Montrer qu’il existe une suite finie d’entiers Q0, Q1,...Qn strictement croissante telle que :
| = |
| + |
| +...+ |
|
|
On suppose que la fraction NUM/DENOM∈]0;1[.
L’algorithme s’écrit en langage non fonctionnel :
DENOM →A
NUM→B
Quotient(A, B)→Q
Reste(A, B)→R
tantque R ≠0 faire
Q+1→D
Afficher D
B-R→B
A*D→A
Quotient(A, B)→Q
Reste(A, B)→R
ftantque
Afficher Q
On écrit la fonction decomp qui va décomposer selon l’algorithme la
fraction frac. Cette fonction va renvoyer la liste lres égale
à [Q0,..Qn] avec 0<Q0<,..<Qn et
frac=1/Q0+..+1/Qn.
Attention frac=b/a et donc fxnd(frac)=fxnd(b/a)=[b,a].
decomp(frac):={ local a,b,l,q,r,lres; l:=fxnd(frac); b:=l[0]; a:=l[1]; q:=iquo(a,b); r:=irem(a,b); lres:=[]; while (r!=0) { lres:= concat(lres, q+1); b:=b-r; a:=a*(q+1); q:=iquo(a,b); r:=irem(a,b); } lres:=concat(lres,q); return lres; }
On tape :
decomp(151/221)
On obtient :
[2,6,61,5056,40895962,4181199228867648]
On vérifie :
1/2+1/6+1/61+1/5056+1/40895962+1/4181199228867648
On obtient :
151/221
On peut écrire un programme pour faire le vérification :
size(l) est égal à la longueur de la liste l
verifie(l):={ local s,k,res; s:=size(l); res:=0; for (k:=0;k<s;k++){ res:=res+1/l[k]; } return res; }
On tape :
verifie([2,6,61,5056,40895962,4181199228867648])
On obtient :
151/221
Soient A et B deux entiers positifs dont on cherche le PGCD.
L’algorithme d’Euclide est basé sur la définition récursive du PGCD :
|
où A mod B désigne le reste de la division euclidienne de A par B.
Voici la description de cet algorithme :
on effectue des divisions euclidiennes successives :
|
Après un nombre fini d’étapes, il existe un entier n tel que :
Rn = 0.
on a alors :
PGCD(A,B)= PGCD(B,R1) =...
PGCD(Rn−1,Rn) = PGCD(Rn−1,0) = Rn−1
-Version itérative
Si B≠ 0 on calcule R=A modB, puis avec B dans le rôle
de A (en mettant B dans A ) et R dans le rôle de B ( en mettant
R dans B) on recommence jusqu’à ce que B=0, le PGCD est alors égal
à A.
fonction PGCD(A,B) local R
tantque B ≠ 0 faire
A mod B=>R B=>A R=>B ftantque retourne A ffonction
-Version récursive
On écrit simplement la définition récursive vue plus haut.
fonction PGCD(A,B)
Si B ≠ 0 alors
retourne PGCD(B,A mod B) sinon retourne A fsi ffonction
- Version itérative :
pgcd(a,b):={ local r; while (b!=0){ r:=irem(a,b); a:=b; b:=r; } return(a); };
- Version récursive.
pgcdr(a,b):={ if (b==0) return(a); else return(pgcdr(b,irem(a,b))); };
-Version itérative :
pgcd:=proc(x,y) local a,b,r: a:=x: b:=y: while (b>0) do r:=irem(a,b): a:=b: b:=r: od: RETURN(a): end:
-Version récursive :
pgcd:=proc(a,b) if (b=0) then RETURN(a) else RETURN(pgcd(b,irem(a,b))): fi: end:
-Version itérative :
pgcd:=proc(a,b) local r: begin while (b>0) do r:=a mod b: a:=b: b:=r: end_while: return(a): end_proc;
-Version récursive :
pgcd:=proc(a,b) begin if (b=0) then return(a) else return(pgcd(b,a mod b)): end_if: end_proc;
-Version itérative
:pgcd(a,b) :Func :Local r
:While b ≠ 0
:mod(a,b)=>r :b=>a :r=>b :EndWhile :Return a :EndFunc
-Version récursive
:pgcd(a,b) :Func
:If b ≠ 0 Then
:Return pgcd(b, mod(a,b)) :Else :Return a :EndIf :EndFunc
On rappelle que ℤ[i]={m+n*i, (m,n)∈ ℤ2}.
Soient a ∈ ℤ[i] et b ∈ ℤ[i]−{0}, alors on dit que le quotient q
de a par b est l’affixe du (ou des) point(s), le plus proche pour le
module, du point d’affixe a/b.
On tape :
quotient(a,b):={ local q1,q2,c; c:=normal(a/b); q1:=re(c); q2:=im(c); return round(q1)+i*round(q2); }:; pgcdzi(a,b):={ local q,r; tantque b!=0 faire q:=quotient(a,b); r:=a-b*q; a:=b; b:=r; ftantque; //on normalise si re(a)<0 et im(a)<=0 alors retourne -a;fsi; si im(a)<0 alors retourne i*a;fsi; si re(a)<=0 alors retourne -i*a;fsi; retourne a; }:;
On tape :
pgcdzi(3+i,3-i)
On obtient :
1+i
On tape :
pgcdzi(7+i,-6+17*i)
On obtient :
3+4*i
On rappelle que ℤ[i√2]={m+i*n√2, (m,n)∈ ℤ2}.
Soient a ∈ ℤ[i√2] et b ∈ ℤ[i√2]−{0}, alors on dit que le
quotient q de a par b est l’affixe du (ou des) point(s), le plus proche
pour le module, du point d’affixe a/b.
On tape :
quorest(a,b):={ local q1,q2,q,r,c; c:=normal(a/b); q1:=normal(round(re(c))); q2:=normal(round(im(c)/sqrt(2))); q:= q1+i*q2*sqrt(2); r:=simplify(a-b*q); return q,r; }:; pgcdzis2(a,b):={ local r; tantque b!=0 faire r:=quorest(a,b)[1]; a:=b; b:=r; ftantque; //on normalise si re(a)<0 alors retourne -a;fsi; retourne a; }:;
On tape :
pgcdzis2(3+i*sqrt(2),3-i*sqrt(2))
On obtient :
1
On tape :
pgcdzis2(4+5*i*sqrt(2),-2+3*i*sqrt(2))
On obtient :
2-(3*i)*sqrt(2)
Dans ce paragraphe la fonction Bezout(A,B) renvoie la liste {U, V, PGCD(A,B)} où U et V vérifient : A × U + B × V = PGCD(A,B)
L’algorithme d’Euclide permet aussi de trouver un couple U et V vérifiant:
A × U + B × V= PGCD(A,B)
En effet, si on note A0 et B0 les valeurs de A et de B du début on a :
|
Puis on fait évoluer A, B, U, V, W, X de façon à ce que ces
deux relations soient toujours vérifiées. Voici comment
A, B, U, V, W, X évoluent :
- on pose :
A=B × Q+R 0 ≤ R < B ( R = A mod B et Q = E(A / B ))
- on écrit alors :
|
Il reste alors à recommencer avec B dans le rôle de A (B=>A W=>U X=>V) et R dans le rôle de B (R=>B S=>W T=>X ) d’où l’algorithme:
fonction Bezout(A,B) local U,V,W,X,S,T,Q,R 1=>U 0=>V 0=>W 1=>X
tantque B ≠ 0 faire
A mod B=>R E(A/B)=>Q //R=A-B*Q U-W*Q=>S V-X*Q=>T B=>A W=>U X=>V R=>B S=>W T=>X ftantque retourne {U, V, A} ffonction
On peut simplifier l’écriture de l’algorithme ci-dessus en utilisant moins de
variables : pour cela on utilise les listes LA, LB, LR pour mémoriser
les triplets {U, V, A}, {W, X, B} et {S, T, R}.
Ceci est très commode car les logiciels de calcul savent ajouter des listes
de même longueur (en ajoutant les éléments de même indice) et savent
aussi multiplier une liste par un nombre (en multipliant chacun des
éléments de la liste par ce nombre).
fonction Bezout(A,B) local LA LB LR {1, 0, A}=>LA {0, 1, B}=>LB
tantque LB[3] ≠ 0 faire
LA-LB*E(LA[3]/LB[3])=>LR LB=>LA LR=>LB ftantque retourne LA ffonction
Si on utilise des variables globales pour A, B, D, U, V, T, on peut voir
la fonction Bezout comme calculant à partir de A B, des valeurs
qu’elle met dans U, V, D (AU+BV=D), grâce à une variable locale
Q.
On écrit donc une fonction sans paramètre : seule la variable Q doit être locale à la foncton alors que les autres variables A, B ...
sont globales.
Bezout fabrique U, V, D vérifiant A*U+B*V=D à partir de A et B.
Avant l’appel récursif (on présérve E(A/B)=Q et on met A et
B à jour ( nouvelles valeurs), après l’appel les variables U, V, D vérifient
A*U+B*V=D (avec A et B les nouvelles valeurs), il suffit
alors de revenir aux premières valeurs de
A et B en écrivant :
B*U+(A-B*Q)*V=A*V+B*(U-V*Q)
On écrit alors :
fonction Bezout local Q Si B != 0 faire E(A/B)=>Q A-B*Q=>R B=>A R=>B Bezout U-V*Q=>W V=>U W=>V sinon 1=>U 0=>V A=>D fsi ffonction
On peut définir récursivement la fonction Bezout par:
Bezout(A,0)={1, 0, A}
Si B ≠ 0 il faut définir Bezout(A,B) en fonction de Bezout(B,R) lorsque R=A−B × Q et Q=E(A/B).
On a:
|
Donc:
|
D’où si B ≠ 0 et si Bezout(B,R)=LT on a :
Bezout(A,B)={LT[2], LT[1]−LT[2] × Q, LT[3]}.
fonction Bezout(A,B) local LT Q R
Si B ≠ 0 faire
E(A/B)=>Q A-B*Q=>R Bezout(B,R)=>LT retourne {LT[2], LT[1]-LT[2]*Q, LT[3]} sinon retourne {1, 0, A} fsi ffonction
- Version itérative avec les listes
bezout(a,b):={ //renvoie [u,v,d] tels que a*u+b*v=pgcd(a,b) (fct iterative) local la,lb,lr,q,lb2; la:=[1,0,eval(a)]; lb:=[0,1,eval(b)]; lb2:=eval(b); while (lb2 !=0){ q:=iquo(la[2],lb2); lr:=la+(-q)*lb; la:=lb; lb:=lr; lb2:=lb[2]; } return(la); };
- Version récursive avec les listes
bezoutr(a,b):={ //renvoie [u,v,d] tels que a*u+b*v=pgcd(a,b) (fct recursive) local lb,q,r; if (b!=0) { q:=iquo(a,b); r:=irem(a,b); lb:=bezoutr(b,r); return([lb[1],lb[0]+(-q)*lb[1],lb[2]]); } else return([1,0,a]); };
Dans cette section, on ne suppose pas connue une table de nombres premiers : on ne se sert donc pas du programme crible.
fonction factprem(N) local D FACT 2 => D {} => FACT tantque N<= 1 faire si N mod D = 0 alors concat(FACT,D) => FACT N/D => N sinon D+1 => D fsi ftantque retourne FACT ffonction
fonction factprem1(N) local D FACT 2 => D {} => FACT tantque D*D!= N faire si N mod D = 0 alors concat(FACT,D) => FACT N/D=> N sinon D+1 => D fsi ftantque concat(FACT,N) => FACT retourne FACT ffonction
Dans la liste FACT, on a les diviseurs premiers éventuellement
plusieurs fois, par exemple :
factprem1(12)={2,2,3}.
Dans la liste FACT, on fait suivre chaque diviseur premier par son
exposant, par exemple :
factprem2(12)={2,2,3,1}.
fonction facprem2(N) local K D FACT {}=>FACT 0 => K tantque N mod 2 == 0 faire K+1 => K N/2 => N ftantque si K !=0 alors concat(FACT,{2 K}) => FACT fsi 3 =>D tantque D*D<= N faire 0 => K tantque N mod D = 0 faire K+1 => K N/D => N ftantque si K !=0 alors concat(FACT,{D K})=> FACT fsi D+2 => D ftantque si N != 1 alors concat(FACT,{N 1})=> FACT fsi retourne FACT ffonction
fonction factprem3(N) local J,D,FACT 2=>D {}=>FACT tantque (D*D<=N) faire 0=>J tantque (N mod D=0) faire N/D=>N J+1=>J ftantque si (J!= 0) alors concat(FACT,{D,J})=>FACT fsi si (D<4) alors 2*D-1=>D sinon D+(4*D mod 6)=>D fsi ftantque si (N !=1) alors concat(FACT,{N,1})=>FACT fsi retourne(FACT) ffonction
On traduit la troisième amélioration.
factprem(n):={ //decompose n en facteur premier dans la liste l de dimension s local j,d,s,l; d:=2; s:=0; l:=[]; while (d*d<=n) { j:=0; while (irem(n,d)==0){ n:=iquo(n,d); j:=j+1; } if (j!=0) { l:=concat(l,[d,j]); s:=s+2; } if (d<4) { d:=2*d-1; } else { d:=d+irem(4*d,6); } } if (n!=1) { l:=concat(l,[n,1]); s:=s+2; } return([l,s]); };
Pour effectuer la décomposition en facteurs premiers de n, on utilise la
table des nombres premiers fabriquée par le crible : on ne teste ainsi que
des nombres premiers.
Si on peut écrire N=A*DJ avec PGCD(A,D)=1 et J>0 alors DJ est un
facteur de la décomposition de N.
On écrit tout d’abord la fonction ddiv(N,D) qui renvoie :
- soit la liste :
[ N,[]] si D n’est pas un diviseur de N,
- soit la liste :
[A,[D,J]] si N=A*DJ avec PGCD(A,D)=1 et J>0.
DJ est alors un diviseur de N et A=N/DJ .
fonction ddiv(N,D) //ddiv renvoie [a,[d,j]] (n=a*d^j, pgcd(a,d)=1) si j!=0 sinon [n,[]] local L,J 0=>J tantque (N mod D)=0) faire N/D=>N J+1=>J ftantque si (J=0) alors {N,{}}=>L sinon {N,{D,J}}=>L fsi retourne(L) ffonction
On cherche la liste des nombres premiers plus petit que √N et on met cette liste dans la variable PREM. Lorsque N>1, on teste si ces nombres premiers sont des diviseurs de N en utilisant ddiv.
fonction criblefact(N) //decomposition en facteurs premiers de n //en utilisant ddiv et crible local D,PREM,S,LD,LDIV; PREM:=crible(floor(sqrt(N))); S:=dim(PREM); LDIV:={}; 1=>K tantque (K<=S et N>1) faire ddiv(N,PREM[K])=>LD concat(LDIV,ld[2])=>LDIV; LD[1]=>N K+1=>K ftantque si (N != 1) alors concat(LDIV,[N,1])=>LDIV; fsi retourne(LDIV); }
ddiv(n,d):={ //ddiv renvoie [a,[d,j]] (n=a*d^j, pgcd(a,d)=1) si j!=0 //sinon [n,[]] local l,j; j:=0; while (irem(n,d)==0){ n:=iquo(n,d); j:=j+1; } if (j==0){ l:=[n,[]]; } else { l:=[n,[d,j]]; } return(l); }
criblefact(n):={ //decomposition en facteurs premiers de n //en utilisant ddiv et crible local d,prem,s,ld,ldiv; prem:=crible(floor(sqrt(n))); s:=size(prem); ldiv:=[]; for (k:=0;k<s;k++){ ld:=ddiv(n,prem[k]); ldiv:=concat(ldiv,ld[1]); n:=ld[0]; k:=k+1; } if (n!=1){ ldiv:=concat(ldiv,[n,1]); } return(ldiv); }
L’algorithme naïf :
pour j de 1 a n faire
si (j divise n) alors
afficher j
fsi
fpour
Les élèves remarquent que l’on peut avoir les diviseurs deux par deux.
pour j de 1 a E(√n) faire
si (j divise n) alors
afficher j, n/j
fsi
fpour
Malheureusement, lorsque l’entier n est le carré de p, p figure deux
fois dans l’affichage des diviseurs.
On améliore donc l’algorithme :
1 → j
tantque j<√n faire
si (j divise n) alors
afficher j, n/j
fsi
j+1 → j
ftantque
si j·j=n alors
afficher j
fsi
Remarque
Les programmes sont ensuite mis sur des calculatrices, c’est pourquoi les
algorithmes précédents utilisent la commande afficher. Si on veut
écrire un programme avec Xcas on fera une fonction : on mettera les
diviseurs dans une liste qui sera à la fin la valeur de la fonction en
utilisant la commande retourne par exemple :
nbdivis(n):={ local j, L,sn; L:=[]; j:=1; sn:=sqrt(n) tantque j<sn faire si irem(n,j)==0 alors L:=concat(L,[j,n/j]); fsi; j:=j+1; ftantque; si j*j==n alors L:=append(L,j) fsi; retourne L; }:;
On décompose n en facteurs premiers, puis on donne aux exposants de ces facteurs premiers toutes les valeurs possibles. Si n=abcγ l’exposant de a paut prendre α+1 valeurs (0..α), celui de b peut prendre β+1 valeurs et celui de c peut prendre γ+1 valeurs donc le nombre de diviseurs de n est (α+1)*(β+1)*(γ+1).
Déscription de l’algorithme sur un exemple :
n=360=23*32*5
n a donc (3+1)*(2+1)*(1+1)=24 diviseurs.
On les écrit en faisant varier le triplet représentant les exposants avec
l’ordre :
(0,0,0),(1,0,0),(2,0,0),(3,0,0),
(0,1,0),(1,1,0),(2,1,0),(3,1,0),
(0,2,0),(1,2,0),...,
(0,2,1),(1,2,1),(2,2,1),(3,2,1))
On a (a1,β,γ) < (b1,b2,b3) si :
γ<b3 ou
γ=b3 et β<b2 ou
γ=b3 et β=b2 et a1<b1.
On obtient les 4*3*2=24 diviseurs de 360 :
1,2,4,8,3,6,12,24,9,18,36,72,5,10,20,40,15,30,60,120,45,90,180,360.
que l’on peut écrire en le tableau suivant :
1,2,4,8 (les puissances de 2)
3,6,12,24 (3*les puissances de 2)
9,18,36,72 (3*3*les puissances de 2)
5,10,20,40 (5*les puissances de 2)
15,30,60,120 (5*3*les puissances de 2)
45,90,180,360 (5*3*3*les puissances de 2).
Comment obtient-on la liste des diviseurs de abcγ
à partir de la liste L1 des diviseurs de abβ ?
Il suffit de rajouter à L1 la liste L2 constituée par :
c*L1,...,cL1
Dans le programme cette liste de diviseurs (L1) sera donc constituée
au fur et à mesure au moyen d’une liste (L2) qui correspond au parcours
de l’arbre.
On initialise L1 avec {1}, puis on rajoute à L1
la liste L2 formée par :
a*L1,...,aL1.
Puis on recommence avec le diviseur suivant :
on rajoute à L1
la liste L2 formée par b*L1,...,bL1 etc...
La liste L1 est la liste destinée à contenir les diviseurs de N.
Au dèbut L1={1} et L2={}.
Pour avoir la liste des diviseurs de N, on cherche A le premier
diviseur de N et on cherche a la puissance avec quelle A
divise N.
On définit la liste L2 :
L2 est obtenue en concaténant, les listes L1*A, L1*A2,
...,L1*Aa : au début L1={1} donc
L2={A,A2,...,Aa}.
On modifie la liste L1 en lui concaténant la liste L2, ainsi
L1={1,A,A2,...,Aa}.
Puis, on vide la liste L2. On cherche B le deuxième diviseur
éventuel de N et on
cherche b la puissance avec quelle B divise N.
On définit la nouvelle liste L2 :
L2 est obtenue en concaténant, les listes L1*B, L1*B2,
..., L1*Bb (c’est à dire
L2={B,B*A,B*A2,..,B*AaB2,B2*A,B2*A2,..,B2*Aa,..,Bb*Aa,})
On modifie la liste L1 en lui concaténant la liste L2, ainsi :
L1={1,A,A2,...,Aa,B,B*A,B*A2,...,B*Aa,...,Bb,Bb*A,Bb*A2,...,Bb*Aa}.
Et ainsi de suite, jusqu’à avoir epuisé tous les diviseurs de N.
fonction NDIV0(N)
local D,L1,L2,K
2 => D
{1} => L1
tant que (N ≠ 1) faire
{}=>L2
0=> K:
tantque ((N MOD D) =0) faire
N/D=> N
K+1 =>K
concat(L2,L1*D K)=> L2
ftantque
concat(L1,L2)=> L1
D+1=> D
ftantque
retourne(L1)
ndiv0(n):={ local d,l1,l2,k; d:=2; l1:=[1]; while (n!=1) { l2:=[]; k:=0; while (irem(n,d)==0) { n:=iquo(n,d); k:=k+1; l2:=concat(l2,l1*d^k); } l1:=concat(l1,l2); d:=d+1; } return(l1); }
On peut améliorer ce programme en calculant l1*d^
k au fur et
à mesure sans utiliser k....
On a en effet sur l’exemple précédent n=360=23*32*5 :
l1:=[1];
les puissances de 2 sont obtenus avec
l2:=l1 on a alors l2:=[1]
l2:=l2*2;l1:=concat(l1,l2) on a alors l2:=[2];l1:=[1,2]
l2:=l2*2;l1:=concat(l1,l2) on a alors l2:=[4];l1:=[1,2,4]
l2:=l2*2;l1:=concat(l1,l2) on a alors l2:=[8];l1:=[1,2,4,8]
les puissances de 3 sont obtenus avec
l2:=l1 on a alors l2:=[1,2,4,8]
l2:=l2*3;l1:=concat(l1,l2) on a alors l2:=[3,6,12,24]; l1:=[1,2,4,8,3,6,12,24]
l2:=l2*3;l1:=concat(l1,l2) on a alors l2:=[9,18,36,72]; l1:=[1,2,4,8,3,6,12,24,9,18,36,72]
les puissances de 5 sont obtenus avec
l2:=l1 on a alors l2:=[1,2,4,8,3,6,12,24,9,18,36,72]
l2:=l2*5;l1:=concat(l1,l2) on a alors l2:=[5,10,20,40,15,30,60,120,45,90,180,360]
donc
l1:=[1,2,4,8,3,6,12,24,9,18,36,72,5,10,20,40,15,30,60,120,45,90,180,360]
On tape :
ndiv1(n):={ local d,l1,l2; d:=2; l1:=[1]; while (n!=1) { l2:=l1; while (irem(n,d)==0) { n:=iquo(n,d); l2:=l2*d l1:=concat(l1,l2); } d:=d+1; } return(l1); }:;
On peut encore améliorer ce programme si on tient compte du fait qu’après
avoir éventuellement divisé N par 2 autant de fois qu’on le pouvait,
les diviseurs potentiels de N sont impairs.
On remplace alors :
D+1 => D
par :
si D=2 alors
D+1 => D
sinon
D+2 => D
fsi
On améliore le programme précédent en remarquant que,
si le diviseur potentiel D est tel que D>√N, c’est que
N est premier ou vaut 1.
On ne continue donc pas la recherche des diviseurs de N et quand
N est diffèrent de 1 on complète
L1 par L1*N.
Et aussi, on ne teste comme diviseur potentiel de N, que les
nombres 2, 3, puis les nombres de la forme 6*k−1 ou de la forme 6*k+1
(pour k∈ ℕ).
On remplace donc :
si D=2 alors
D+1 => D
sinon
D+2 => D
fsi
par :
si D<4 alors
2*D-1 => D
sinon
D+(4*D mod6) => D
fsi
fonction ndiv2(N)
local D,L1,L2,K
2 => D
{1} => L1
tant que (D ≤ √N) faire
{}=>L2
0=> K:
tantque ((N MOD D) =0) faire
N/D=> N
K+1 =>K
concat(L2,L1*D K)=> L2
ftantque
concat(L1,L2)=> L1
si D<4 alors
2*D-1 => D
sinon
D+(4*D mod6) => D
fsi
ftantque
si N ≠ 1 alors
concat(L1,L1*N) =>L1
fsi
retourne(L1)
ndivi(n):={ local d,l1,l2,k; d:=2; l1:=[1]; l2:=l1 while (irem(n,d)==0) { n:=iquo(n,d); l2:=l2*d l1:=concat(l1,l2); } d:=3; while (d<=sqrt(n) and n>1) { l2:=l1; while (irem(n,d)==0) { n:=iquo(n,d); l2:=l2*d l1:=concat(l1,l2); } d:=d+2; }; if (n!=1) {l1:=concat(l1,l1*n)}; return(l1); }:;
Si on ne teste pas d<sqrt(n) le programme est plus simple :
ndivis(n):={ local d,l1,l2,k; d:=2; l1:=[1]; l2:=l1 while (irem(n,d)==0) { n:=iquo(n,d); l2:=l2*d l1:=concat(l1,l2); } d:=3; while (n>1) { l2:=l1; while (irem(n,d)==0) { n:=iquo(n,d); l2:=l2*d l1:=concat(l1,l2); } d:=d+2; }; return(l1); }:;
Ou bien on utilise ifactors : on obtient un programme plus rapide surtout
lorsqu’il y a de grands facteurs premiers car la
décomposition en facteurs premiers est optimisée.
ndivfact(n) renvoie la liste des diviseurs de n et la longueur de
cette liste.
On calcule la longueur de cette liste en faisant le produit des exposants
augmentés de 1. Par exemple si n=360=23*32*5, la liste des diviseurs
de n=360 a comme longueur sd:=(3+1)*(2+1)*(1+1)=24
La liste l1 va contenir la liste des diviseurs et pour
chaque nouveau diviseur de n, la liste l2 contient les nouveaux
diviseurs qui doivent être rajoutés à l1.
Par exemple si n=360
au début
l1:=[1] et l2:=l1
d:=2 est un diviseur donc
l2:=2*l2 i.e. l2:=[2] et l1:=concat(l1,l2) i.e. l1=[1,2]
d:=2 est encore un diviseur donc
l2:=2*l2 i.e. l2:=[4] et l1:=concat(l1,l2) i.e. l1=[1,2,4]
d:=2 est encore un diviseur donc
l2:=2*l2 i.e. l2:=[8] et l1:=concat(l1,l2) i.e. l1=[1,2,4,8]
On a épuiser le diviseur 2. On recopie l1 dans l2
l2:=l1 i.e. l2=[1,2,4,8]
le nouveau diviseur est 3 donc
l2:=3*l2 i.e. l2:=[3,6,12,24] et l1:=concat(l1,l2) i.e.
l1=[1,2,4,3,6,12,24]
d:=3 est encore un diviseur donc
l2:=3*l2 i.e. l2:=[9,18,36,72] et l1:=concat(l1,l2) i.e.
l1=[1,2,4,8,3,6,12,24,9,18,36,72]
On a épuiser le diviseur 3. On recopie l1 dans l2
l2:=l1 i.e. l2=[1,2,4,8,3,6,12,24,9,18,36,72]
le nouveau diviseur est 5 donc
l2:=5*l2 i.e. l2:=[5,10,20,40,15,30,60,120,45,90,180,360] et
l1:=concat(l1,l2) i.e.
l1=[1,2,4,8,3,6,12,24,9,18,36,72,5,10,20,40,15,30,60,120,45,90,180,360]
On a épuiser tous les diviseurs donc les diviseurs de 360 sont
l1=[1,2,4,8,3,6,12,24,9,18,36,72,5,10,20,40,15,30,60,120,45,90,180,360]
On tape :
ndivfact(n):={ local F,d,l1,l2,k,kd,sf,sd,j; si n==0 alors retourne "erreur"; fsi; si n<0 alors n:=-n; fsi; F:=ifactors(n); sf:=size(F)-1; sd:=1; pour k de 1 jusque sf pas 2 faire sd:=sd*(F[k]+1); fpour; k:=1; l1:=[1]; while (k<=sf) { l2:=l1;kd:=F[k]; d:=F[k-1]; pour j de 1 jusque kd faire l2:=l2*d l1:=concat(l1,l2); fpour; k:=k+2; }; return l1,sd; }:;
Comparons les temps d’exécution sur 2 exemples.
On tape :
ndivis(30!);
On obtient :
Temps mis pour l’évaluation: 7.53
On tape :
ndivi(30!);
On obtient :
Temps mis pour l’évaluation: 8.03
On tape :
ndivfact(30!);
On obtient :
Temps mis pour l’évaluation: 7.33
Mais si on tape :
ndivis(30!*907);
On obtient :
Temps mis pour l’évaluation: 19.28
On tape :
ndivi(30!*907);
On obtient :
Temps mis pour l’évaluation: 17.07
On tape :
ndivfact(30!*907);
On obtient :
Temps mis pour l’évaluation: 17.07
Donc dans le 1ier cas ndivis va plus vite et dans le 2nd cas c’est
ndivi qui va plus vite.
On utilise le programme factprem (qui donne la liste des facteurs premiers de N (cf 8.4.2) pour obtenir la liste des diviseurs de N selon l’algorithme utilisé dans NDIV1.
fonction fpdiv(N) //renvoie la liste des diviseurs de n en utilisant factprem local L1,L2,L3,D,ex,S factprem(N)=>L3 dim(L3)=>S {1}=>L1 pour K de 1 a S-1 pas 2 faire {}=>L2 L3[K]=>D L3[K+1]=>ex pour J de 1 a ex faire concat(L2,L1*(D^J))=>L2 } concat(L1,L2)=>L1 } retourne(L1) }
fpdiv(n):={ //renvoie la liste des diviseurs de n en utilisant factprem local l1,l2,l3,d,ex,s; l3:=factprem(n); s:=size(l3); l1:=[1]; for (k:=0;k<s-1;k:=k+2) { l2:=[]; d:=l3[k]; ex:=l3[k+1]; for (j:=1;j<=ex;j++) { l2:=concat(l2,l1*(d^j)); } l1:=concat(l1,l2); } return(l1); }
Pour obtenir la liste des diviseurs de N selon l’algorithme utilisé
dans NDIV1, on utilise le programme criblefact
(cf 8.5.2) qui donne la liste des facteurs premiers de N.
fonction criblediv(N) //renvoie la liste des diviseurs de n en utilisant factprem local L1,L2,L3,D,ex,S criblefact(N)=>L3 dim(L3)=>S {1}=>L1 pour K de 1 a S-1 pas 2 faire {}=>L2 L3[K]=>D L3[K+1]=>ex pour J de 1 a ex faire concat(L2,L1*(D^J))=>L2 } concat(L1,L2)=>L1 } retourne(L1) }
criblediv(n):={ //renvoie la liste des diviseurs de n en utilisant criblefact local l1,l2,l3,d,ex; l3:=criblefact(n); s:=size(l3); l1:=[1]; for (k:=0;k<s-1;k:=k+2) { l2:=[]; d:=l3[k]; ex:=l3[k+1]; for (j:=1;j<=ex;j++) { l2:=concat(l2,l1*(d^j)); } l1:=concat(l1,l2); } return(l1); }
-Premier algorithme
On utilise deux variables locales PUIS et I.
On fait un programme itératif de façon qu’à chaque étape, PUIS
représente AI modN
fonction puimod1 (A, P, N) local PUIS, I 1=>PUIS pour I de 1 a P faire A*PUIS mod N =>PUIS fpour retourne PUIS ffonction
-Deuxième algorithme
On n’utilise ici qu’une seule variable locale PUI, mais on fait varier P de
façon qu’à chaque étape de l’itération on ait :
PUI * AP modN=constante. Au début PUI=1 donc
constante=AP modN (pour la valeur initiale du paramètre P, c’est
à dire que cette constante est égale à ce que doit retourner la
fonction), et, à chaque étape, on utilise l’égalité
PUI*AP modN=(PUI*AmodN)*AP−1 modN, pour diminuer la valeur de P,
et pour arriver à la fin à P=0, et alors on a la constante=PUI.
fonction puimod2 (A, P, N) local PUI 1=>PUI tantque P>0 faire A*PUI mod N =>PUI P-1=>P ftantque retourne PUI ffonction
-Troisième algorithme
On peut aisément modifier ce programme en remarquant que :
A2*P = (A*A)P .
Donc quand P est pair, on a la relation :
PUI*AP = PUI*(A*AmodN)P/2 modN
et quand P est impair, on a la relation :
PUI*AP = (PUI*AmodN)*AP−1modN.
On obtient alors, un algorithme rapide du calcul de AP modN .
fonction puimod3 (A, P, N) local PUI 1=>PUI tantque P>0 faire si P mod 2 =0 alors P/2=>P A*A mod N=>A sinon A*PUI mod N =>PUI P-1=>P fsi ftantque retourne PUI ffonction
On peut remarquer que si P est impair, P−1 est pair.
On peut donc écrire :
fonction puimod4 (A, P, N) local PUI 1=>PUI tantque P>0 faire si P mod 2 =1 alors A*PUI mod N =>PUI P-1=>P fsi P/2=>P A*A mod N=>A ftantque retourne PUI ffonction
-Programme récursif
On peut définir la puissance par les relations de récurrence :
A0=1
AP+1 modN =(AP modN )*A modN
fonction puimod5(A, P, N) si P>0 alors retourne puimod5(A, P-1, N)*A mod N sinon retourne 1 fsi ffonction
-Programme récursif rapide
fonction puimod6(A, P, N) si P>0 alors si P mod 2 =0 alors retourne puimod6((A*A mod N), P/2, N) sinon retourne puimod6(A, P-1, N)*A mod N fsi sinon retourne 1 fsi ffonction
puimod(a,p,n):={ //calcule recursivement la puissance rapide a^p modulo n if (p==0){ return(1); } if (irem(p,2)==0){ return(puimod(irem(a*a,n),iquo(p,2),n)); } return(irem(a*puimod(a,p-1,n),n)); }
Étant donné deux entiers a∈ ℕ* et n∈ ℕ, n≥ 2, on veut
connaitre les différentes valeurs de ap modn pour p ∈ ℕ, c’est
à dire l’orbite de a dans (ℤ/nℤ,×).
On démontre que l’orbite se termine toujours par un cycle puisque ℤ/nℤ
a un nombre fini d’éléments.
ah=ah+T modn |
Solution
On tape :
(irem(2^
p ,24)$(p=0..10))
On obtient : 1,2,4,8,16,8,16,8,16,8,16
donc h=3 et T=2
On utilise la commande member qui teste si un élément est dans une
liste et member renvoie soit l’indice +1, soit 0.
On peut utiliser soit un tantque soit un repeter (tantque non arrêt faire....tantque ou repeter ...jusqua arrêt) et on remarquera
le test d’arrêt. On sait qu’une affectation renvoie la valeur affectée,
donc k:=member(b,L) renvoie soit 0 soit un nombre non nul. On fait une
boucle et on s’arrete quand k:=member(b,L) est non nul.
orbite1(a,n):={ local k,h,T,p,b,L; L:=[1]; p:=1; b:=irem(a,n); tantque !(k:=member(b,L)) faire L:=append(L,b); b:=irem(b*a,n); p:=p+1; ftantque; h:=k-1; T:=p-h; return h,T,L; }:;
orbite2(a,n):={ local k,h,T,p,b,L; L:=[]; p:=0; b:=1; repeter L:=append(L,b); b:=irem(b*a,n); p:=p+1; jusqua (k:=member(b,L)); h:=k-1; T:=p-h; return h,T,L; }:;
On dessine les points du cycle et de 2 périodes avec la couleur a ou la couleur 0 lorsque a=7 :
dessin(a,n):={ local k,h,T,L,P,s,LT; P:=NULL; h,T,L:=orbite1(a,n); s:=dim(L); LT:=mid(L,h); L:=concat(concat(L,LT),LT); pour k de 0 jusque s+2*T-1 faire P:=P,point(k,L[k]); fpour; si a==7 alors return affichage(P,epaisseur_point_3);fsi; return affichage(P,a+epaisseur_point_3); }:;
On tape : dessin(2,11)
On tape : dessin(3,11)
On tape : dessin(2,9)
On tape : dessin(3,9)
et on observe.....
On peut rappeler
an−1=1 modn |
aφ(n)=1 modn |
On tape :
est_premier_avec(n):={ local L,a; L:=NULL; pour a de 1 jusque n-1 faire si gcd(a,n)==1 alors L:=L,a; fsi; fpour; return L; }:;
Puis, on tape : E:=est_premier_avec(9)
On obtient : 1,2,4,5,7,8
et on a bien : euler(9)=6=dim(E)
Une démonstration rapide de ces théorèmes :
Si a et n sont premiers entre eux, a est inversible dans ℤ/nℤ,×
Soit E l’ensemble des nombres de [1..n] qui sont premiers avec n
(E:=est_premier_avec(n)) et soit Ea l’ensemble des k*a pour
k∈ E. Tous les éléments de Ea sont distincts et inversibles dans
ℤ/nℤ,×: donc
les ensembles E et Ea sont les mêmes. En faisant le produit de tous ces
éléments on obtient Πk∈ Ek=Πk∈ Eak=aφ(n)Πk∈ Ek,
Πk∈ Ek étant inversible dans ℤ/nℤ,×
on en déduit que aφ(n)=1.
- Premier algorithme
On va écrire un fonction booléenne de paramètre N, qui sera égale à
VRAI quand N est premier, et, à FAUX sinon.
Pour cela, on cherche si N posséde un diviseur différent de 1 et
inférieur ou égal à
E(√N) (partie entière de racine de N).
On traite le cas N=1 à part !
On utilise une variable booléenne PREM qui est au départ à VRAI, et
qui passe à FAUX dès que l’on rencontre un diviseur de N.
Fonction estpremier(N) local PREM, I, J
E(√N) =>J
Si N = 1 alors FAUX=>PREM sinon VRAI=>PREM fsi 2=>I
tantque PREM et I ≤J faire
si N mod I = 0 alors FAUX=>PREM sinon I+1=>I fsi ftantque retourne PREM ffonction
-Première amélioration
On peut remarquer que l’on peut tester si N est pair, et ensuite,
tester si N posséde un diviseur impair.
Fonction estpremier(N) local PREM, I, J
E(√N) =>J
Si (N = 1) ou (N mod 2 = 0) et N≠2 alors
FAUX=>PREM sinon VRAI=>PREM fsi 3=>I
tantque PREM et I ≤J faire
si N mod I = 0 alors FAUX=>PREM sinon I+2=>I fsi ftantque retourne PREM ffonction
- Deuxième amélioration
On regarde si N est divisible par 2 ou par 3, sinon on regarde si N posséde un diviseur de la forme 6 × k−1 ou 6 × k+1
(pour k∈ ℕ).
Fonction estpremier(N) local PREM, I, J
E(√N) =>J
Si (N = 1) ou (N mod 2 = 0) ou ( N mod 3 = 0) alors FAUX=>PREM sinon VRAI=>PREM fsi si N=2 ou N=3 alors VRAI=>PREM fsi 5=>I
tantque PREM et I ≤J faire
si (N mod I = 0) ou (N mod I+2 =0) alors FAUX=>PREM sinon I+6=>I fsi ftantque retourne PREM ffonction
estprem(n):={ //teste si n est premier local prem,j,k; if ((irem(n,2)==0) or (irem(n,3)===0) or (n==1)) { return(false); } if ((n==2) or (n==3)) { return(true); } prem:=true; k:=5; while ((k*k<=n) and prem) { if (irem(n,k)==0 or irem(n,k+2)==0) { prem:=false; } else { k:=k+6; } } return(prem); }
fonction estpremc(N) //utilise la fonction crible pour tester si n est premier local PREM,S; crible(floor(sqrt(N)))=>PREM dim(PREM)=>S si (N=1) retourne(FAUX) pour K de 1 a S faire si (N mod ,PREM[K])=0) retourne(FAUX); fsi fpour retourne(VRAI) ffonction
estpremc(n):={ //utilise la fonction crible pour tester si n est premier local prem,s; prem:=crible(floor(sqrt(n))); s:=size(prem); if (n==1) return(false); for (k:=0;k<s;k++){ if (irem(n,prem[k])==0){ return(false); } } return(true); }
Si N est premier alors tous les nombres K strictement inférieurs à N
sont premiers avec N, donc d’après le petit théorème de Fermat on a :
KN−1 = 1 modN
Par contre, si N n’est pas premier, les entiers K (1<K<N) vérifiant :
KN−1 = 1 modN sont peu nombreux.
La méthode probabiliste de Rabin consiste à prendre au hasard un
nombre K dans l’intervalle [2 ; N−1] (
1< K < N ) et à calculer :
KN−1 mod N
Si KN−1 = 1 mod N on refait un autre tirage du nombre K, et, si
KN−1 ≠ 1 mod N on est sûr que N n’est pas premier.
Si on obtient KN−1 = 1 mod N pour 20 tirages successifs de K on
peut conclure que N est premier avec une probabilité d’erreur faible :
on dit alors que N est pseudo-premier.
Bien sûr cette méthode est employée pour savoir si de grands nombres
sont pseudo-premiers mais on préfére utiliser la méthode de Miller-Rabin
(cf 8.12)
qui est aussi une méthode probabiliste mais qui donne N premier avec une
probabilité d’erreur plus faible (inférieure à (0.25)20 si on a
effectué 20 tirages, soit, une erreur de l’ordre de 10−12).
On suppose que :
hasard(N) donne un nombre entier au hasard entre 0 et N−1.
Le calcul de
KN−1 mod N
se fait grâce à l’algorithme de la puissance rapide (cf page ??).
On suppose que :
powmod(K, P, N) calcule KP modN
Fonction estprem(N) local K, I, P 1=>I 1=>P tantque P = 1 et I < 20 faire hasard(N-2)+2=>K powmod(K, N-1, N)=>P I+1=>I ftantque Si P =1 alors retourne VRAI sinon retourne FAUX fsi ffonction
La fonction powmod existe dans Xcas : il est donc inutile de la programmer.
rabin(n):={ //teste par la methode de Rabin si n est pseudo-premier local k,j,p; j:=1; p:=1; while ((p==1) and (j<20)) { k:=2+rand(n-2); p:=powmod(k,n-1,n); j:=j+1; } if (p==1) { return(true); } return(false); }
Rappel Le théorème de Fermat :
Si n est premier et si k est un entier quelconque alors kn=k modn.
et donc
Si n est premier et si k est premier avec n alors kn−1=1 modn.
Soit N=561=3*11*17. Il se trouve que l’on a :
pour tout A (A<N), on a AN=A mod N, donc si A est premier avec
N on a AN−1=1 mod N, le test de Rabin est donc en defaut,
seulement pour A non premier avec N.
Par exemple on a :
3560=375 mod 561
11560=154 mod 561
17560=34 mod 561
471560=375 mod 561
mais pour tous les nombres A non multiples de 3, 11 ou 17 on a :
AN−1=1 mod 561.
Par exemple on a :
5560=1 mod 561.
52N−1=1 mod 561.
On risque donc de dire avec le test de Rabbin que 561 est pseudo-premier.
Il faut donc affiner le test en remarquant que si N est premier l’équation:
X2=1 modN n’a pour solution que X=1 modN ou X=−1 modN.
Le test de Miller-Rabin est basé sur cette remarque.
Pour N=561, N−1=560, on a :
560=35*216
1335=208 mod 561
1335*2=67 mod 561
1335*4=1 mod 561
1335*8=1 mod 561...
On vient de trouver que 67 est solution de X2=1 mod561 donc on peut
affirmer que 561 n’est pas premier.
A=13 vérifie le test de Rabin car 13560=1 mod 561
mais ne vérifie pas le test de Miller-Rabin car
1335*2≠ −1 mod561 et 1335*2 ≠ 1 mod561
et pourtant 1335*4=1335*4=1 mod 561
Par contre ce test ne suffit pas pour affirmer qu’un nombre est premier car :
10135=560=−1 mod561 et donc 10135*2=1 mod561 et cela ne
fournit pas de solutions autre que 1 ou -1 à l’équation X2=1 mod561.
L’algorithme est basé sur :
1/ Le petit théorème de Fermat:
AN−1 = 1 modN si N est premier et si A<N.
2/ Si N est premier, l’équation X*X = 1 modN n’a pas d’autres
solutions que X=1 modN ou X=−1 modN.
En effet il existe un
entier k vérifiant X*X−1=(X+1)*(X−1)=k*N donc,
puisque N est premier, N divise X+1 ou X−1. On a donc soit
X=1 modN ou X=−1 modN.
On élimine les nombres pairs que l’on sait ne pas être premiers.
On suppose donc que N est impair et donc que N−1 est pair et s’écrit :
N−1=2t*Q avec t>0 et Q impair.
Si AN−1=1 modN c’est que AN−1 modN est le carré de
B=AN−1/2=A2t−1Q modN.
Si on trouve B≠ 1 modN et B≠ −1 modN on est sûr que N
n’est pas premier.
Si B=−1 modN on recommence avec une autre valeur de A.
Si B=1 modN on peut recommencer le même raisonnement si
N−1/2 est encore pair
(B=AN−1/2=(AN−1/4)2 modN) ou
si N−1/2 est impair, on recommence avec une autre valeur de A.
On en déduit que :
si N−1=2t.Q et
si AN−1 = 1 modN et
si AQ ≠ 1 modN et
si pour 0 ≤ ex < t on a A2ex.Q ≠ −1 modN c’est que N n’est pas premier.
D’où la définition :
Soit N un entier positif impair égal à 1+2t*Q avec Q impair.
On dit que N est pseudo-premier fort de base A si :
soit AQ=1 modN
soit si il existe e, 0 ≤ e<t tel que AQ*2e=−1 modN.
On voit facilement qu’un nombre premier impair est pseudo-premier fort dans
n’importe quelle base A non divisible par N.
Réciproquement on peut montrer que si N>4 n’est pas premier, il existe
au plus N/4 bases A (1<A<N) pour lesquelles N est pseudo-premier fort
de base A.
L’algorithme va choisir au hasard au plus 20 nombres Ak compris entre 2
et N−1 : si N est pseudo-premier fort de base Ak pour k=1..20 alors
N est premier avec une tres forte probabilité égale à
(1/4)20(<10−12).
Bien sûr cette méthode est employée pour savoir si de grands nombres
sont pseudo-premiers.
On suppose que :
hasard(N) donne un nombre entier au hasard entre 0 et N−1.
Le calcul de
KN−1 modN
se fait grâce à l’algorithme de la puissance rapide (cf page ??).
On notera :
powmod(K, P, N) la fonction qui calcule KP mod N
Fonction Miller(N) local Q,P,t,C,A,B,ex si (N=2) alors retourne FAUX si (N mod 2)==0) alors retourne FAUX N-1=>Q 0=>t tantque (Q mod 2 =0) faire t+1=>t E(Q/2)=>Q ftantque //N-1=2^t*Q 20=>C VRAI=>P tantque (C>0 et P) faire hasard(N-2)+2=>A 0=>ex powmod(A, Q, N)=>B si B<>1 alors tant que (B<>1) et (B<>N-1) et (ex<t-1) faire ex+1=>ex powmod(B,2,n)=>B ftantque si (B<>N-1) alors FAUX=>P fsi C-1=>C ftantque retourne P ffonction
La fonction powmod existe dans Xcas : il est donc inutile de la programmer.
miller(n):={ local p,q,t,c,a,b,ex; if (n==2){return(true);} if (irem(n,2)==0) {return(false);} q:=n-1; t:=0; while (irem(q,2)==0) { t:=t+1; q:=iquo(q,2); } //ainsi n-1=q*2^t c:=20; p:=true; while ((c>0) and p) { //rand(k) renvoie un nombre entier de [0,k-1] si k<999999999 if (n<=10^9) {a:=2+rand(n-2);} else {a:=2+rand(999999999);} ex:=0; b:=powmod(a,q,n); //si b!=1 on regarde si b^{2^(ex)}=-1 mod n (ex=0..t-1) if (b!=1) { while ((b!=1) and (b!=n-1) and (ex<=t-2)) { b:=powmod(b,2,n); ex:=ex+1;} //si b!=n-1 c'est que n n'est pas premier if (b!=n-1) {p:=false;} } c:=c-1; } return(p); };
On a besoin ici des fonctions de Xcas :
- asc qui convertit un caractère
ou une chaîne de caractères,
en une liste de nombres et,
- char qui convertit un nombre ou
une liste de nombres en un caractère
ou une chaîne de caractères.
On a :
char(n) pour n entier, (0 ≤ n ≤ 255) donne le caractère
ayant comme code ASCII l’entier n.
char(l) pour une liste d’entiers l (0 ≤ l[j] ≤ 255), donne la
chaîne de caractères dont
les caractères ont pour code ASCII les entiers l[j] qui composent
la liste l.
asc(mot) renvoie la liste des codes ASCII des lettres composant le mot.
Exemples
asc("A")=[65]
char(65)="A"
asc("Bonjour")= [66,111,110,106,111,117,114]
char([66,111,110,106,111,117,114])="Bonjour"
Remarque :
Il existe aussi la fonction ord qui a pour argument une chaîne de
caractères mais qui renvoie le code ASCII de la première lettre de la
chaîne de caractères :
ord("B")= 66
ord("Bonjour")= 66
- Version itérative
Si n<b, il n’y a rien à faire : l’écriture en base b est la même que
l’écriture en base dix et est n.
On divise n par b : n=b*q+r avec 0≤ r<b).
Le reste r de la division euclidienne de n par b
(r:=irem(n,b)) donne le dernier chiffre de l’écriture en base b de
n.
L’avant dernier chiffre de l’écriture en base b de n sera donné par le
le reste de la division euclidienne de q (q:=iquo(n,b)) par b.
On fait donc une boucle en remplacant n par q (n:=iquo(n,b))
tant que n ≥ b en mettant à chaque étape r:=irem(n,b) au
début de la liste qui doit renvoyer le résultat.
On écrit la fonction itérative ecritu
qui renvoie la liste des chiffres de n en base b :
ecritu(n,b):={ //n est en base 10 et b<=10, ecrit est une fonction iterative //renvoie la liste des caracteres de l'ecriture de n en base b local L; L:=[]; while (n>=b){ L:=concat([irem(n,b)],L); n:=iquo(n,b); } L:=concat([n],L); return(L); }
- Version récursive
Si n<b, l’écriture en base b est la même que
l’écriture en base dix et est n.
Si n ≥ b, l’écriture en base b de n est formée par
l’écriture en base b de q suivi de r, lorsque q et r sont le
quotient et le reste de la division euclidienne de n par b
(n=b*q+r avec 0≤ r<b).
On écrit la fonction récursive ecritur
qui renvoie la liste des chiffres de n en base b :
ecritur(n,b):={ //n est en base 10 et b<=10, ecritur est recursive //renvoie la liste des caracteres de l'ecriture de n en base b if (n>=b) return(concat(ecritur(iquo(n,b),b),irem(n,b))); else return([n]); }
On choisit 36 symboles pour écrire un nombre :
les 10 chiffres 0,1..9 et les 26 lettres majuscules A,B,..,Z.
On transforme tout nombre positif ou nul n (n<b) en un caractère :
ce caractére est soit un chiffre (si n<10) soit une lettre (A,B...Z)
(si 9<n<36).
chiffre(n,b):={ //transforme n (0<=n<b) en son caractere ds la base b if (n>9) n:=char(n+55); else n:=char(48+n); return(n); }
On obtient alors la fonction itérative ecritu:
ecritu(n,b):={ //n est en base 10 et b<=36, ecritu est une fonction iterative //renvoie la liste des caracteres de l'ecriture de n en base b local L,r,rc; L:=[]; while (n>=b){ r:=irem(n,b); rc:=chiffre(r,b); L:=concat([rc],L); n:=iquo(n,b); } n:=chiffre(n,b); L:=concat([n],L); return(L); }
- Version recursive
ecriture(n,b):={ //n est en base 10 et b<=36, ecriture est une fonction recursive //renvoie la liste des caracteres de l'ecriture de n en base b local r,rc; if (n>=b){ r:=irem(n,b); rc:=chiffre(r,b); return(append(ecriture(iquo(n,b),b),rc)); } else { return([chiffre(n,b)]); } }
En utilisant la notion de séquence on peut aussi écrire :
ecrit(n,b):= { //renvoie la sequence des chiffres de n dans la base b local m,u,cu; m:=(NULL); while(n!=0){ u:=(irem(n,b)); if (u>9) { cu:=(char(u+55)); } else { cu:=(char(u+48)); }; m:=(cu,m); n:=(iquo(n,b)); }; return(m); }
Il faut convertir ici chaque caractère en sa valeur (on convertit le
caractère contenu dans m en le nombre nm).
Si m=(c0,c1,c2,c3) alors n=c0*b3+c1*b2+c2*b+c3.
On calcule n en se servant de l’algorithme de Hörner (cf 8.15).
En effet le calcul de n=c0*b3+c1*b2+c2*b+c3 revient à calculer la
valeur du polynôme P(x)=c0*x3+c1*x2+c2*x+c3 pour x=b.
n va contenir successivement :
0 (n:=0) puis
c0 (n:=n*b+c0) puis
c0*b+c1 (n:=n*b+c1) puis
c0*b2+c1*b+c2=(c0*b+c1)*b+c2 (n:=n*b+c2) et enfin
c0*b3+c1*b2+c2*b+c3 (n:=n*b+c3).
On écrit donc la fonction nombre dans Xcas :
nombre(m,b):={ local s,k,am,nm,n; s:=(size(m)); n:=(0); k:=(0); if (s!=0) { while(k<s){ am:=(asc(m[k])[0]); if (am>64) { nm:=(am-55); } else { nm:=(am-48); }; if (nm>(b-1)) { return("erreur"); } n:=(n*b+nm); k:=(k+1); }; } return(n); }
On veut afficher en base dix la suite ordonnée des entiers dont l’écriture en base trois ne comporte que des 0 ou des 1 (pas de 2).
pasde21(n):={ local L,j,J,p; L:=[0]; p:=1; j:=1; tantque p<n faire J:=convert(j,base,3); si not(est_element(2,J)) alors L:= append(L,j); p:=p+1; fsi; j:=j+1; ftantque; retourne L; } :;
pasde22(n):={ local J,a,p,L; L:=[]; pour p de 0 jusque n-1 faire J:=convert(p,base,2); a:=convert(J,base,3); L:=append(L,a) fpour retourne L; }:;
pasde23(n):={ local L,p; L:=[0]; p:=1; tantque p<n faire L:=mat2list(map(L,x->[x*3,x*3+1])); p:=2*p; ftantque; L:=mid(L,0,n); retourne L; } :;Dans le programme ci-dessus la liste L est recréée à chaque itération, il est donc préférable de modifier ce programme pour qu’à chaque itération on ne calcule que les nouveaux termes dans la liste LA et ce sont ces nouveaux termes qui créront les termes suivants...
pasde23b(n):={ local L,p,LA; L:=[0,1]; LA:=[1]; p:=2; tantque p<n faire LA:=mat2list(map(LA,x->[x*3,x*3+1])); L:=concat(L,LA); p:=2*p; ftantque; L:=mid(L,0,n); retourne L; } :;À la fin de la boucle tantque, L a 2j=p≥ n éléments : il faut raccourcir la liste L (L:=mid(L,0,n)) et on calcule des termes pour rien...On modifie encore le programme, mais comme la liste LA engendre les termes 2 par 2, on calcule quand même un terme de trop si n est impair.
pasde23t(n):={ local L,p,LA; L:=[0,1]; LA:=[1]; p:=2; tantque 2p<=n faire LA:=mat2list(map(LA,x->[x*3,x*3+1])); L:=concat(L,LA); p:=2*p; ftantque; LA:=mat2list(map(mid(LA,0,iquo(n-p+1,2)),x->[x*3,x*3+1])); L:=concat(L,LA); retourne mid(L,0,n); } :;
pasde24(n):={ local L,j,p,puis3j; L:=[0]; j:=0; p:=1; puis3j:=1; tantque p<n faire L:=concat(L,L+[puis3j$p]); //L:=concat(L,map(L,x->x+puis3j)); j:=j+1; puis3j:=3*puis3j; p:=2*p; ftantque; L:=mid(L,0,n); retourne L; } :;À la fin de la boucle tantque, L a 2j=p>=n éléments : il faut donc raccourcir la liste L (L:=mid(L,0,n)) et on calcule des termes pour rien...On modifie donc le programme :
pasde24b(n):={ local L,j,p,puis3j; L:=[0]; j:=0; p:=1; puis3j:=1; tantque 2*p<=n faire L:=concat(L,L+[puis3j$p]); j:=j+1; puis3j:=3*puis3j; p:=2*p; ftantque; L:=concat(L,mid(L,0,n-p)+[puis3j$(n-p)]);; retourne L; }:;
Ce qui montre que le dernier algorithme est le meilleur...
Soient deux entiers p,q, premiers entre eux tel que q<p. On veut écrire un entier n sous la forme :
n= |
| nk( |
| )k avec nk<p |
On définit la suite :
u(0)=n, u(1)=iquo(u(0),p)*q, u(k+1)=iquo(u(k),p)*q
u est une suite décroissante donc il existe s tel que u(s)=0.
On a :
u(0)=iquo(u(0,p)*p+irem(u(0),p)=u(1)*p/q+irem(u(0),p)
u(1)=iquo(u(1,p)*p+irem(u(1),p)=u(2)*p/q+irem(u(1),p)
On pose n0=irem(u(0),p)
Donc :
qs−1u(0)=u(1)*p*qs−2+qs−1n0
et par itération :
pqs−2u(1)=u(2)p2qs−3+pqs−2n1 avec n1==irem(u(1),p)
...
qs−1u(0)=∑k=0s−1pkqs−k−1nk
ou encore :
n=u(0)=∑k=0s−1pkq−knk
Cette écriture est unique : on raisonne par récurrencesur n.
Le développement est unique pour tous les n<p.
Si il y a unicité pour tous les entiers m<n alors si on a 2 développements
de n :
n==∑k=0s−1pkq−kak et n==∑k=0s−1pkq−kbk
puisque n=a0+p*∑k=1s−1pk−1q−kak=b0+p*∑k=1s−1pk−1q−kbk= on en déduit que a0=b0=irem(n,p) et on applique
l’hypothèse de récurrence à iquo(n,p)*q qui est strictement inférieur à n.
On écrit le programme dev qui renvoie la liste de dimension s :
[n0,n1..ns−1] et le programme verif qui effectue la vérification.
dev(n,p,q):={ local L,s,u; si gcd(p,q)!=1 ou q>p-1 alors return "erreur"; fsi; L:=NULL; si n<p alors return n; fsi; s:=n; tantque s>0 faire u:=irem(s,p); s:=iquo(s,p)*q; L:=L,u; } return [L]; } :; verif(L,p,q):={ local n,s,k; n:=L[0]; s:=size(L); pour k de 1 jusque s-1 faire n:=n+L[k]*(p/q)^k; fpour; return n; }:;
On tape :
L:=dev(33,3,2)
On obtient :
[0,1,2,2,1,2]
On tape :
verif(L,3,2)
On obtient :
33
On tape :
L:=dev(133,13,8)
On obtient :
[3,2,9,11,8]
On tape :
verif(L,13,8)
On obtient :
133
Soit un polynôme P donné sous la forme d’une liste l formée par
les coefficients de P selon les puissances décroissantes.
hornerl(l,a) renvoie une liste formée par la valeur val
du polynôme en x=a et par la liste lq des coefficients selon les
puissances décroissantes du quotient Q(x) de P(x) par (x−a).
On a :
P(a)=l[0]*ap+l[1]*ap-1+...+l[p]=
l[p]+a*(l[p-1]+a*(l[p-2]+...+a*(l[1]+a*l[0])))
P(x)=l[0]*xp+l[1]*xp-1+...+l[p]=
(x-a)*(lq[0]*xp-1+...lq[p-1])+val
donc val=P(a)
et p=s-1 si s est la longueur de la liste l donc :
lq[0]=l[0]
lq[1]=a*lq[0]+l[1]
lq[j]=a*lq[j-1]+l[j]
....
val=a*lq[p-1]+l[p]
hornerl(l,a):={ local s,val,lq,j; s:=size(l); //on traite les polys constants (de degre=0) if (s==1) {return [l[0],[0]]}; // si s>1 lq:=[]; val:=0; for (j:=0;j<s-1;j++) { val:=val*a+l[j]; lq:=append(lq,val); } val:=val*a+l[s-1]; return([val,lq]); };
On tape :
hornerl([1,2,4],12)
On obtient :
[172,[1,14]]
ce qui veut dire que :
x2+2x+4=(x+14)(x−12)+172
Si le polynôme est donné avec son écriture habituelle.
Pour utiliser la fonction précédente on a alors besoin des deux
fonctions :
symb2poly qui transforme un polynôme en la liste de ses
coefficients selon les puissances décroissantes.
poly2symb qui transforme une liste en l’écriture habituelle du
polynôme ayant cette pour coefficients selon les puissances décroissantes.
hornerp(p,a,x):={ //ne marche pas pour les polys constants (de degre=0) local l,val,lh; l:=symb2poly(p,x); lh:=hornerl(l,a); p:=poly2symb(lh[1],x); val:=lh[0]; return([val,p]); };
On tape :
hornerp(x^
2+2x+4,12,x)
On obtient :
172,x+14
On tape :
hornerp(y^
2+2y+4,12,y)
On obtient :
172,y+14
Dans Xcas, il existe la fonction horner qui calcule selon la
méthode de Hörner la valeur d’un polynôme (donné sous forme de liste
ou par son expression) en un point :
On tape :
horner(x^
2+2x+4,12)
On obtient :
172
On tape :
horner(y^
2+2y+4,12,y)
On obtient :
172
On tape :
horner([1,2,4],12)
On obtient :
172
Trouver le plus petit entier positif n, tel que n, 2n, 3n, 4n, 5n,
6n contiennent exactement les mêmes chiffres.
On tape la fonction booléenne qui teste si les entiers n et m ont des
chiffres identiques.
On se sert de la fonction string qui transforme un entier en une chaine de caractères, puis on
transforme cette chaine en la liste de ses caractères ou en son code de Ascii, puis on
trie cette liste.
On tape :
chiffreid(n,m):={ local S1,S2,s1,s2,L1,L2,k; S1:=string(n);s1:=size(S1); S2:=string(m);s2:=size(S2); si s1!=s2 alors retourne faux; fsi; L1:=[sort(S1[k]$(k=0..s1-1))]; L2:=[sort(S2[k]$(k=0..s1-1))]; retourne L1==L2; }:;
ou
chiffreid(n,m):={ local S1,S2,s1,s2,L1,L2,k; S1:=string(n);s1:=size(S1); S2:=string(m);s2:=size(S2); si s1!=s2 alors retourne faux; fsi; L1:=sort(asc(S1)); L2:=sort(asc(S2)); retourne L1==L2; }:;
On tape la fonction booléenne qui teste si les entiers n, 2n, 3n, 4n,
5n, 6n ont des chiffres identiques. Si cela est le cas on
sait que n est divisible par 3 puisque la somme des chiffres de n est égale la somme des chiffres
de 3n.
On tape :
chiffreid16(n):={ local k; si irem(n,3)!=0 alors retourne faux; fsi; pour k de 6 jusque 2 pas -1 faire si chiffreid(n,k*n)==faux alors retourne faux fsi; fpour; retourne vrai; }:;
On tape la fonction qui renvoie le plus petit entier positif n, tel que n,
2n, 3n, 4n, 5n, 6n contiennent exactement les mêmes chiffres.
On tape :
ppchiffreid():={ local n; n:=3; tantque chiffreid16(n)==faux faire n:=n+3; ftantque; retourne n; }:;
On tape :
ppchiffreid()
On obtient :
142857
On vérifie :
On tape :
n:=142857;2*n;3*n;4*n;5*n;6*n
On obtient ;
142857,285714,428571,571428,714285,857142
On peut changer le programme ci-dessus pour savoir qui est le n suivant en
initialisant n à 142860. On trouve alors 1428570.
Puis on change à nouveau le programme ci-dessus pour savoir qui est le n suivant en
initialisant n à 1428573. On trouve alors 1429857.
Puis par curiosité, on cherche le suivant (mais c’est long !), on trouve 14285700.
On a donc le début de cette suite :
142857, 1428570, 1429857, 14285700, 14298570
On va écrire la fonction récursive estdivpoly qui a comme arguments,
deux polynômes A et B écrits sous forme symbolique et qui
renverra 1 si A est divisible par B et 0 sinon.
On rappelle que degree(A) renvoie le degré de A et que
valuation(A) renvoie la valuation de A
(la plus petite puissance de A).
Pour Savoir si A est divisible par B, on s’interesse aux termes de
plus haut degré et de plus bas degré de A et B : c’est à dire
qu’a chaque étape on essaye de faire la division par les 2 bouts ....
Par exemple si :
A=x^
3+2*x-3 et B=x^
2+x on sait que A n’est pas
divisible par B car -3 n’est pas divisible par x,
ou encore si :
A=x^
3+2*x^
2 et B=x^
2+1 on sait que A
n’est pas divisible par B car le quotient aurait pour degré
3-2=1 et pour valuation 2-0=2, ce qui est impossible 1<2 (le
degré n’peut pas être inférieur à la valuation.
estdivpoly(A,B):={ local da,db,va,vb,dq,vq,dva,dvb,dvq,Q,Ca,Cb; da:=degree(A); va:=valuation(A); dva:=da-va; db:=degree(B); vb:=valuation(B); dvb:=db-vb; if (A==0) then return 1;end_if; if ((da<db) or (va<vb)) then return 0;end_if; if ((dva==0) and (dvb>0)) then return 0;end_if; if ((dva>=0) and (dvb==0)) then return 1;end_if; Cb:=coeffs(B); if ((dva>0) and (dvb>0)) then dq:=da-db; vq:=va-vb; dvq:=dq-vq; if (dvq<0) then return 0;end_if; Ca:=coeffs(A); Q:=Ca[0]/Cb[0]*x^(dq); if (dvq==0) then A:=normal(A-B*Q); else Q:=Q+Ca[dva]/Cb[dvb]*x^(vq); A:=normal(A-B*Q); end_if; da:=degree(A); va:=valuation(A); end_if; return estdivpoly(A,B); };
On tape :
A:=normal((x^
4-x^
3+x^
2-1)*(x^
5-x^
3+x^
2-1)
puis,
estdivpoly(A,x^
4-x^
3+x^
2-1)
On obtient :
1
Lorsque A est divisible par B on peut en modifiant le programme
précédent avoir facilement le quotient exact de A par B.
On écrit la fonction récursive quoexpoly qui a trois arguments,
deux polynômes A et B écrits sous forme symbolique et 0.
quoexpoly renverra 1,Q si A=B*Q et 0 sinon.
Puis on écrit la fonction quopoly(A,B) qui est égale à
quoexpoly(A,B,0).
quoexpoly(A,B,SQ):={ local da,db,va,vb,dq,vq,dva,dvb,dvq,Q,Ca,Cb; da:=degree(A); va:=valuation(A); dva:=da-va; db:=degree(B); vb:=valuation(B); dvb:=db-vb; if (A==0) then return 1,SQ;end_if; if ((da<db) or (va<vb)) then return 0;end_if; if ((dva==0) and (dvb>0)) then return 0;end_if; if ((dva>=0) and (dvb==0)) then return 1,normal(SQ+normal(A/B));end_if; Cb:=coeffs(B); if ((dva>0) and (dvb>0)) then dq:=da-db; vq:=va-vb; dvq:=dq-vq; if (dvq<0) then return 0;end_if; Ca:=coeffs(A); Q:=Ca[0]/Cb[0]*x^(dq); if (dvq==0) then A:=normal(A-B*Q); SQ:=normal(SQ+Q); else Q:=Q+Ca[dva]/Cb[dvb]*x^(vq); A:=normal(A-B*Q); SQ:=normal(SQ+Q); end_if; da:=degree(A); va:=valuation(A); end_if; return quoexpoly(A,B,SQ); }; estquopoly(A,B):=quoexpoly(A,B,0);
On tape :
A:=normal((x^
4-x^
3+x^
2-1)*(x^
5-x^
3+x^
2-1)
puis,
estquopoly(A,x^
4-x^
3+x^
2-1)
On obtient :
1,x^
5-x^
3+x^
2-1
Pour rendre plus facile la lecture d’un grand nombre entier, on veut l’afficher par tranches, c’est à dire selon une chaîne de caractères constituées par les p premiers chiffres du nombre et d’un espace, puis les p suivants etc... On écrit le programme qui va afficher le nombre n par tranches de p chiffres:
affichen(n,p):={ local reste,result,s; result:=""; while (n>10^p) { //on transforme irem(n,10^p) en une chaine reste:=cat(irem(n,10^p),""); s:=size(reste); //on ajoute l'espace et les zeros qui manquent reste:=cat(" ",op(newList(p-s)),reste); n:=iquo(n,10^p); //on concatene reste avec result result:=cat(reste,result); } reste:=cat(n); return cat(reste,result); };
On tape :
affichen(1234567,3)
On obtient :
"1 234 567"
Pour avoir la transformation inverse, on va transformer une chaîne
comportant des chiffres et un autre caractère (par exemple un espace) en un
nombre entier.
On écrit le programme :
enleve(chn,ch):={ local l,s; s:=length(chn)-1; //on transforme chn en une liste de ces lettres //puis, on enleve le caractere ch de cette liste l:=remove(x->(ord(x)==ord(ch)),seq(chn[k],k,0,s)); //on transforme la liste en chaine return expr(char(ord(l))); };
On peut aussi remplacer la dernière ligne :
return char(ord(l))
(ord(l) transforme la liste de caractères en la liste de leurs codes
ascii et char transforme la liste des codes ascii en une chaîne).
par :
return cat(op(l))
car op(l) transforme la liste en une séquence et cat
concatène les éléments de cette séquence en une chaîne.
On tape :
enleve("1 234 567"," ")
On obtient :
1234567
Pour rendre plus facile la lecture d’un nombre décimal de [0,1[, on veut l’afficher par tranches, c’est à dire selon une chaîne de caractères constituées par les p premières décimales du nombre et d’un espace, puis les p suivants etc... On suppose que l’écriture de d comporte un point (.) suivi des décimales et ne comporte pas d’exposant (pas de e4)
On écrit le programme qui va afficher le nombre d par tranches de p chiffres:
affiched(d,p):={ local deb,result; //on suppose 0<=d<1 d:=cat(d,""); if (d[0]=="0") {d:=tail(d);} if (expr(tail(d))<10^p){return d;} deb:=mid(d,0,p+1); result:=cat(deb," "); d:=mid(d,p+1); while (expr(d)>10^p) { deb:=mid(d,0,p); result:=cat(result,deb," "); d:=mid(d,p); } return cat(result,d); };
On tape :
affiched(0.1234567,3)
On obtient :
".123 456 7"
Remarque
La commande enleve(affiched(d,3)," ") permet encore de retrouver d.
enleve(chn,ch):={ local l,s; s:=length(chn)-1; //on transforme chn en une liste de ces lettres //puis, on enleve le caractere ch de cette liste l:=remove(x->(ord(x)==ord(ch)),seq(chn[k],k,0,s)); //on transforme la liste en chaine return expr(char(ord(l))); };
Pour rendre plus facile la lecture d’un nombre décimal, on veut
l’afficher par tranches, c’est à dire selon une chaîne de caractères
constituées par sa partie entière écrite par tranches de p chiffres,
puis ses p premières décimales du nombre et d’un espace, puis les p
suivants etc...
Ici, le nombre f peut comporter un exposant à la fin de son écriture.
On écrit le programme qui va afficher le nombre décimal f par tranches
de p chiffres :
//pour les flottants f utiliser affichef // appelle affichen et affiched //par exemple affichef(1234.12345,3) affichef(f,p):={ local deb,result,s,indicep,fn,fd,indicee; //on suppose f>1 f:=cat(f); s:=size(f)-1; indicep:=member(".",seq(f[k],k,0,s)); indicee:=member("e",seq(f[k],k,0,s)); if (indicep!=0) { fn:=mid(f,0,indicep-1); fd:=mid(f,indicep-1); if (indicee!=0) { return affichen(expr(fn),p)+affiched(expr(mid(fd,0, indicee-1)),p)+mid(fd,indicee-1);} return affichen(expr(fn),p)+affiched(expr(fd),p) } return affichen(expr(f),p); };
On tape :
affichef(1234567.1234567,3)
On obtient (pour 12 chiffres significatifs) :
"1 234 567.123 46"
On obtient (pour 14 chiffres significatifs) :
"1 234 567.123 456 7"
On obtient (pour 15 chiffres significatifs) :
"0.123 456 712 345 670 0*e7"
Remarque
La commande enleve(affichef(q,3)," ") permet encore de retrouver q.
enleve(chn,ch):={ local l,s; s:=length(chn)-1; //on transforme chn en une liste de ces lettres //puis, on enleve le caractere ch de cette liste l:=remove(x->(ord(x)==ord(ch)),seq(chn[k],k,0,s)); //on transforme la liste en chaine return expr(char(ord(l))); };
Pour obtenir la partie entière et le développement décimal de
a/b, on va construire deux listes :
L1 la liste des restes et L2
la liste des quotients obtenus par l’algorithme de la potence .
On met le quotient q dans L1 et le reste r dans L2.
On a ainsi, la partie entière de a/b dans L1
et comme a/b=q+r/b on cherche la partie entière de 10*r/b qui va rallonger L1 etc...
Si on veut, par exemple, le développement décimale de
278/31 on cherche :
le quotient q=8 et le reste r=30 de la divison euclidienne de 278 par 31.
La partie entière est donc 8 et, on met 8L1 .
Pour avoir la partie décimale de 278/31, on fait
comme à la main l’algorithme de
la potence : on multiplie le reste trouvé par 10, on trouve 300
puis on le divise par 31 : le quotient trouvé 9 est
rajouté à L1 et le reste est rajouté à L2 etc...
On écrit la fonction potence qui renvoie dans la première liste
la partie entière puis les n décimales de a/b et dans la
deuxième liste les restes successifs obtenus.
potence(a,b,n):={ local L1,L2,k; b0:=b; b:=iquo(a,b0); a:=irem(a,b0); L1:=[b]; L2:=[a]; for (k:=1;k<=n and a!=0;k++){ b:=iquo(a*10,b0); a:=irem(a*10,b0); L2:=append(L2,a); L1:=append(L1,b); }; return([L1,L2]); };
En exécutant potence(278,31,20), on lit la partie entière de
278/31 et les chiffres de sa partie décimale dans la
première liste et, la suite des restes dans la deuxième liste.
Exercice
Écrire la partie entière et le développement décimal de :
a=11/7, b=15/14 et
c=17/28.
Calculer a−b et a−c
et donner leur partie entière et leur
développement décimal.
Que remarqez-vous ?
Exercice
Comment modifier L1 et L2 pour que les chiffres de la
partie décimale de a/b se lisent par paquet de trois
chiffres dans L1.
Avec l’exemple 278/31 on veut obtenir :
L1=[8,967,741,935 ...]
Tester votre modification pour 349/1332.
Que remarquez vous ?
division(a,b,n,t) donne la partie entière suivie de n paquets de t décimales (i.e. des n*t premières décimales) de a/b.
division(a,b,n,t):={ local L1,L2,p,q,r,k; L1:=[iquo(a,b)]; r:=irem(a,b); for (k:=1;k<=n and r!=0;k++) { q:=iquo(r*10^t,b); //10^(p-1)<= q <10^p if (q==0) {p:=1} else {p:=floor(ln(q)/ln(10)+1)}; //on complete par des zeros pour avoir un paquet de t decimales for (j:=p+1;j<=t;j++){ L1:=append(L1,0); } L1:=append(L1,q); r:=irem(r*10^t,b); } return(L1,r); };
On tape pour avoir 5*6=30 decimales :
division(2669,201,6,5)
On obtient :
[13,27860,69651,74129,35323,38308,45771],29
Trouver un nombre rationnel qui s’écrit :
0.123123123123... se terminant par une suite illimitée de 123.
Trouver un nombre rationnel qui s’écrit :
0.120123123123... se terminant par une suite illimitée de 123.
Écrire un programme qui permet de trouver un nombre rationnel à partir
d’un développement décimal périodique.
Réponse :
On écrit la fonction rationnel qui a comme le paramètre deux listes
l1 et l2 :
- l1 désigne la partie non périodique de ce développement et
l1[0] désigne la partie entière.
- l2 représente un développement décimal périodique.
rationnel(l1,l2):={ //l1 et l2 sont non vides local pui,s1,s2,n,p,np,pui,k; pui:=10; s2:=size(l2); n:=l2[0]; for (k:=1;k<s2;k++){ pui:=pui*10; n:=n*10+l2[k]; } // 0.123123...=123/999 p:=n/(pui-1); //np partie non periodique np:=l1[0]; s1:=size(l1); pui:=1; for (k:=1;k<s1;k++){ pui:=pui*10; np:=np+l1[k]/pui; } //pui=10^(s1-1) return(np+p/pui); };
Théorème1 Si a et b sont des entiers naturels premiers entre eux, alors il existe des entiers naturels a0,a1,...,an (0 ≤ n) tels que :
| =a0+ |
|
Si b≤ a les aj sont non nuls et, si a<b alors a0=0 et les autres
aj sont non nuls.
Définition
On pose alors a/b=(a0,a1,...an) et on dit que
(a0,a1,...an)
est une fraction continue : c’est le développement en fraction continue de
a/b.
Remarque si b≤ a et si a/b=(a0,a1,...an)
alors b/a=(0,a0,a1,...an).
Réduite et reste
On dit que la fraction Pp/Qp égale à la
fraction continue (a0,a1,...ap), où p≤ n, est la réduite de
rang p de a/b ou que c’est le développement en
fraction continue d’ordre p de a/b.
On dit que
r=(0,ap+1,..,an) est le reste du développement d’ordre p (r<1)
et on a a/b=(a0,a1,...ap+r)=(a0,a1,...ap,1/r),
a/b=a0+1/a1+1/a2+1/...ap−3+1/ap−2+1/ap+r.
Si Pp/Qp égale à la
fraction continue (a0,a1,...ap), où p≤ n, est la réduite de
rang p de a/b=(a0,a1,...an), on a :
P0=a0
Q0=1
P1=a0*a1+1
Q1=a1
Pp=Pp−1*ap+Pp−2
Qp=Qp−1*ap+Qp−2
En effet on le montre par récurrence :
P2/Q2=a0+a2/(a1a2+1) donc
P2=a2(a0+a1+1)+a0=a2P1+P0 et
Q2=a2a1+1=a2Q1+Q0
(a0,a1,...,ap+1/ap+1)=Pp+1/Qp+1 donc
Pp+1/Qp+1=((ap+1/ap+1)Pp−1+Pp−2)/((ap+1/ap+1)Qp−1+Qp−2)
Pp+1=ap+1(apPp−1+Pp−2)+Pp−1=ap+1Pp+Pp−1 et
Qp+1=ap+1(apQp−1+Qp−2)+Qp−1=ap+1Qp+Qp−1
Le programme f2dfc :
On veut transformer une fraction en son développement en fraction continue :
f2dfc(a/b)=(a0, a1,...an).
Pour obtenir le développement en fraction continue de a/b, on cherche le
quotient q et le reste r de la division euclidienne de a par b.
On a : q=a0 et a/b=a0+r/b=a0+1/(b/r) et, on continue en cherchant
la partie entiére de b/r qui sera a1....On reconnait
l’algorithme d’Euclide : la suite (a0, a1,...an) est donc la suite
des quotients de l ’algorithme d’Euclide.
On écrit le programme :
f2dfc(fract):={ local r,q,l,lres,a,b; l:=f2nd(fract); a:=l[0]; b:=l[1]; lres:=[]; while (b>0) { q:=iquo(a,b) lres:=concat(lres,q); r:=irem(a,b); a:=b; b:=r: } return lres; }
On tape :
f2dfc(2599/357)
On obtient :
[7,3,1,1,3,14]
Le programme f2reduites d’un rationnel et l’identité de Bézout :
On veut obtenir la suite des réduites de a/b.
L’algorithme pour obtenir les réduites ressemble beaucoup à l’algorithme
utilisé pour obtenir les coefficients u et v de l’identité de
Bézout (cf 8.3.5).
En effet on a :
P0=a0=a0*1+0 alors que v0=0
Q0=1=a0*0+1 alors que u0=1
P1=a0a1+1=P0*a1+1 alors que v1=1
Q1=a1=a1*Q0+0 alors que u1=0
Pp=Pp−1*ap+Pp−2 alors que vp=vp−2−ap−2vp−1
Qp=Qp−1*ap+Qp−2 alors que up=up−2−ap−2up−1
Ainsi :
P0=0+a0*1=v0−a0*v1=−v2
P1=1+P0*a1=v1−v2*a1=v3
P2=P0+P1*a2=−v2+v3*a2=−(v2−v3*a2)=−v4
On a donc pour tout p ≥ 0, si ap est la suite des quotients de
l’algorithme d’Euclide :
Qp=Qp−1*ap+Qp−2 avec Q−2=1=u0 et Q−1=0=u1 et,
Pp=Pp−1*ap+Pp−2 avec P−2=0=v0 et P−1=1=v1
Dans l’algorithme de Bézout on a :
up=−up−1*ap−2+up−2 et vp=−vp−1*ap−2+vp−2
Q0=0+u2 donc Q1=−u3, Q2=u4 etc...donc Qn=(−1)nun+2 et,
P0=−v2+0 donc P1=v3, P2=−v4 etc...donc Pn=(−1)n+1vn+2.
Donc Pn/Qn=−vn+2/un+2
Donc la suite Qj/Pj est donc la suite des −u/v.
On écrit le programme (calqué sur le programme Bezout avec les
listes) qui transforme une fraction en son développement en fraction continue
suivi de la suite de ces réduites :
f2reduites(fract):={ local lr,q,l,lres,la,lb,lq; l:=f2nd(fract); //a:=l[0];b:=l[1]; la:=[1,0,l[0]]; lb:=[0,1,l[1]]; lq:=[]; lres:=[]; while (lb[2]>0) { q:=iquo(la[2],lb[2]) lr:=la-q*lb; lq:=concat(lq,q); lres:=concat(lres,-lr[1]/lr[0]); la:=lb; lb:=lr; } return lq,lres; }
On tape :
f2reduites(2599/357)
On obtient :
[7,3,1,1,3,14],[7,22/3,29/4,51/7,182/25,2599/357]
Remarque :
On ne peut pas remplacer :
lr:=la-q*lb;
lres:=concat(lres,-lr[1]/lr[0])
par :
lr:=la+q*lb;
lres:=concat(lres,lr[1]/lr[0]);
car alors lr n’est plus la suite des restes !
Le programme dfc2reduites d’un rationnel et l’identité de Bézout:
On veut obtenir la suite des réduites d’une liste l (qui sera par exemple
le développement en fraction continue d’un rationnel a/b)
On écrit le programme (calqué sur le programme Bezout sans les
listes) qui transforme une liste [a0,a1,..an] en la
liste [a0,a0+1/a1,....,(a0+1/a1+1/...+1/an−1+1/an)] :
dfc2reduites(l):={ local s,p0,q0,p1,q1,p,q,lres,j; s:=size(l); lres:=[]; p0:=0; p1:=1; q0:=1; q1:=0; for (j:=0;j<s;j++){ p:=p0+l[j]*p1; q:=q0+l[j]*q1; lres:=concat(lres,p/q); p0:=p1; q0:=q1; p1:=p; q1:=q; } return lres; }
On remarquera que :
-la suite des P est initialisée par
p0 et p1, puis, quand j=0, on fait le calcul de P0 qui est mis dans
p, puis, quand j=1 on fait
le calcul de P1 qui est mis dans p, etc... et que
- la suite des Q est initialisée par :
q0 et q1, puis, quand j=0 on fait le calcul de Q0 qui est mis dans
q, quand j=1, on fait
le calcul de Q1 est mis dans q, etc...
On tape :
dfc2reduites([7,3,1,1,3,14])
On obtient :
[7,22/3,29/4,51/7,182/25,2599/357]
Théorème2 Si α est un nombre réel non rationnel, alors il existe des entiers naturels non nuls a0,a1,...,an et un réel β<1 tels que :
α=a0+ |
|
On dit que (a0,a1,...an) est le développement en fraction continue
d’ordre n+1 de α et que β est le reste de ce développement.
Un rationnel a un développement en fraction continue fini et
réciproquement, un développement en fraction continue fini représente
un rationnel.
Un réel non rationnel a un développement en fraction continue infini.
Si α est un nombre quadratique (i.e. α est racine d’une
équation du second degré), α a un développement en fraction
continue périodique et réciproquement, un développement en fraction
continue périodique représente un nombre quadratique.
On va écrire deux fonctions r2dfc et dfc2r.
r2dfc(alpha,n) qui transforme un réel alpha en son
développement en fraction continue et qui renvoie deux listes, soit :
- [a0,a1...ap],[] avec p ≤ n où les aj sont
des entiers, la deuxième liste est
vide et la première liste est le développement en fraction
continue de alpha (les aj sont des entiers et donc alpha
est une fraction)
- [a0,a1...an-1,b],[], la deuxième liste est vide et
la première liste est le développement en fraction
continue d’ordre n−1 de alpha suivi de b>1 (le reste est égal à
1/b), où les aj sont des entiers et b est un réel
plus grand que 1, soit,
- [a0,a1...ap],[ar,..ap] avec r≤ p < n
où les aj sont des entiers, la première liste est le
développement en fraction continue d’ordre p de alpha et
la deuxième liste représente la période de cedéveloppement en fraction
continue (les aj sont des entiers et donc alpha est un nombre
quadratique)
.
On remarquera dans le programme ci-dessous que :
a0=floor(alpha)=q remplace q:=iquo(a,b) lorsque
alpha=a/b
et que r=alpha-q remplace irem(a,b)/b lorsque
alpha=a/b
et donc que si r=alpha-q, a1=floor(1/r) etc...
Le problème ici est de pouvoir comparer alpha et q
c’est à dire savoir si r==0 et pour cela on est obligé de
faire les calculs avec beaucoup de décimales c’est à dire d’augmenter
le nombre de digits (on tape par exemple DIGITS:=30).
Il faut bien sûr repérer la période, pour cela on forme la liste
lr des restes successifs. La liste lq des parties entières
successives forme le début du développement en fraction continue.
r2dfc(alpha,n):={ local r,q,lq,lr,p,j; q:=floor(alpha); r:=normal(alpha-q); lq:=[]; lr:=[]; for (j:=1;j<=n;j:=j+1) { lq:=concat(lq,q); if (r==0){return (lq,[]);} p:=member(r,lr); if (p) {return (lq,mid(lq,p)):} lr:=concat(lr,r); alpha:=normal(1/r); q:=floor(alpha); r:=normal(alpha-q); } return (concat(lq,alpha),[]); };
On tape :
dfc2r(sqrt(2),1)
On obtient :
([1,sqrt(2)+1],[])
On tape :
dfc2r(sqrt(2),2)
On obtient :
([1,2],[2])
On tape :
dfc2r(pi),6
On obtient :
([3,7,15,1,292,1,(-33102*pi+103993)/(33215*pi-104348)],[])
Remarque
Le premier argument doit être un nombre exact, car sinon les calculs sont
faits en mode approché et le test r==0 n’est jamais réalisé...
On écrit la fonction réciproque de r2dfc qui à partir d’un
développement en fraction continue et d’un reste éventuel ou d’un
développement en fraction continue et d’une période éventuelle
renvoie un réel.
dfc2r(d,t) transforme en un réel, la liste d représente un
développement en fraction continue et la liste
t représente la période.
On remarquera que lorsque la liste
t n’est pas vide il faut déterminer le nombre 0<y<1 qui admet
cette liste périodique comme développement en fraction continue et
pour ce faire résoudre l’équation :
y=(0,t0,...tst-1+y)
le reste est alors y+ds-1 (s:=size(d)).
On écrit le programme :
dfc2r(d,t):={ local s,st,alpha,l,ap,k; s:=size(d); alpha:=d[s-1]; for (k:=s-2;k>=0;k:=k-1) {alpha:=normal(d[k]+1/alpha);} if (t==[]) {return normal(alpha);} st:=size(t); purge(y); ap:=t[st-1]+y; for (k:=st-2;k>=0;k:=k-1) {ap:=normal(t[k]+1/ap);} l:=solve(y=1/ap,y); if (l[0]>0){y:=normal(l[0]);}else{y:=normal(l[1]);}; alpha:=d[s-1]+y; for (k:=s-2;k>=0;k:=k-1) {alpha:=normal(d[k]+1/alpha);} return(normal(a)lpha); };
ou avec une écriture plus concise :
dfc2r(d,t):={ local s,st,alpha,l,ap,k; s:=size(d); st:=size(t); if (st==0) {y:=0;} else {purge(y); ap:=t[st-1]+y; for (k:=st-2;k>=0;k:=k-1) {ap:=normal(t[k]+1/ap);} l:=solve(y=1/ap,y); if (l[0]>0){y:=normal(l[0]);}else{y:=normal(l[1]);}; } alpha:=d[s-1]+y; for (k:=s-2;k>=0;k:=k-1) {alpha:=normal(d[k]+1/alpha);} return(normal(alpha)); };
1/ Développement en fraction continue de :
1393/972,
1+√13 et
1-√13.
On a :
r2dfc(1393/972,3)=[1,2,3,130/31],[]
r2dfc(1393/972,7)=[1,2,3,4,5,6],[]
et on a bien :
r2dfc(130/31,3)=[4,5,6],[]
r2dfc(31/130,4)=[0,4,5,6],[]
On peut vérifier que :
dfc2r([1,2,3,4,5,6],[])=1393/972
dfc2r([1,2,3+31/130],[])=dfc2r([1,2,3,130/31],[])=1393/972
On a :
r2dfc(1+sqrt(13),3)=[4,1,1,(sqrt(13)+2)/3],[]
r2dfc(1+sqrt(13),6)=[4,1,1,1,1,6],[1,1,1,1,6]
r2dfc(1-sqrt(13),7)=[-3,2,1,1,6,1,1],[1,1,6,1,1]
2/ Trouver les réels qui ont comme développement en fraction continue :
[2,4,4,4,4,4....] (suite illimitée de 4) et
[1,1,1,1,1,1....] (suite illimitée de 1).
On a :
dfc2r([2,4],[4])=sqrt(5)
ou encore
dfc2r([2],[4])=sqrt(5)
On a :
dfc2r([1],[1])=(sqrt(5)+1)/2
Si alpha a comme développement en fraction continue
(a0,a1,....an....), la suite des réduites est la suite des
nombres rationnels ayant comme développement en fraction continue :
(a0),(a0,a1),..,(a0,a1....an),.... .
On écrit le programme permettant d’obtenir les p premières
réduites de alpha.
On écrit le programme reduiten (on recalcule les réduites sans
se servir des relations de récurrence) :
reduiten(alpha,p):={ local l,k,ld,lt,st,s,q,lred,redu; ld:=r2dfc(alpha,p); l:=ld[0]; s:= size(l); if (s<p) { lt:=ld[1]; st:=size(lt); if (st!=0){ q:=iquo(p-s,st); for (j:=0;j<=q; j++){ l:= concat(l,lt) } } else { p:=s; } } lred:=[]; for (k:=1;k<=p;k++){ redu:=dfc2r(mid(l,0,k),[]); lred:=append(lred,redu); } return (lred); };
reduiten(sqrt(53),5)
On obtient :
[7,22/3,29/4,51/7,182/25]
On écrit maintenant le programme reduite permettant d’obtenir les
p premières réduites de alpha, en se servant de la fonction
dfc2reduites écrite auparavant et qui utilise les relations de
récurrence.
reduite(alpha,p):={ local l,ld,lt,st,s,q,lred; ld:=r2dfc(alpha,p); l:=ld[0]; s:= size(l); if (s<p) { lt:=ld[1]; st:=size(lt); if (st!=0){ q:=iquo(p-s,st); for (j:=0;j<=q; j++){ l:= concat(l,lt) } } } l:= mid(l,0,p); lred:=dfc2reduites(l); return lred; }
On tape :
reduite(sqrt(53),5)
On obtient :
[7,22/3,29/4,51/7,182/25]
On tape :
reduite(11/3,2)
On obtient :
[3,4]
Si alpha a comme développement en fraction continue
(a0,a1,....an....), la suite des réduites "plus 1"
est la suite des nombres
rationnels ayant comme développement en fraction continue :
(a0+1),(a0,a1+1),..,(a0,a1....an+1),.... .
On écrit le programme permettant d’obtenir les p premières
réduites "plus 1" de alpha.
reduite1(alpha,p):={ local l,ld,lt,st,s,q,lred; ld:=r2dfc(alpha,p); l:=ld[0]; s:= size(l); if (s<p) { lt:=ld[1]; st:=size(lt); if (st!=0){ q:=iquo(p-s,st); for (j:=0;j<=q; j++){ l:= concat(l,lt) } } } l:= mid(l,0,p)+1; lred:=dfc2reduites(l); return lred; }
Propriété des réduites de alpha :
Une réduite p/q approche alpha à moins de 1/q2 et si
s/t est la réduite plus 1 de même rang n on a :
- |p/q-s/t|<1/q2
- si n est pair p/q ≤ alpha ≤ r/s
- si n est impair r/s ≤ alpha ≤ p/q
- les réduites de rang pair et les réduites de rang impair forment
deux suites adjacentes qui convergent vers alpha
- les réduites plus 1 de rang pair et les réduites plus 1 de rang impair
forment deux suites adjacentes qui convergent vers alpha
Donc, si on pose :
lred:=reduite(alpha,10) et
lred1:=reduite1(alpha,10),
ces deux suites lred et lred1 fournissent un encadrement de
alpha plus précisément on a :
lred[0] ≤ lred1[1]≤..≤ lred[2p] ≤ lred1[2p+1]<alpha
alpha<lred[2p+1]≤ lred1[2p] ≤ ...≤ lred[1] ≤ lred1[0]
c’est à dire que l’encadrement fait avec 2 réduites successives de rang
p−1 et p est moins bon que l’encadrement fait avec la réduite de rang
p et la réduite plus 1 de rang p.
Exemple
On a :
r2dfc(sqrt(53),5)= [7,3,1,1,3,sqrt(53)+7],[]
dfc2r([7,3,1,1,3],[])=182/25
reduite(sqrt(53),5)[4]=182/25=7.28
reduite1(sqrt(53),5)[4]=233/32=7.28125
reduite(182/25,5)[4]=182/25=7.28
reduite1(182/25,5)[4]=233/32=7.28125
et donc 7.28<√53<7.28125
r2dfc(sqrt(53),6)= [7,3,1,1,3,14],[3,1,1,3,14]
dfc2r([7,3,1,1,3,14],[])=2599/357
reduite(sqrt(53),6)[5]=2599/357=7.28011204482
reduite1(sqrt(53),6)[5]=2781/382=7.28010471204
reduite(2599/357,5)[4]=2599/357=7.28011204482
reduite1(2599/357,5)[4]=2781/382=7.28010471204
et donc 7.28010471204<√53<7.28011204482
On a 1/3572=7.84627576521e−06 et 1/3822=6.8528823223e−06
La suite de Hamming est la suite des nombres entiers qui n’ont pour diviseurs
premiers que 2, 3 et 5.
Cette suite commence par : [2,3,4,5,6,8,9,10,12,15,16,18,20,24,25...]
On écrit tous les nombres de Hamming de 0 à n>0 et on barre les
nombres qui sont de la forme 2a*3b*5c avec a,b,c variant de 0 à un
nombre tel que 2a*3b*5c ≤ n: les nombres barrés (excepté 1)
sont les nombres de Hamming inférieurs à n>0.
Voici la fonction hamming(n) écrite en Xcas pour obtenir
les nombres de Hamming inférieurs à n>0.
hamming(n):={ local H,L,a,b,c,j,d; L:=makelist(x->x,0,n); //les nbres de Hamming sont 2^a*3^b*5^c c:=0; b:=0;a:=0; d:=1; while (d<=n) { while (d<=n){ while (d<=n) { L[d]:=0; //d:=5*d c:=c+1; d:=2^a*3^b*5^c; } c:=0; b:=b+1; //d:=2^a*3^b*5^c d:=2^a*3^b; } //c:=0; b:=0; a:=a+1; //d:=2^a*3^b*5^c d:=2^a; } H:=[]; for (j:=2;j<=n;j++) { if (L[j]==0) H:=append(H,j); } return H; }
ou encore en supprimant la variable c :
hamming(n):={ local H,L,a,b,j,d; L:=makelist(x->x,0,n); //les nbres de Hamming sont 2^a*3^b*5^c a:=0; d:=1; while (d<=n) { b:=0; while (d<=n){ while (d<=n) { L[d]:=0; d:=5*d; } b:=b+1; d:=2^a*3^b; } a:=a+1; d:=2^a; } H:=[]; for (j:=2;j<=n;j++) { if (L[j]==0) H:=append(H,j); } return H; }
ou encore en supprimant a,b,c et en preservant la valeur de d avant les while :
hamming(n):={ local H,L,d,j,k; L:=makelist(x->x,0,n); //les nbres de Hamming sont 2^a*3^b*5^c d:=1; while (d<=n) { j:=d; while (j<=n){ k:=j; while (k<=n) { L[k]:=0; k:=5*k; } j:=3*j; } d:=2*d; } H:=[]; for (j:=2;j<=n;j++) { if (L[j]==0) H:=append(H,j); } return H; }
On tape :
hamming(20)
On obtient :
[2,3,4,5,6,8,9,10,12,15,16,18,20]
On tape :
hamming(40)
On obtient :
[2,3,4,5,6,8,9,10,12,15,16,18,20,24,25,27,30,32,36,40]
Supposons que l’on ait trouvé les premiers éléments de cette suite par
exemple : 2,3,4,5.
L’élément suivant est obtenu en multipliant une des cases
précédentes par 2, 3 ou 5.
Le problème c’est d’avoir les éléments suivants dans l’ordre....
Comment trouver lélément suivant de H:=[2,3,4,5] :
on a déja multiplié H[0]=2 par 2 pour obtenir 4 donc
on peut multiplier H[1]=3 par 2 pour obtenir m=6 ou
multiplier H[0]=2 par 3 pour obtenir p=6 ou
multiplier H[0]=2 par 5 pour obtenir q=10.
L’élément suivant est donc 6=min(6,6,10) et H:=[2,3,4,5,6].
Maintenant, on a déja multiplier
H[0]=2 par 2 et par 3 pour obtenir 4 et 6 et
on a déja multiplier H[1]=3 par 2 pour obtenir 6 donc
donc
on peut multiplier H[2]=4 par 2 pour obtenir m=8 ou
multiplier H[1]=3 par 3 pour obtenir p=9 ou
multiplier H[0]=2 par 5 pour obtenir q=10.
L’élément suivant est donc 8=min(8,9,10) et H:=[2,3,4,5,6,8].
Pour que chaque terme de la suite soit multiplié par 2, par 3 et par 5,
il faut donc pévoir 3 indices :
k0 qui sera l’indice de l’élément qu’il faut multiplier par 2,
k1 qui sera l’indice de l’élément qu’il faut multiplier par 3,
k2 qui sera l’indice de l’élément qu’il faut multiplier par 5.
Cela signifie que :
pour tout r<k0 les 2*H[r] ont déjà été rajoutés,
pour tout r<k1 les 3*H[r] ont déjà été rajoutés,
pour tout r<k2 les 5*H[r] ont déjà été rajoutés,
Naturellement k0≥ k1≥ k2.
Les 3 candidats pour être l’élément suivant sont donc :
2*H[k0], 3*H[k1], 5*H[k2]
l’un de ces éléments est plus petit que les autres et on le rajoute à la
suite. Il faut alors augmenter l’indice correspondant de 1 : par exemple
si c’est 3*H[k1] qui est le minimum il faut augmenter k1 de 1 et
si 3*H[k1]= 5*H[k2] est le minimum, il faut augmenter k1 et
k2 de 1.
hamming(n) va renvoyer les n premiers éléments de la suite de
Hamming.
L’indice j sert simplement à compter les éléments de H.
k est une suite qui contient les indices k0,k1,k2.
On peut initialiser H à [2,3,4,5] donc j à 4,
et k à [1,0,0] (car H[0]=2 a été multiplié par 2,
mais pas par 3, ni par 5) mais cela suppose n>3.
On peut aussi initialiser H à [1], k à [0,0,0]
(H[0]=1 n’a pas été multiplié par 2, ni par 3, ni par 5)
et j à 0 puis enlever 1 de H à la fin car 1
n’est pas un terme de la suite.
Voici la fonction hamming(n) écrite en Xcas pour n>3.
//pour n>3 hamming(n):={ local H,j,k,m,p,q,mi; H:=[2,3,4,5]; j:=4; k:=[1,0,0]; while (j<n) { m:=2*H[k[0]]; p:=3*H[k[1]]; q:=5*H[k[2]]; mi:=min(m,p,q); H:=append(H,mi); j:=j+1; if (mi==m) {k[0]:=k[0]+1}; if (mi==p) {k[1]:=k[1]+1}; if (mi==q) {k[2]:=k[2]+1}; } return H; }
Voici la fonction hamming(n) écrite en Xcas pour n>0.
//pour n>0 hamming(n):={ local H,j,k,m,p,q,mi; H:=[1]; j:=0; k:=[0,0,0]; while (j<n) { m:=2*H[k[0]]; p:=3*H[k[1]]; q:=5*H[k[2]]; mi:=min(m,p,q); H:=append(H,mi); j:=j+1; if (mi==m) {k[0]:=k[0]+1}; if (mi==p) {k[1]:=k[1]+1}; if (mi==q) {k[2]:=k[2]+1}; } return tail(H); }:;
On tape :
hamming(20)
On obtient :
[2,3,4,5,6,8,9,10,12,15,16,18,20,24,25,27,30,32,36,40]
Le développement diadique de a/b∈ [0;1[ est lécriture de
a/b sous la forme :
a/b=d1/2+d2/22+...+dk/2k avec
dk∈ {0,1}.
diadiquen(a,b,n):={ local d,q,k,p,L; p:=2; L:=NULL; q:=a/b; pour k de 1 jusque n faire d:=floor(p*q); L:=L,d; q:=q-d/p; p:=2*p fpour; retourne L; }:;On tape : diadiquen(3,10,15)
diadiques(a,b):={ local d,q,k,p,L,A; L:=NULL; A:=NULL; q:=a/b; a:=numer(q); p:=2; d:=floor(p*q); repeter L:=L,d; si d!=0 alors A:=A,a; sinon A:=A,0; fsi; q:=q-d/p; a:=numer(q); p:=2*p d:=floor(p*q); k:=member(a,[A]); jusqua k!=0 and d!=0; retourne [L,mid([L],k-1)]; }:;On tape : diadiques(3,10)
On veut écrire un entier n ∈ ℕ sous la forme ∑j≥ 1 ajj! avec
0≤ aj<j pour tout j.
Par exemple 43=1· 4!+3· 3!+0· 2!+1· 1!.
| j· j!<N! |
Si n=∑j= 1j=J ajj! avec 0≤ aj<j et aJ≠ 0 on a :
J!≤ n=aJJ!+∑j= 1j=J−1 ajj!<J· J!+∑j= 1j=J−1 j· j!
donc
J!≤ n<(J+1)!
ecritfac(n):={ local j,J,k,L,a; L:=NULL; j:=1; tantque n>=j! faire j:=j+1 ftantque; J:=j-1; pour k de J jusque 1 pas -1 faire a:=iquo(n,k!); L:=L,a; n:=irem(n,k!); fpour; return L; }:;On tape : ecritfac(43)
Définitions
Lorsque pour p ∈ ℕ, Mp=2p−1 est premier on dit que c’est un nombre
premier de Mersenne.
Un nombre n est parfait si il est égal à la somme de ses diviseurs
propres (1 est compris mais pas n).
Par exemple 6 et 28 sont parfaits car 6=1+2+3 et 28=1+2+4+7+14.
Téorème 1
k est un nombre parfait pair si et seulement si il est de la forme
k= 2n−1(2n−1) avec Mn=2n−1 premier.
Téorème 2
Si Mn=2n−1 est premier, alors n est aussi premier.
La réciproque est fausse (voir le Test de Lucas-Lehmer ci-après),
par exemple, M11 n’est pas premier :
M11=211−1=2047=23*89
Téorème 3
Soient 2 nombres premiers p et q. Si q divise Mp = 2p−1, alors
q=+/−1 (mod8) et il existe k ∈ ℕ tel que q = 2kp + 1.
Téorème 4
Si p un nombre premier vérifiant p = 3 (mod4) alors 2p+1 est un
nombre premier si et seulement si 2p+1 divides Mp.
Téorème 5
Si on fait la somme des chiffres d’un nombre parfait pair différent de 6,
puis la somme des chiffres du résultat et que l’on continue le processus
alors on obtient 1.
Exercice
Écrire un programme qui teste si un nombre n vérifie le théorème 5.
Il faut donc utiliser la fonction revlist(convert(n,base,10)) de
Xcas qui renvoie la liste des chiffres de l’écriture en base 10 de
l’entier n.
On tape :
sumchiffre(n):={ local L,s; si n==6 alors retourne 1 fsi; s:=n; tantque s>9 faire L:=convert(n,base,10); s:=sum(L); ftantque; si s==1 alors retourne 1; sinon retourne s; fsi; }:;
Test de Lucas-Lehmer
Si p est un nombre premier alors le nombre de Mersenne Mp=2p−1 est
premier si et seulement si 2p−1 divise S(p−1) lorsque S(n) est la suite
définie par S(n+1)=S(n)2−2, et S(1)=4.
Exercice
Écrire le programme correspondant à ce test : on calculera la suite S(n)
modulo 2p−1 pour gagner du temps.
On tape :
Test_LL(p):={ local s,j; s := 4; pour j de 2 jusque p-1 faire s := s^2-2 mod n; fpour; si s == 0 alors return "2^"+string(p)+"-1 est premier"; sinon return "2^"+string(p)+"-1 est non premier"; fsi; }:;
On tape :
Test_LL(11213)
On obtient (Evaluation time: 6.43) :
2^
11213-1 est premier
On tape :
Test_LL(11351)
On obtient :
2^
11351-1 est non premier
Remarque
En janvier 1998, un élève ingénieur a prouvé que Mp était premier
pour p=3021377 (Mp a 909526 chiffres !).
Définitions
Un nombre n est parfait si il est égal à la somme de ses diviseurs
propres (1 est compris mais pas n).
Par exemple 6 et 28 sont parfaits car 6=1+2+3 et 28=1+2+4+7+14.
L’énoncé
Quels sont les nombres parfaits inférieurs à 11000?
Montrer que si 2p−1 est premier alors 2p−1(2p−1) est parfait.
La solution
On utilise l’instruction idivis(n) qui renvoie la liste des diviseurs
de l’entier n et l’instruction sum(L) qui renvoie la somme de la
liste L.
On tape avec les instructions françaises :
parfait(n):={ local j,a,b,L; L:=NULL; pour j de 2 jusque n faire a:=sum(idivis(j))-j; si a==j alors L:=L,j; fsi; fpour; retourne L; }:;
On tape pour avoir les nombres parfaits inférieur à 11000 :
parfait(11000)
0n obtient :
6,28,496,8128
Si 2p−1 est premier alors les diviseurs de 2p−1(2p−1) sont :
1,(2p−1),2,2(2p−1),22,22(2p−1),..2p−2,2p−2(2p−1),2p−1,2p−1(2p−1).
La somme de ces diviseurs est :
On tape pour avoir cette somme simplifiée et factorisée :
factor(normal(sum(2^
k*(1+2^
p-1),k=0..p-1)))
0n obtient :
2^
p*(2^
p -1)
La somme de tous les diviseurs propres de 2p−1(2p−1) est
2p−1(2p−1) donc si 2p−1 est premier 2p−1(2p−1) est parfait.
Euler a montré que tous les nombres parfaits pairs sont de cette forme.
Donc 2p−1(2p−1) est parfait si Mp=2p−1 est premier.
Pour p=2 on a 22−1=3 est premier donc 2*3=6 est parfait.
Pour p=3 on a 23−1=7 est premier donc 4*7=28 est parfait.
Pour p=5 on a 25−1=31 est premier donc 16*31=496 est parfait.
Pour p=7 on a 27−1=127 est premier donc 64*127=8128 est parfait.
Pour p=13 on a 213−1=8191 est premier donc 4096*8191=33550336 est parfait.
Pour p=17 on a 217−1=13107 est premier donc 65536*131071=8589869056 est parfait (il a été découvert en 1588 par Cataldi).
Pour p=19 on a 219−1=524287 est premier donc 262144*524287=137438691328 est parfait (il a été découvert en 1588 par Cataldi).
Puis pour p=31,61,89 on a encore 2p−1 premier ....
En 1936 le Dr Samuel I. Krieger dit que pour p=257 2513−2256 est
parfait (il a 155 chiffres ) malheureusement le nombre 2257−1 n’est pas
premier..... .
On refait donc un programme qui teste si 2p−1 est premier et on en déduit
le nombre parfait correspondant.
parfait2(p):={ local j,a,b,L; L:=NULL; pour j de 2 jusque p faire a:=2^(j-1); b:=2*a-1; si isprime(b) alors L:=[L,a*b,j]; fsi; fpour; retourne L; }:;
On tape :
A:=parfait2(1100)
size(A)
On obtient :
14
On tape :
A[13]
On obtient le 14ième nombre parfait :
[2^
606*(2^
607-1),607]
On tape :
B:=[A]:;
col(B,1)
On obtient la liste des nombres p≤ 1100 tels que 2p−1 soit premier :
[2,3,5,7,13,17,19,31,61,89,107,127,521,607]
Remarque : relation entre les nombres parfaits et les cubes
0n remarque qu’à part 6 chaque nombre parfait est égal à la somme des
cubes de nombres impairs consécutifs:
28=13+33=sum((2*n+1)^
3,n=0..1)
496=13+33+53+73=sum((2*n+1)^
3,n=0..3)
8128=13+33+53+73+93+113+133+153=sum((2*n+1)^
3,n=0..7)
33550336=sum((2*n+1)^
3,n=0..63)
8589869056=sum((2*n+1)^
3,n=0..255)
137438691328=sum((2*n+1)^
3,n=0..511)
0n remarque aussi que l’on fait la somme pour n variant de 0 à :
1=21−1
3=22−1
7=23−1
63=26−1
255=28−1
511=29−1
Question ouverte
Existe-t-il des nombres parfaits impairs ????????????
Ce que l’on sait c’est que si il en existe un alors il est tres grand !
Cete question est certainement le plus vieux problème de mathematiques non
résolu....
L’énoncé
On se propose d’écrire un programme qui donne la suite des couples amiables
dont l’un des nombres est inférieur ou égal à un entier n. Voici les
définitions des nombres parfaits et des nombres amiables :
Définitions
Un nombre n est parfait si il est égal à la somme de ses diviseurs
propres (1 est compris mais pas n).
Deux nombres a et b sont amiables ou amis, si l’un est égal à la
somme des diviseurs propres de l’autre et inversement.
Les nombres parfaits a sont des nombres amiables avec eux mêmes.
La solution
On utilise l’instruction idivis(n) qui renvoie la liste des diviseurs
de l’entier n et l’instruction sum(L) qui renvoie la somme de la
liste L.
Pour ne pas avoir 2 fois le même couple on n’affiche que les couples [j,a]
avec j≤ a.
On tape avec les instructions françaises :
amiable(n):={ local j,a,b,L; L:=NULL; pour j de 2 jusque n faire a:=sum(idivis(j))-j; b:=sum(idivis(a))-a; si b==j et j<=a alors L:=L,[j,a]; fsi; fpour; retourne L; }:;
On tape pour avoir les nombres amiable inférieur à 11000 :
amiable(11000)
0n obtient :
[6,6],[28,28],[220,284],[496,496],[1184,1210],[2620,2924],
[5020,5564],[6232,6368],[8128,8128],[10744,10856]
Les nombres parfaits a sont les nombres amiables [a,a].
On se propose d’écrire un programme qui donne les dimensions des
parallélépipèdes rectangles presque parfaits dont les côtés sont
inférieurs ou égaux à un entier n≤ 1000. Voici
la définition d’un parallélépipède rectangle presque parfait :
Définitions
Un parallélépipède rectangle est presque parfait si :
Par exemple, le parallélépipède rectangle de côtés 44 17 240 est
presque parfait.
Attention les 6 permutations de (44 17 240) représentent le même
parallélépipède rectangle.
Si 3 entiers (a,b,c) vérifiant a< b< c représentent un parallélépipède rectangle, pour su’il soit presque parfait il faut que :
Comment tester qu’un nombre p est un carré parfait ?
On peut écrire :
frac(sqrt(p))==0 ou
floor(sqrt(p))^
2-p==0
mais cela demande un calcul ! Il est donc préferable d’utiliser la commande
type qui renvoie le type de l’argument. Par exemple :
type(sqrt(4))==integer renvoie 1 et type(sqrt(5))==integer
renvoie 0.
On tape :
parapparfait(n):={ local a,b,c,L; L:=NULL; pour a de 1 jusque n faire pour b de a+1 jusque n faire si type(sqrt(a^2+b^2))==integer alors pour c de b+1 jusque n faire si type(sqrt(a^2+c^2))==integer alors si type(sqrt(c^2+b^2))==integer alors L:=L,[a,b,c]; fsi; fsi; fpour; fsi; fpour; fpour; retourne L; }:;
0n tape : L:=parapparfait(1000)
On obtient (c’est long !):
[44,117,240],[85,132,720],[88,234,480],[132,351,720],
[140,480,693],[160,231,792],[176,468,960],[240,252,275],
[480,504,550],[720,756,825]
On peut modifier le programme pour avoir pour chaque a, une liste provisoire
P qui sera la liste a,b1,b2...bp telle que a2+bj2 soit un
carré. Puis dans cette liste on cherchera les bj et les bk tels que
bj2+bk2 soit un carré. Le triplet [a,bj,bk] répond alors à la
quesstion.
On tape :
paralparfait(n):={ local a,b,c,L,P,j,s,k,b2; L:=NULL; pour a de 1 jusque n faire P:=a; pour b de a+1 jusque n faire si type(sqrt(a^2+b^2))==integer alors P:=P,b; fsi; fpour; s:=size(P)-1; pour j de 1 jusque s-1 faire b:=P[j]; b2:=b^2; pour k de j+1 jusque s faire c:=P[k]; si type(sqrt(b2+c^2))==integer alors L:=L,[a,b,c]; fsi; fpour; fpour; fpour; retourne L; }:;
0n tape : L:=paralparfait(1000)
On obtient (c’est 2 fois moins long !):
[44,117,240],[85,132,720],[88,234,480],[132,351,720],
[140,480,693],[160,231,792],[176,468,960],[240,252,275],
[480,504,550],[720,756,825]
Remarque
On peut facilement avoir les couples a,b tel que a2+b2 soit un carré.
On tape :
sommecarre(n):={ local a,b,L,P; L:=NULL; pour a de 1 jusque n faire P:=a; pour b de a+1 jusque n faire si type(sqrt(a^2+b^2))==integer alors P:=P,b; fsi; fpour; si size(P)>1 alors L:=L,[P];fsi; fpour; retourne L; }:;
On tape : sommecarre(100)
On obtient :
[3,4],[5,12],[6,8],[7,24],[8,15],[9,12,40],[10,24],
[11,60],[12,16,35],[13,84],[14,48],[15,20,36],
[16,30,63],[18,24,80],[20,21,48,99],[21,28,72],
[24,32,45,70],[25,60],[27,36],[28,45,96],[30,40,72],
[32,60],[33,44,56],[35,84],[36,48,77],[39,52,80],
[40,42,75,96],[42,56],[45,60],[48,55,64,90],[51,68],
[54,72],[56,90],[57,76],[60,63,80,91],[63,84],[65,72],
[66,88],[69,92],[72,96],[75,100],[80,84]
Par exemple, on voit que :
202+152 est un carré : c’est 252,
202+212 est un carré : c’est 292,
202+482 est un carré : c’est 522,
202+992 est un carré : c’est 1012.
ou encore
602+112 est un carré : c’est 612,
602+252 est un carré : c’est 652,
602+322 est un carré : c’est 682,
602+452 est un carré : c’est 752,
602+632 est un carré : c’est 872,
602+802 est un carré : c’est 1002.
602+912 est un carré : c’est 1092.
On peut aussi en déduire que :
602+1442 est un carré : c’est 1562 (car 152+362=392).
602+1752 est un carré : c’est 1852 (car 122+352=372).
602+2972 est un carré : c’est 3032.(car 202+992=1012).
Mais si on veut tous les nombres b≤ 300 tels que n2+b2 soit un
carré il est préférable d’écrire le programme :
n2plusb2(n,N):= { local b,P; P:=n; pour b de 1 jusque N faire si type(sqrt(n^2+b^2))==integer alors P:=P,b; fsi; fpour ; P; }:;
On tape : n2plusb2(20,1000)
On obtient :
20,15,21,48,99
On tape : n2plusb2(60,300)
On obtient :
60,11,25,32,45,63,80,91,144,175,221,297
seul 602+2212=2292 n’avait pas été trouvé précédemment car 229
est un nombre premier !
On tape : n2plusb2(60,1000)
On obtient :
60,11,25,32,45,63,80,91,144,175,221,297,448,899
On se propose d’écrire un programme qui donne la suite des nombres heureux inférieurs ou égaux à un entier n. Voici un algorithme définissant cette suite :
Par exemple :
après la première étape on a : 2,3,5,7,9,11,13,15,17...
après la deuxième étape on a : 2,3,5,7,11,17...
Ce programme ressemble au crible d’Eratosthène, mais si m est le
nombre que l’on vient d’entourer et qu’il est d’indice p, on supprime les
nombres d’indices p+m,p+2m...p+km mais dans la liste tab modifiée
et non les multiples du nombre m.
On tape avec les instructions françaises :
heureux(n):={ local tab,heur,m,j,p,k; tab:=j$(j=2..n); tab:=concat([0,0],[tab]); heur:=[]; p:=2; tantque (p<=n) faire m:=p; k:=0; pour j de p+1 jusque n faire si tab[j]!=0 alors k:=k+1; fsi; si irem(k,m)==0 alors tab[j]:=0 fsi; fpour; p:=p+1; si p<=n alors tantque tab[p]==0 and p<n faire p:=p+1 ftantque; si p==n and tab[p]==0 alors p:=n+1;fsi; fsi; ftantque; pour p de 2 jusque n faire si (tab[p]!=0) alors heur:=append(heur,p); fsi; fpour; retourne(heur); }:;
Dans ce programme on peut se passer de la liste heur : il suffit de
supprimer la dernière instruction pour et de mettre :
retourne remove(x->x==0,tab); à la place de retourne heur
On tape : heureux(100)
On obtient :
[2,3,5,7,11,13,17,23,25,29,37,41,43,47,53,61,67,71,77,83,89,91,97]
On peut aussi et ce sera plus rapide, modifier la liste tab au fur et
à mesure en supprimant à chaque étape les valeurs barrées c’est à
dire les valeurs mises à 0 en utilisant l’instruction remove et en
mettant au fur et à mesure les nombres heureux dans heur.
On tape avec les instructions françaises :
//renvoie la liste des nombres heureux<=n heureux2(n):={ local tab,heur,m,j,k,s; tab:=[j$(j=2..n)]; heur:=[]; s:=dim(tab); k:=0; tantque (s>0) faire j:=0; m:=tab[0]; heur[k]:=m; tantque j<s faire tab[j]:=0; j:=j+m; ftantque; tab:=remove(x->x==0,tab); s:=dim(tab); k:=k+1; ftantque; retourne(heur); }:;
On tape : heureux2(100)
On obtient :
[2,3,5,7,11,13,17,23,25,29,37,41,43,47,53,61,67,71,77,
83,89,91,97]
Résoudre l’équation de Pell c’est trouver les plus petits entiers x,y qui
sont solutions de x2−n*y2=1 lorsque n est un entier.
Euler à montré que l’on pouvait résoudre cette équation à l’aide du
développement en fraction continue de √n (par exemple dfc(n,20)).
Supposons que le développement en fraction continue de √n soit de
période k. On a :
Soit le développement en fraction continue de √n :
√n=[a0,a1,....ak,xk]
Soient :
| =[a0,a1,....ak] |
xk= |
|
On a les relations :
A2=0, A1=1, Ak=ak Ak−1+Ak−2 |
B2=1, B1=0, Bk=ak Bk−1+Bk−2 |
ak=floor( |
| ) |
Puisque xk+1=1/(xk−ak) on a :
P0=0, Pk+1=akQk−Pk |
Q0=1, Qk+1=ak(Pk−Pk−1)+Qk−1 |
Donc
| − |
| =(−1)k+1 |
|
On a :
√ |
| = |
| = |
|
Donc
QkAk−2+Pk Ak−1=n Bk−1 |
QkBk−2+Pk Bk−1=Ak−1 |
On a donc
(Ak−2Bk−1−Ak−1Bk−2)Qk=nBk−12−Ak−12=(−1)k−1Qk |
Si (Pn +√n)/Qn est le n-ième quotient du développement en
fraction continue de √n alors si Qh= Qh−1
(resp Ph= Ph−1) on a k=2h−1 (resp k=2h−2).
On en déduit donc la valeur k de la période.
La plus petite solution de x2 − ny2 =1 est donnée par :
Ak−1 + Bk−1√n où An/Bn est la n-ième fraction convergeant
vers √n.
Remarque
Soit n un entier. Si x0,y0 sont solutions de x2−n*y2=1 alors
xk,yk sont d’autres solutions grace à la formule :
xk+yk | √ |
| =(x0+y0 | √ |
| )k |
En effet, par récurrence si on a xk+yk√n=(x0+y0√n)k et
(xk,yk) solution de x2−ny2=1 alors :
xk+1+yk+1√n=(x0+y0√n)*(xk+yk√n) donc
xk+1=x0xk+ny0yk et
yk+1=x0yk+y0xk donc
xk+12=(x0xk+ny0yk)2=x02xk2+n2y02yk2+2nx0xky0yk=
x02xk2+(1+x02)(1+xk2)+2nx0xky0yk=2x02xk2+x02+1+xk2+2nx0xky0yk
nyk+12=n(x0yk+y0xk)2=nx02yk2+ny02xk2+2nx0yky0xk=
=x02(1+xk2)+(1+x02)xk2+2nx0yky0xk=2x02xk2+x02+xk2
donc
xk+12−nyk+12=1 |
Le programme ci dessous trouve les plus petits entiers A,B qui sont solutions
de A2−n*B2=1 lorsque n est entier qui n’est pas un carré parfait.
Les différentes valeurs de a sont le développement en fraction continue
de √n.
Ap/Bp sont les réduites de rang p (Ap/Bp=[a0,...ap])
On a Pk et Qk qui sont tels que:
√n=[a−0,a−1,...ak−1+Qk/(√n+Pk)]
Pell(n):={ local A,B,P,Q,R,a,sn,AA,BB,NA,NB; if (type(n)!=DOM_INT) {return "erreur"}; if (round(sqrt(n))^2==n) {return "erreur"}; R:=0; Q:=1; P:=0; sn:=floor(sqrt(n)); a:=sn; AA:=1;A:=a; BB:=0;B:=1; print(a,P,Q,R,A,B); while (A^2-n*B^2!=1) { P:=sn-R; Q:=(n-P^2)/Q; a:=floor((P+sn)/Q); R:=irem(P+sn,Q); NA:=a*A+AA; NB:=a*B+BB; AA:=A; BB:=B; A:=NA; B:=NB; print(a,P,Q,R,A,B); } return (A,B); } :;
On tape :
Pell(13)
On obtient :
649,180
En effet : 6492−13*1802=1
On tape :
Pell(43)
On obtient :
3482,531
En effet : 34822−43*5312=1