# Chapitre 7 Les programmes d’arithmétique

## 7.1 Quotient et reste de la division euclidienne

### 7.1.1 Les fonctions iquo, irem et smod de `Xcas`

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.

### 7.1.2 Activité

#### le texte de l’exercice

1. Vérifier que :

   13
   18
   | = |
   1
   2
   | + |
   1
   5
   | + |
   1
   45

2. On donne deux entiers a et b vérifiant : 0<b <a. On note q et r le quotient et le reste de la division euclidienne de a par b (a=bq+r avec 0≤ r<b).

   Démontrer que :

   q>0

   1
   q+1
   | < |
   b
   a
   | ≤ |
   1
   q

3. On définit u et v par :

   b
   a
   | − |
   1
   q+1
   | = |
   v
   u

   et

   u=a(q+1)

   Exprimer v en fonction de b et r.

   Démontrer que :

   v≤ b<a<u

   Si r=0, vérifier que :

   b
   a
   | = |
   1
   q

4. Si r est différent de zéro, on pose : a1=u et b1=v.

   Puis, on recommence : on divise a1 par b1.
   On trouve un quotient q1 et un reste r1.Si r1 est nul, vérifier que :

   b
   a
   | = |
   1
   q+1
   | + |
   1
   q1

   Si r1 n’est pas nul, on recommence.

   Montrer qu’il existe une suite finie d’entiers Q0, Q1,...Qn strictement croissante telle que :

   b
   a
   | = |
   1
   Q0
   | + |
   1
   Q1
   | +...+ |
   1
   Qn

5. Rédiger l’algorithme décrit ici et l’appliquer à la fraction :

   151
   221

#### L’algorithme

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
`

#### Traduction Xcas

On écrit la fonction `decomp` qui va décomposer selon l’algorithme la fraction `frac`. Cette fonction va renvoyer la liste `lres` égale à `[Q``0``,..Q``n``]` avec `0<Q``0``<,..<Q``n` et `frac=1/Q``0``+..+1/Q``n`.
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;
    }

#### Application à 151/221

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`

## 7.2 Calcul du PGCD par l’algorithme d’Euclide

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 :

PGCD(A,0) | = | A
PGCD(A,B) | = | PGCD(B,A mod B) si B ≠ 0

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 :

A= | B × Q1+R1 | 0 ≤ R1 < B
B= | R1 × Q2+R2 | 0 ≤ R2 < R1
R1= | R2 × Q3+R3 | 0 ≤ R3 < R2
.......

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

### 7.2.1 Traduction algorithmique

-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

### 7.2.2 Traduction Xcas

- 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)));
    };

### 7.2.3 Traduction MapleV

-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:

### 7.2.4 Traduction MuPAD

-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;

### 7.2.5 Traduction TI89 92

-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

### 7.2.6 Le pgcd dans ℤ[i]

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.

- Montrer que |q−a/b|2 ≤ 1/2. En déduire que |a−bq|2 ≤ |b|2/2 et que l’algorithme d’Euclide se termine lorsqu’on prend q comme quotient euclidien.
- Écrire un programme qui calcule le pgcd de 2 nombres de ℤ[i]. On normalisera le résultat (en multipliant le résultat par 1,-1,i ou -i) pour que le pgcd soit un nombre de partie rèelle strictement positive et de partie imaginaire positive ou nulle.

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`

### 7.2.7 Le pgcd dans ℤ[i√2]

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)`

## 7.3 Identité de Bézout par l’algorithme d’Euclide

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)

### 7.3.1 Version itérative sans les listes

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 :

A | =A0 × U+B0 × V avec U=1 et V=0
B | =A0 × W+B0 × X avec W=0 et X=1

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 :

R=A−B × Q=A0 × (U−W × Q)+B0 × (V−X × Q)=A0 × S+B0 × T
avec S=U−W × Q et T=V−X × Q

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

### 7.3.2 Version itérative avec les listes

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

### 7.3.3 Version récursive sans les listes

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

### 7.3.4 Version récursive avec les listes

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:

Bezout(B,R)=LT={W, X, pgcd(B,R)}
avec W × B+X × R=pgcd(B,R)

Donc:

W × B+X × (A−B × Q) | = | pgcd(B,R) ou encore
X × A+(W−X × Q) × B | = | pgcd(A,B).

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

### 7.3.5 Traduction Xcas

- 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]);
    };

## 7.4 Décomposition en facteurs premiers d’un entier

Dans cette section, on ne suppose pas connue une table de nombres premiers : on ne se sert donc pas du programme crible.

### 7.4.1 Les algorithmes et leurs traductions algorithmiques

- Premier algorithme
  Soit N un entier.
  On teste, pour tous les nombres D de 2 à N, la divisibilité de N par D.
  Si D divise N, on cherche alors les diviseurs de N/D etc...N/D joue le rôle de N et on s’arrête quand N=1
  On met les diviseurs trouvés dans la liste `FACT`.

      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

- Première amélioration
  On ne teste que les diviseurs D entre 2 et E(√N).
  En effet si N=D1*D2 alors on a :
  soit D1 ≤ E(√N), soit D2 ≤ E(√N) car sinon on aurait :
  D1*D2 ≥ (E(√N)+1)2 >N.

      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}`.

- Deuxième amélioration
  On cherche si 2 divise N, puis on teste les diviseurs impairs D entre 3 et E(√N).

  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

- Troisième amélioration
  On cherche si 2 et 3 divisent N, puis on teste les diviseurs D entre 5 et E(√N) de la forme 6*k−1 ou 6*k+1.
  On remarque que si :
  D=6*k−1 on a D+(4*D mod6)= 6*k+1
  et que si :
  D=6*k+1 on a D+(4*D mod6)=6*(k+1)−1
  Dans la liste `FACT`, on fait suivre chaque diviseur par son exposant, par exemple :
  `factprem3(12)={2,2,3,1}`.

      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

### 7.4.2 Traduction Xcas

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]);
    };

## 7.5 Décomposition en facteurs premiers en utilisant le crible

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 .

### 7.5.1 Traduction Algorithmique

    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);
    }

### 7.5.2 Traduction Xcas

    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);
    }

## 7.6 La liste des diviseurs

### 7.6.1 Les programmes avec les élèves

L’algorithme naïf :
`pour i de 1 a n faire
si (i divise n) alors
afficher i
fsi
fpour`
Les élèves remarquent que l’on peut avoir les diviseurs deux par deux.
`pour i de 1 a E(``√``n``) faire
si (i divise n) alors
afficher i, n/i
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` `→``i
tantque i<``√``n` `faire
si (i divise n) alors
afficher i, n/i
fsi
i+1` `→``i
ftantque
si i``·``i=n alors
afficher i
fsi`

### 7.6.2 Le nombre de diviseurs d’un entier n

On décompose n en facteurs premiers, puis on donne aux exposants de ces facteurs premiers toutes les valeurs possibles. Si n=a b cγ 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).

### 7.6.3 L’algorithme sur un exemple

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 a b cγ à partir de la liste `L1` des diviseurs de a bβ ?
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...

### 7.6.4 Les algorithmes donnant la liste des diviseurs de n

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*A``2`, ...,`L1*A``a` : au début `L1={1}` donc `L2={A,A``2``,...,A``a``}`.
On modifie la liste `L1` en lui concaténant la liste `L2`, ainsi `L1={1,A,A``2``,...,A``a``}`.
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*B``2`, ..., `L1*B``b` (c’est à dire `L2={B,B*A,B*A``2``,..,B*A``a``B``2``,B``2``*A,B``2``*A``2``,..,B``2``*A``a``,..,B``b``*A``a``,}`)
On modifie la liste `L1` en lui concaténant la liste `L2`, ainsi :
`L1={1,A,A``2``,...,A``a``,B,B*A,B*A``2``,...,B*A``a``,...,B``b``,B``b``*A,B``b``*A``2``,...,B``b``*A``a``}`.
Et ainsi de suite, jusqu’à avoir epuisé tous les diviseurs de `N`.

#### Traduction Algorithmique

`fonction NDIV1(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)`

#### Traduction Xcas

    ndiv1(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 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 encore 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` `mod` `6) => D` `fsi`

#### Traduction Algorithmique

`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` `mod` `6) => D` `fsi`
`ftantque`
`si N ≠ 1 alors`
`concat(L1,L1*N) =>L1`
`fsi`
`retourne(L1)`

## 7.7 La liste des diviseurs avec la décomposition en facteurs premiers

### 7.7.1 `FPDIV`

On utilise le programme `factprem` (qui donne la liste des facteurs premiers de `N` (cf 7.4.2) pour obtenir la liste des diviseurs de `N` selon l’algorithme utilisé dans `NDIV1`.

#### Traduction Algorithmique

    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)
    }

#### Traduction Xcas

    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);
    }

### 7.7.2 `CRIBLEDIV`

Pour obtenir la liste des diviseurs de `N` selon l’algorithme utilisé dans `NDIV1`, on utilise le programme `criblefact` (cf 7.5.2) qui donne la liste des facteurs premiers de `N`.

### 7.7.3 Traduction Algorithmique

    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)
    }

#### Traduction Xcas

    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);
    }

## 7.8 Calcul de AP mod N

### 7.8.1 Traduction Algorithmique

-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

### 7.8.2 Traduction Xcas

    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));
    }

### 7.8.3 Un exercice

É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.

- Trouver l’orbite de 2p mod24
- Écrire une fonction de a et n qui renvoie les plus petis entiers h≥ 0 et T>0 vérifiant :

  ah=ah+T modn

  et la liste des ap modn pour p=0..h+T−1
- Écrire une fonction de a, et n qui représente graphiquement ap modn en fonction de p.

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

- le Théorème de Fermat :si n est premier et 0<a<n, alors :

  an−1=1 modn

- la Généralisation du théorème de Fermat : si a et n sont premiers entre eux, alors si `euler(n)`=φ(n) est l’indicatrice d’Euler, on a :

  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.

## 7.9 La fonction "estpremier"

### 7.9.1 Traduction Algorithmique

- 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

### 7.9.2 Traduction Xcas

    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);
    }

## 7.10 La fonction estpremc en utilisant le crible

### 7.10.1 Traduction algorithmique

    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

### 7.10.2 Traduction Xcas

    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);
    }

## 7.11 Méthode probabiliste de Mr Rabin

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 7.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).

### 7.11.1 Traduction Algorithmique

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

### 7.11.2 Traduction Xcas

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);
    }

## 7.12 Méthode probabiliste de Mr Miller-Rabin

### 7.12.1 Un exemple

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.

### 7.12.2 L’algorithme

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.

### 7.12.3 Traduction Algorithmique

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

### 7.12.4 Traduction Xcas

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);
    };

## 7.13 Numération avec Xcas

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`

### 7.13.1 Passage de l’écriture en base dix à une écriture en base b

#### La base b est inférieure ou égale à 10

- 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]);
    }

#### La base b est inférieure ou égale à 36

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);
    }

### 7.13.2 Passage de l’écriture en base b de n à l’entier n

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 7.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);
    }

### 7.13.3 Un exercice et sa solution

#### L’énoncé

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).

1. Calculer à la main les huit premiers termes de cette suite.
2. Décrire un algorithme qui donne les 128 premiers termes de cette suite.
3. Écrire une fonction qui renvoie la liste des n premiers termes de cette suite.

#### La correction

1. Voici les 8 premiers termes de cette suite :
   `[0,1,3,4,9,10,12,13]` dont lécriture en base 3 est :
   `[[0],[1],[1,0],[1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]]`
2. Il y a plusieurs algorithmes possibles :
   - On écrit les nombres de 0 à N en base 3 et met dans la liste réponse ceux qui ne contiennent pas de 2 dans leur écriture en base 3. On s’arrete quand la liste réponse contient n éléments,
   - On écrit les nombres de 0 à n en base 2 et on considère cette écriture comme étant celle d’un nombre écrit en base 3.
   - On regarde comment on peut les termes de la suite se déduisent les uns des autres. On peut remarquer que :
     0=3*0,1=3*0+1,3=3*1,4=3*1+1,9=3*3,10=3*3+1,12=3*4,13=3*4+1....
   - On regarde comment sont faits les termes de la suite. On peut remarquer que :
     3=0+3,4=1+3 donc 3 et 4 ont èté obtenu à partir de 0 et 1 en leur ajoutant 3,
     9=0+9,10=1+9,12=3+9,13=4+9 donc 9,10,11 et 12 ont èté obtenu à partir de 0,1,3 et 4 en leur ajoutant 9... Les 8 prochains termes de cette suite seront obtenus en ajoutant 33 à chacun des 8 premiers. On obtient ainsi les 16 premiers termes de cette suite. Puis les 16 prochains termes de cette suite seront obtenus en ajoutant 34 à chacun des 16 premiers termes etc...
3. On va traduire avec `Xcas` chacun des algorithmes ci-dessus. Voici les fonctions de `Xcas` que l’on va utiliser :
   - La fonction `est_element(L,a)` qui teste si `a` est dans la liste `L` et qui renvoie `0` ou `n+1` si `n` est l’indice de la première occurence de `a` dans `L`,
   - La fonction `convert(n,base,b)` (resp `convert(L,base,b)`) qui convertit un entier `n` en la liste des coefficients en base `b` dans l’ordre croissant (resp qui convertit la liste `L` des coefficients en base `b` dans l’odre croissant en un entier `n`).
   - Pour ajouter un nombre `a` à chacun des termes d’une liste `L` on peut utiliser : la fonction `map` et écrire `map(L,x->x+a)` ou bien utiliser l’addition de `L` et de la liste `[a,a..a]` ayant la longueur de `L` et écrire `L+[a$dim(L)]`
   - Pour faire les opérations : multiplier par 3 et multiplier par 3 puis ajouter 1 sur chacun des termes d’une liste `L`, on peut utiliser : la fonction `map` et écrire `mat2list(map(L,x->[3*x,3*x+1]))` car `map(L,x->[3*x,3*x+1])`renvoie une matrice et `mat2list` transforme une matrice en liste.
   Voici les programmes correspondants aux algorithmes décrits précédement à la question 2. On tape les fonctions `pasde21(n)` ..`pasde24(n)` qui renvoient les `n` premiers termes de la liste demandée. La variable `p` contient à chaque étape la dimension de la liste `L`.

   -     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;
         }:;

   - À la fin de la boucle `tantque`, `L` a p≥ n éléments : il faut donc raccourcir la liste `L` (`L:=mid(L,0,n)`)

         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);
         }
         :;

   - La variable `j` contient le nombre d’iterations : à chaque étape on a p=2j et puis3j=3j.

         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;
         }:;

   Voici ce que l’on obtient :

   Ce qui montre que le dernier algorithme est le meilleur...

## 7.14 Écriture d’un entier dans une base rationnelle

Soient deux entiers p,q, premiers entre eux tel que q<p. On veut écrire un entier n sous la forme :

n= |
s−1
∑
k=0
| nk( |
p
q
| )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`

## 7.15 Traduction Xcas de l’algorithme de Hörner

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]*a``p``+l[1]*a``p-1``+...+l[p]=`
`l[p]+a*(l[p-1]+a*(l[p-2]+...+a*(l[1]+a*l[0])))`
`P(x)=l[0]*x``p``+l[1]*x``p-1``+...+l[p]=`
`(x-a)*(lq[0]*x``p-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`

## 7.16 Savoir si le polynôme A est divisible par B

### 7.16.1 Programmation de la fonction booléenne `estdivpoly`

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`

### 7.16.2 Autre version du programme précedent : `quoexpoly`

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`

## 7.17 Affichage d’un nombre en une chaîne comprenant des espaces

### 7.17.1 Affichage d’un nombre entier par tranches de p chiffres

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"`

### 7.17.2 Transformation d’un affichage par tranches en un nombre entier

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`

### 7.17.3 Affichage d’un nombre décimal de [0,1[ par tranches de p chiffres

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)));
    };

### 7.17.4 Affichage d’un nombre décimal par tranches de p chiffres

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)));
    };

## 7.18 Écriture décimale d’un nombre rationnel

### 7.18.1 Algorithme de la potence

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 8`L1` . 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 ?

### 7.18.2 Avec un programme

`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`

### 7.18.3 Construction d’un rationnel

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);
    };

## 7.19 Développement en fraction continue

### 7.19.1 Développement en fraction continue d’un rationnel

#### Les définitions

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 :

a
b
| =a0+ |
1
a1+ |
1
a2+ |
1
...an−2+ |
1
an−1+ |
1
an

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.

#### Propriétés des réduites

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

#### Les programmes

Le programme f2dfc :
On veut transformer une fraction en son développement en fraction continue :
`f2dfc(a/b)=(a``0``, a``1``,...a``n``)`.
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 7.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]`

### 7.19.2 Développement en fraction continue d’un réel quelconque

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+ |
1
a1+ |
1
a2+ |
1
...an−2+ |
1
an−1+ |
1
an+β

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.

### 7.19.3 Les programmes

On va écrire deux fonctions `r2dfc` et `dfc2r`.

#### La fonction r2dfc

`r2dfc(alpha,n)` qui transforme un réel `alpha` en son développement en fraction continue et qui renvoie deux listes, soit :
- `[a``0``,a``1``...a``p``],[]` avec `p ≤ n` où les `a``j` sont des entiers, la deuxième liste est vide et la première liste est le développement en fraction continue de `alpha` (les `a``j` sont des entiers et donc `alpha` est une fraction)
- `[a``0``,a``1``...a``n-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 `a``j` sont des entiers et `b` est un réel plus grand que 1, soit,
- `[a``0``,a``1``...a``p``],[a``r``,..a``p``]` avec `r≤ p < n`
où les `a``j` 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 `a``j` sont des entiers et donc `alpha` est un nombre quadratique)
.
On remarquera dans le programme ci-dessous que :
`a``0``=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`, `a``1``=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é...

#### La fonction dfc2r

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,t``0``,...t``st-1``+y)` le reste est alors `y+d``s-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));
    };

### 7.19.4 Exemples

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`

### 7.19.5 Suite des réduites successives d’un réel

Si `alpha` a comme développement en fraction continue `(a``0``,a``1``,....a``n``....)`, la suite des réduites est la suite des nombres rationnels ayant comme développement en fraction continue : `(a``0``),(a``0``,a``1``),..,(a``0``,a``1``....a``n``),....` .
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]`

### 7.19.6 Suite des réduites "plus 1" successives d’un réel

Si `alpha` a comme développement en fraction continue `(a``0``,a``1``,....a``n``....)`, la suite des réduites "plus 1" est la suite des nombres rationnels ayant comme développement en fraction continue : `(a``0``+1),(a``0``,a``1``+1),..,(a``0``,a``1``....a``n``+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;
    }

### 7.19.7 Propriété des réduites

Propriété des réduites de `alpha` :
Une réduite `p/q` approche `alpha` à moins de `1/q``2` et si `s/t` est la réduite plus 1 de même rang `n` on a :
- `|p/q-s/t|<1/q``2`
- 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

## 7.20 Suite de Hamming

### 7.20.1 La définition

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...]

### 7.20.2 L’algorithme à l’aide d’un crible

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]`

### 7.20.3 L’algorithme sans faire un crible

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.

### 7.20.4 La traduction de l’algorithme avec Xcas

`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]`

## 7.21 Développement diadique de a/b∈ [0;1[

### 7.21.1 L’énoncé

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}.

1. Écrire un programme qui affiche la liste des dk se terminant par la liste des premiers termes d1,d2..,dn de la suite d,
2. Écrire un programme qui affiche la liste des dk se terminant par la liste des dk qui forme la période. Par exemple, on a :
   7/12=1/2+0/22+0/23+1/24+0/25+1/26+.... et on écrit [1,0,0,1,0,[1,0]].

### 7.21.2 La solution

1. Si q=a/b et si d1=floor(2*q), on a a/b=d1/2+2a−b*d1/2*b. Donc, le développement diadique de a/b∈ [0;1[ est lécriture de q=a/b sous la forme : d1 suivi du développement diadique de la fraction 2a−b*d1/2*b de [0;1[.
   On tape pour avoir les premiers termes d1,d2..,dn de la suite d :

       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)`
   On obtient : `0,1,0,0,1,1,0,0,1,1,0,0,1,1,0`
2. Pour trouver la période, il faut savoir que l’on commence une période lorsque l’on retrouve parmi la liste des nouvelles fractions à développer un même numérateur. On garde donc dans `A` la liste des numérateurs en mettant un 0 quand le terme correspondant de d est nul.
   On tape pour avoir les premiers termes de la suite d et sa période :

       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 obtient : `[0,1,0,0,1,[1,0,0,1]]`

## 7.22 Écriture d’un entier comme ∑j≥ 1 ajj! avec 0≤ aj<j

### 7.22.1 L’énoncé

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!.

1. Quel est le plus grand entier J tel que aJ ≠ 0 ?
2. Écrire un programme `ecritfac` qui renvoie les coefficients aj du développement dans l’ordre décroissant : par exemple `ecritfac(43)` renverra `(1,3,0,1)`.

### 7.22.2 La solution

1. Montrons par recurrence que :

   j=N−1
   ∑
   j= 1
   | j· j!<N!

   vrai pour N=2 car 1<2!=2
   si ∑j= 1j=N−1 j· j!<N! alors ∑j= 1j=N−1 j· j!+N· N!<N!+N· N!=(N+1)!

   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)!

2. On cherche d’abord la valeur de J, puis on fait le quotient de n par J! et on recommence avec comme valeur de n le reste de la division de n par J!.
   On tape :

       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)`
   On obtient : `(1,3,0,1)` On tape : `ecritfac(150)`
   On obtient : `(1,1,1,0,0)`

## 7.23 Les nombres de Mersenne

### 7.23.1 Définitions et téorèmes

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;
    }:;

### 7.23.2 Test de Lucas-Lehmer

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 !).

## 7.24 Les nombres parfaits et les nombres amiables

### 7.24.1 Les nombres parfaits

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....

### 7.24.2 Les nombres amiables

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].

## 7.25 Les parallélépipèdes rectangles presque parfaits

### 7.25.1 L’énoncé

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 :

1. les longueurs de ses côtés sont des nombres entiers,
2. les longueurs des dagonales de ses 3 faces sont aussi des nombres entiers.

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.

### 7.25.2 La solution

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 :

1. √a2+b2 soit un entier,
2. √a2+c2 soit un entier,
3. √b2+c2 soit un entier

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`

## 7.26 Les nombres heureux

### 7.26.1 L’énoncé

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 :

- On écrit la suite des nombres entiers de 2 à n,
- On entoure 2 et on supprime les nombres de 2 en 2,
- On entoure 3 et on supprime les nombres de 3 en 3,
- On continue de la même façon : à chaque fois, on entoure m le premier nombre non entouré et on supprime les nombres de m en m,
- On s’arrête quand il ne reste que des nombres entourés : ce sont les nombres heureux

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...

### 7.26.2 La solution

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]`

## 7.27 L’équation de Pell

### 7.27.1 Les proriétés

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 :

- pour k=2h−1,
  √n=[a0,a1,..,ah−1,ah−1,..,a1,2a0],[a1,..,ah−1,ah−1,..,a1,2a0]].
  Par exemple
  `dfc(sqrt(13),20)=[3,1,1,1,1,6,[1,1,1,1,6]]`
- pour k=2h,
  √n=[a0,a1,..,ah−1,ah, ah−1,..,a1,2a0],[a1,..,ah−1,ah,ah−1,..,a1,2a0]] (ici k=5,)
  Par exemple
  `dfc(sqrt(23),20)=[4,1,3,1,8,[1,3,1,8]]` (ici k=4)

Soit le développement en fraction continue de √n :
√n=[a0,a1,....ak,xk]
Soient :

Ak
Bk
| =[a0,a1,....ak]

xk= |
Pk+ | √ |
n
Qk

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( |
Pk+ | √ |
n
Qk
| )

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

Ak
Bk
| − |
Ak+1
Bk+1
| =(−1)k+1 |
1
BkBk+1

On a :

√ |
n
| = |
Ak−1xk+Ak−2
Bk−1xk+Bk−2
| = |
Ak−1 | √ |
n
| +PkAk−1+QkAk−2
Bk−1 | √ |
n
| +PkBk−1+QkBk−2

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 | √ |
n
| =(x0+y0 | √ |
n
| )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

### 7.27.2 Le programme

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