corrigeTP1_ulma401.mws

Transcription

corrigeTP1_ulma401.mws
ULMA 401
Corrigé de la feuille de travaux pratiques N˚1
Exercice 1. Numéro de sécurité sociale.
43
Exercice 2. Nombres parfaits.
(a) Ecrire une fonction qui teste si un nombre est parfait:
On parcourt tout les entiers plus petits strictement que n et on additionne ceux qui divisent n:
> parfait := proc(n)
local s, d;
s:=0;
for d from 1 to n-1 do
if n mod d = 0 then
s:=s+d;
end if;
end do;
return evalb(s=n);
end proc;
(a) Vérifions que les numéros d’Alice, Bob et Oscar sont valables:
Un nombre est divisible par 97 si et seulement si son reste modulo 97 vaut 0.
Alice:
> (2530775073004+83) mod 97;
0
Bob:
> (1870985123598+15) mod 97;
0
Oscar:
> (1980412526354+94) mod 9;
0
On en déduit que les numéros d’Alice, Bob et Oscar sont valides.
(b) On essaie sur des exemples:
- Erreur sur un seul chiffre:
> (1530775073004+83) mod 97;
47
- Interversion de deux chiffres:
> (2350775073004+83) mod 97;
88
On souhaite retrouver un numéro de sécurité sociale sachant qu’il s’écrit
2 xx 07 35 231 584 19 où xx sont deux chiffres.
Premiére méthode: on essaie toute les valeurs possibles de xx de 00 à 99, en
remarquant que 2xx0735231584= 2000735231584+xx*10000000000
local s, d;
s := 0;
> for xx from 0 to 99 do
if (2000735231584+10000000000*xx+19) mod 97 = 0 then
print(xx);
end if;
end do;
6
28
496
Comme précédemment nous pouvons utiliser l’instruction select:
> select(parfait,[seq(i,i=1..1000)]);
43
Il est aussi possible de faire la recherche directement avec l’instruction "select" qui permet de
chercher dans une liste:
> select(xx->(2000735231584+10000000000*xx+19) mod 97 = 0,
[seq(i,i=0..99)]);
[43 ]
Deuxième méthode:
on remarque que xx est solution de l’équation
(2000735231584+xx*10000000000 +19) mod 97 = 0
dont une solution est donnée par
xx=-(2000735231584+19)*10000000000^(-1) mod 97
> print((-(2000735231584+19)*10000000000^(-1)) mod 97);
parfait := proc(n)
for d to n − 1 do if n mod d = 0 then s := s + d end if end do;
return evalb(s = n )
end proc
(b) En déduire la liste des nombres parfaits inférieurs à 1000
> for i from 1 to 1000 do
if parfait(i) then
print(i);
end if;
end do;
[6, 28, 496 ]
(c) Vérifier que 8128 est parfait
> parfait(8128);
true
> #parfait(2^12*(2^13-1));
> parfait2 := proc(a)
local s, d, n;
n:=2^(a+1)-1;
s:=0;
for d from 1 to n do
if n mod d = 0 then
s:=s+d;
end if;
end do;
n:=n*2^a;
s:=s*(2^(a+1)-1)-n;
return evalb(s=n);
end proc;
parfait2 := proc(a)
local s, d, n;
n := 2^(a + 1 ) − 1;
s := 0;
for d to n do if n mod d = 0 then s := s + d end if end do;
n := n∗2^a;
s := s∗(2^(a + 1 ) − 1 ) − n;
return evalb(s = n )
end proc
> parfait2(12);
true
(e)
> select(parfait2,[seq(i,i=1..18)]);
[1, 2, 4, 6, 12, 16, 18 ]
Exercice 3. Critère de divisibilités.
(a) divisibilité par 9:
Pour simplifier on écrit d’abord une fonction qui calcule la somme des chiffres.
> sommechiffres:= proc (n)
local s,m;
s:=0; m:=n;
while m>0 do
s:=s+(m mod 10);
m:=iquo(m,10);
end do;
s;
end proc;
sommechiffres := proc(n)
if m = 9 then
m:=0;
end if;
m;
end proc;
modneuf := proc(n)
local m;
m := n; while 10 ≤ m do m := sommechiffres(m) end do; if m = 9 then m := 0 end if; m
end proc
> modneuf(963);
0
> modneuf(1729);
1
(b) divisibilité par 11:
Pour cela nous commençons par écrire une fonction qui retourne la somme alternée des
chiffres:
> sommealtchiffres:= proc (n)
local s,m,e;
s:=0; m:=n; e:=1;
while m>0 do
s:=s+e*(m mod 10);
e:=-e:
m:=iquo(m,10);
end do;
s;
end proc;
sommealtchiffres := proc(n)
local s, m, e;
s := 0;
m := n;
e := 1;
while 0 < m do s := s + e∗(m mod 10 ); e := −e; m := iquo(m, 10 ) end do;
s
local s, m;
s := 0; m := n; while 0 < m do s := s + (m mod 10 ); m := iquo(m, 10 ) end do; s
end proc
> sommealtchiffres(519);
end proc
> sommechiffres(789);
> sommealtchiffres(7193);
24
Pour tester la divisibilité par neuf nous itérons la somme des chiffres jusqu’à ce qu’il n’y ait
plus qu’un chiffre.
> modneuf := proc (n)
local m;
m:=n;
while m>=10 do
m:=sommechiffres(m);
end do;
> sommealtchiffres(-12);
13
-12
0
Pour tester la divisibilité par 11 nous itérons la valeur absolue de la somme alternée des
chiffres jusqu’à ce qu’il n’y ait plus qu’un chiffre.
> divonze := proc (n)
local m;
m:=n;
while m>=10 do
m:=abs(sommealtchiffres(m));
end do;
if m = 9 then
m:=0;
end if;
evalb(m=0);
end proc;
divonze := proc(n)
local m;
m := n;
while 10 ≤ m do m := abs(sommealtchiffres(m)) end do;
if m = 9 then m := 0 end if;
evalb(m = 0 )
0.015
> t:=time():igcd(a,b);time()-t;
1
0.
(d) igcdex permet d’obtenir u et v tel que a*u+n*v=1. nous avons donc a*u mod n =1
> invmod:= proc(a,n)
local u,v;
if igcdex(a,n,’u’,’v’) <>1 then
print ("Error, a et n ne sont pas premier entre eux");
return 0;
end if;
return u;
end proc;
invmod := proc(a, n)
local u, v;
if igcdex(a, n, ’u’, ’v’) ≠ 1 then
end proc
> divonze(1111);
true
> divonze(12345);
false
Remarque: pour obtenir le reste modulo 11, il suffit de tenir compte du signe à chaque étapes.
Exercice 4. Algorithme d’Euclide.
print("Error, a et n ne sont pas premier entre eux" ); return 0
end if;
return u
end proc
> invmod(7,37);
16
(a)
> eucl:= proc(aa,bb)
local r,a,b;
a:=aa; b:=bb;
while b<>0 do
r:=a mod b;
a:=b;
b:=r;
end do;
a;
end proc;
> 16*7 mod 37;
eucl := proc(aa, bb)
local r, a, b;
a := aa; b := bb; while b ≠ 0 do r := a mod b; a := b; b := r end do; a
end proc
> eucl(21,34);
1
> eucl(24,36);
12
(b)
> r:=rand(10^1000):a:=r():b:=r():
t:=time():eucl(a,b);time()-t;
1
1
(e) L’équation a*x*+b*y=c a des solutions si et seulement si c est un multiple du pgcd d de a
et b.
Dans ce cas il existe u, v telque a*u+b*v=d et il suffit de multiplier par c/d.
La solution générale est donnée par x=u*c/d+l*b/d y=v*c/d-l*a/d ou l est un entier.
> equa:= proc(a,b,c)
local u,v,d;
d:=igcdex(a,b,’u’,’v’);
if c mod d <> 0 then
print ("Pas de solution");
return [];
end if;
return [u*c/d+l*b/d,v*c/d-l*a/d];
end proc;
equa := proc(a, b, c)
local u, v, d;
d := igcdex(a, b, ’u’, ’v’);
if c mod d ≠ 0 then print("Pas de solution" ); return [ ] end if;
return [u∗c / d + l∗b / d, v∗c / d − l∗a / d ]
end proc
> equa(11,13,5);
[30 + 13 l, −25 − 11 l ]
> 11*(30+13*l)+13*(-25-11*l);
5
Exercice 5. Ecrire une fonction qui teste naïvement si un
nombre est premier.
> estpremier:= proc(n)
local i;
for i from 2 to floor(sqrt(n)) do
if n mod i = 0 then
return false;
end if;
end do;
return true;
end proc;
estpremier := proc(n)
local i;
for i from 2 to floor(sqrt(n )) do if n mod i = 0 then return false end if end do;
return true
end proc
> estpremier(101);
true
Exercice 6. Déterminer les nombres premiers inférieurs à n
en utilisant le crible d’Eratosthène.
Remarque: pour être un crible d’Eratosthène, le programme ne doit pas faire de division, de
multiplication et de modulo, mais
seulement des additions et des soustractions.
> eratosthene:= proc(n)
local T,p,i;
T:=[seq(true,i=1..n)];
T[1]:=false;
for p from 2 to floor(sqrt(n)) do
if T[p] then
for i from p+p by p to n do
T[i]:=false;
end do;
end if;
end do;
select(i->T[i],[seq(i,i=1..n)]);
end proc;
eratosthene(100);
eratosthene := proc(n)
local T, p, i;
T := [seq(true, i = 1 .. n )];
T[1 ] := false;
for p from 2 to floor(sqrt(n )) do
if T[p ] then for i from 2∗p by p to n do T[i ] := false end do end if
end do;
select(i → T[i ], [seq(i, i = 1 .. n )])
end proc
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 ]
Exercice 7. Petit théorème de Fermat.
(a)
> estpremier(97);
true
(b)
> t:=time(): (1234^1234567 mod 7): time()-t;
4.351
> t:=time(): (1234&^1234567 mod 7): time()-t;
0.
(c) On vérifie que le petit théorème de Fermat est vrai por toutes les bases entre 1 et 96:
> for a from 1 to 96 do
if a &^ 96 mod 97 <> 1 then
print("erreur :",a);
end if;
end do;
(d)
> testpseudo:= proc(a,n)
return evalb(a&^(n-1) mod n=1);
end proc;
testpseudo := proc(a, n) return evalb(‘&^‘(a, n − 1 ) mod n = 1 ) end proc
(e)
> estpseudopremier:= proc (n,N)
local r,b,i;
r:=rand(1..n);
for i from 1 to N do
b:=r();
while gcd(b,n)<>1 do
b:=r();
end do;
if not testpseudo(b,n) then
return false;
end if;
end do;
return true;
end proc;
estpseudopremier := proc(n, N)
local r, b, i;
r := rand(1 .. n );
for i to N do
b := r( );
while gcd(b, n ) ≠ 1 do b := r( ) end do;
if not testpseudo(b, n ) then return false end if
end do;
return true
end proc
> estpseudopremier(150,10);
false
> estpseudopremier(17,10);
true
(f)
> estpseudopremier(561,10);
true
> estpseudopremier(1105,10);
true
> estpremier(561);
false
> estpremier(1105);
false
(g)
> t:=time(): estpremier(10^11+3): time()-t;
1.327
> t:=time(): isprime(10^11+3): time()-t;
0.001
> t:=time(): estpseudopremier(10^11+3,100): time()-t;
0.021
> t:=time(): isprime(10^60+7): time()-t;
0.002
> t:=time(): estpseudopremier(10^60+7,100): time()-t;
0.646
Exercice 8. Calcul du pgcd
> gcd_factor:=proc(a,b)
local fa, fb, g, i, j, G,t;
t:=time();
fa:=ifactors(a)[2];
fb:=ifactors(b)[2];
g:=[];
for i from 1 to nops(fa) do
for j from 1 to nops(fb) do
if fa[i][1]=fb[j][1] then
g:=[op(g),[fa[i][1],min(fa[i][2],fb[j][2])]];
fi;
od;
od;
G:=map(x->x[1]^x[2],g);
return mul(G[i],i=1..nops(G));
end:
> a:=rand(10^20)():b:=rand(10^20)():t:=time():gcd_factor(a,b):t
ime()-t;
0.003
> a:=rand(10^30)():b:=rand(10^30)():t:=time():gcd_factor(a,b):t
ime()-t;
2.457
> a:=rand(10^30)():b:=rand(10^30)():t:=time():eucl(a,b):time()t;
0.
avec la factorisation, ca commence a devenir difficile avec 30 chiffres, alors qu’avec
l’algorithme d’Euclide, on a vu à l’exercice 4 qu’on pouvait traiter 1000 chiffres sans
problèmes. Cela montre qu’il faut eviter à tout prix la factorisation quand on programme car
c’est un problème difficile.
Exercice 9. Théorème chinois
(a) Si m et n sont premiers entre eux, il existe u, v telque u*m+v*n=1, donc x=u*x*m+v*x*n
donc x=u*m*b+v*n*a [mod n*m]
> chinois:= proc(a,b,m,n)
local u,v;
if igcdex(m,n,’u’,’v’)<>1 then
return "n et m ne sont pas premier entre eux";
end if;
return (u*b*m+v*n*a) mod m*n;
end proc;
chinois := proc(a, b, m, n)
local u, v;
if igcdex(m, n, ’u’, ’v’) ≠ 1 then return "n et m ne sont pas premier entre eux" end if;
return (u∗b∗m + v∗n∗a ) mod m∗n
end proc
(b) On compare chinois et chrem sur l’example x=3 [11] et x=7 [15]
> chinois(3,7,11,15);
157
> chrem([3,7],[11,15]);
157
(c) Généralisation :
> chinois_gen:=proc(A,M)
#A et M sont des vecteurs et le système à résoudre est donné
par les equations x est congru à A[i] modulo M[i]. Bien sur
les M[i] sont supposés premiers entre eux deux à deux.
local g,i;
g:=[A[1],M[1]];
for i from 2 to nops(A) do
g:=[chinois(g[1],A[i],g[2],M[i]),M[i]*g[2]];
od;
return g[1];
end:
> chrem([1,2,3,4],[11,17,4,89]);
> chinois_gen([1,2,3,4],[11,17,4,89]);
17359
17359
Exercice 10. Ecrire une fonction qui calcule l’indicateur
d’Euler
L’indicateur d’Euler de n est le nombre d’entiers plus petits que n et premiers à n.
> eulerphi:=proc(n)
local i, s;
s:=0;
for i from 0 to n-1 do
if (igcd(i,n)=1) then
s:=s+1;
end if;
end do;
return s;
end proc;
eulerphi := proc(n)
local i, s;
s := 0; for i from 0 to n − 1 do if igcd(i, n ) = 1 then s := s + 1 end if end do; return s
end proc
> eulerphi(24);
8
> eulerphi(124);
60