TP — Arithmétique¶
Travaux pratiques · MPSI (tronc commun) · 2021-2022 · Python
Division euclidienne¶
On rappelle que si \(a,b \in \N\) avec \(b \neq 0\), il existe un unique couple (quotient, reste) \((q,r) \in \N^2\) tel que \(a = bq + r\) et \(0 \le r < b\). On appelle cette opération une division euclidienne.
Un algorithme pour calculer ce couple consiste à remarquer que \(a = b \times 0 + a = b \times 1 + (a - b) = b \times 2 + (a - 2b) = \dots\)
Question 1
def division_euclidienne(a,b):
'''Renvoie le couple (q,r)'''
Corrigé
def division_euclidienne(a,b):
'''Renvoie le couple (q,r)'''
q = 0
r = a
while r >= b:
q = q + 1
r = r - b
return q, r
Tests :
from civ import *
init()
test(division_euclidienne, 10, 3, (3, 1))
test(division_euclidienne, 3, 5, (0, 3))
test(division_euclidienne, 153, 7, (21, 6))
test(division_euclidienne, 10, 1, (10, 0))
valide()
PGCD et algorithme d'Euclide¶
On rappelle que si \((a,b) \in \N^2 \setminus \{(0,0)\}\), alors le PGCD de \(a\) et de \(b\), noté \(a \wedge b\) est le plus grand diviseur commun à \(a\) et à \(b\).
On va donner ici deux versions de l'algorithme de calcul du PGCD dû à Euclide.
La première, appelée méthode des soustractions successives, revient à remarquer :
- que \(a \wedge 0 = 0 \wedge a = a\)
- que si \(a \ge b\) alors \(a \wedge b = (a - b) \wedge b\)
On en déduit alors une manière récursive de calculer un PGCD :
Pour simplifier, on pourra remarquer que \(a \wedge b = b \wedge a\) et donc qu'on peut remettre les nombres dans l'ordre qu'on souhaite.
Question 2
def pgcd_soustraction(a, b):
'''Implémenter ce calcul du pgcd'''
Corrigé
def pgcd_soustraction(a, b):
'''Implémenter ce calcul du pgcd'''
if b == 0:
return a
if a < b:
return pgcd_soustraction(b, a)
return pgcd_soustraction(a-b, b)
Tests :
from civ import *
init()
test(pgcd_soustraction, 24, 40, 8)
test(pgcd_soustraction, 24, 0, 24)
test(pgcd_soustraction, 40, 24, 8)
valide()
Cette méthode peut être raffinée à l'aide de la division euclidienne : si \(a = bq + r\) alors \(a \wedge b = b \wedge r\).
Question 3
En déduire l'algorithme usuel d'Euclide utilisant la division euclidienne directement plutôt que des soustractions (note : la division utilise les soustractions, elles n'ont pas disparu).
def pgcd(a,b):
'''Algorithme d'Euclide du PGCD'''
Corrigé
def pgcd(a,b):
'''Algorithme d'Euclide du PGCD'''
if b == 0:
return a
q, r = division_euclidienne(a, b)
return pgcd(b, r)
Tests :
from civ import *
init()
test(pgcd, 24, 40, 8)
test(pgcd, 24, 0, 24)
test(pgcd, 40, 24, 8)
valide()
Test de primalité naïf¶
Question 4
Écrire une fonction est_premier qui vérifie si un nombre n est premier
en testant sa divisibilité par des entiers \(1 < d < n\).
def est_premier(n):
Corrigé
def est_premier(n):
for d in range(2,n):
if n % d == 0:
return False
return n != 1
Tests :
from civ import *
init()
for i in [1,4,6,10]:
test(est_premier, i, False)
for i in [2,3,5,11]:
test(est_premier, i, True)
valide()
Crible d'Ératosthène¶
On cherche ici à déterminer tous les nombres premiers \(\le N\) où \(N\) est une limite qu'on se fixe à l'avance.
L'idée du crible d'Ératosthène consiste à aligner tous les nombres de 2 à \(N\), puis d'effectuer le processus suivant :
- Déterminer le plus petit entier \(n\) non barré et non encore étudié.
- Barrer tous les multiples de \(n\) qui sont \(> n\).
- S'il reste des nombres non barrés \(> n\) retourner à l'étape 1.
- Les nombres non barrés sont premiers.
Par exemple pour \(N = 9\) :
- initialisation : 2 3 4 5 6 7 8 9
- étape 1 – \(n = 2\) on barre donc 4, 6, 8 : 2 3
456789 - étape 2 – \(n = 3\) on barre donc 6, 9 (mais 6 est déjà barré) : 2 3
456789 - étape 3 – \(n = 5\) mais il n'y a pas de multiples : 2 3
456789 - étape 4 – \(n = 7\) mais il n'y a pas de multiples : 2 3
456789 - étape 5 – il n'y a plus de nombres non barrés, on a donc trouvé les premiers \(\le 9\) : 2 3 5 7
Pour réaliser cet algorithme on va utiliser un tableau premier de booléens
tel que premier[i] soit à True ssi i est premier. Pour cela, il va
falloir créer un tableau de \(N+1\) valeurs contenant initialement True pour
tous les nombres sauf 0 et 1 qui sont les deux seuls qu'on peut barrer a
priori.
On rappelle que pour créer un tableau contenant 10 fois la valeur True, il
suffit d'écrire [ True ] * 10.
Question 5
def initialise_crible(N):
'''Renvoie un tableau de N+1 corespondant à l'état initial du crible.'''
Corrigé
def initialise_crible(N):
'''Renvoie un tableau de N+1 corespondant à l'état initial du crible.'''
t = [ True ] * (N+1)
t[0] = t[1] = False
return t
Tests :
from civ import *
init()
test(initialise_crible, 3, [False, False, True, True])
test(initialise_crible, 5, [False, False, True, True, True, True])
valide()
Question 6
Écrire une fonction barre_multiple qui prend en entrée un tableau t de
booléens donné par la fonction précédente et un entier n et passe à
False tous les indices multiples stricts (i.e. \(\neq n\)) de n. On
évitera de faire des multiplications à répétition car on peut se contenter
d'additions. On rappelle que \(N+1\) = len(t) ici.
def barre_multiple(t,n):
Corrigé
def barre_multiple(t,n):
m = 2*n
while m < len(t):
t[m] = False
m = m + n
Tests :
from civ import *
init()
t = [False, False, True, True, True, True, True, True, True]
def test(t, n):
return t == [False, False, True, True, False, True, False, True, False]
test_condition(barre_multiple, test, t, 2)
t = [False, False, True, True, True, True, True, True, True]
def test(t, n):
return t == [False, False, True, True, True, True, False, True, True]
test_condition(barre_multiple, test, t, 3)
valide()
Question 7
Écrire une fonction crible qui prend en entrée N et qui renvoie le
tableau de booléens correspondant à l'application de l'algorithme.
def crible(N):
Corrigé
def crible(N):
v = initialise_crible(N)
for n in range(N):
if v[n]:
barre_multiple(v,n)
return v
Tests :
from civ import *
init()
test(crible, 10, [False, False, True, True, False, True, False, True, False, False, False])
test(crible, 15, [False, False, True, True, False, True, False, True, False, False, False,
True, False, True, False, False])
valide()
Question 8
Écrire une fonction premiers qui utilise crible pour déterminer la
liste des nombres premiers \(\le N\). On pourra utiliser l.append(x) pour
rajouter l'élément x à l.
def premiers(N):
Corrigé
def premiers(N):
p = []
l = crible(N)
for i in range(N+1):
if l[i]:
p.append(i)
return p
Tests :
from civ import *
init()
test(premiers, 5, [2,3,5])
test(premiers, 100, [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])
valide()
Conjecture de Goldbach¶
En 1742, Christian Goldbach énonce cette conjecture célèbre :
Tout nombre entier pair \(\ge 3\) peut s'écrire comme la somme de deux nombres premiers.
Question 9
Écrire une fonction decompose qui prend en entrée un entier n pair et
\(\ge 3\) et renvoie un couple de nombres premiers \((p_1,p_2)\) tel que
\(n = p_1 + p_2\) et \(p_1 \le p_2\).
def decompose(n):
Corrigé
def decompose(n):
for i in range(2,n//2+1):
if est_premier(i) and est_premier(n-i):
return (i,n-i)
Tests :
from civ import *
init()
test(decompose, 6, (3, 3))
test(decompose, 10000, (59, 9941))
valide()
Il vous reste du temps ?¶
C'est le bon moment d'utiliser ce que vous avez appris sur le site https://projecteuler.net/ !