Lyc´ee Thiers - MPSI-3

publicité
Lycée Thiers
TP PYTHON - 02 - CORRECTION
PARTIE A - Quelques rappels de cours
1. Définir des fonctions
[Qu. 1] Deux fonctions très simples ...
Après avoir saisi dans l’éditeur le code source suivant et l’avoir validé
def perimetreC(r):
print(2*pi*r)
def plusProcheEntier(x):
if x % 1 < 0.5:
return x//1
else:
return x//1+1
On voit apparaître (dans le shell) un message (executing lines 1 to 8 of "tp-2.py") indiquant que
l’interprétation s’est déroulée normalement.
Ensuite, l’instruction
>>> perimetreC(1); plusProcheEntier(-1.3)
déclenche une erreur :
...
NameError: global name ’pi’ is not defined
ce qui est normal puisque la constante pi n’est pas définie par défaut. On rectifie en ajoutant (juste
avant la définition de perimetreC) :
from math import pi
après quoi, les choses se déroulent comme souhaité :
>>> perimetreC(1); plusProcheEntier(-1.3)
6.283185307179586
-1.0
[Qu. 2] Pour obtenir le multiple de p le plus proche de x, il suffit de calculer l’entier le plus proche de
x/p puis de le multiplier par p. Bien entendu, cela n’a aucun sens si p est nul, et ce cas doit donc être
traité à part :
TP PYTHON - 02 - CORRECTION
2
def plusProche(x, p):
if p == 0:
return 0
else:
return plusProcheEntier(x/p) * p
[Qu. 3] Permutation de deux valeurs
1) On essaie le script proposé ...
2) ... et la permutation n’a pas eu lieu ! Cela s’explique par le fait que les objets référencés par a et
b ne sont pas mutables . Signalons que les variables locales x et y (qui sont créées au moment de
l’invocation de la fonction permute), après avoir été initialisées à 1 et 2 respectivement, voient
leurs valeurs effectivement échangées : on peut le constater en ajoutant l’instruction print(x, y)
dans le corps de la fonction (à la suite des trois affectations). Mais, encore une fois, cela reste sans
effet sur a et b. Voilà ce qui se passe (en bleu les variables locales) :
a b
b
Appel
a
b b z=x
x
bb
1 2
1 2
1 2
a
z x
y
y
x=y
a b
1 2
Sortie
a
bb
y=z
1 2
z x
y
a
bb
1 2
z x
y
mercredi 9 octobre 13
3) Une solution : renvoyer le couple inversé, avec la fonction
def permuteMieux(x, y):
return (y, x)
puis exécuter l’instruction (a, b) = permuteMieux(a, b)
2. manipulations avancées sur les listes
[Qu. 4] L’expression [k**2 for k in range(1,11)] calcule la liste des carrés des entiers de 1 à 10.
En s’inspirant de cet exemple, écrire :
1) L’expression suivante calcule la liste des 10 premiers nombres impairs :
>>> [2*k-1 for k in range(1,11)]
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
2) Fonction affichant la liste des n premiers nombres impairs :
def affiche_impairs(n):
print([2*k-1 for k in range(1, n + 1)])
3) Fonction renvoyant la liste des n premiers nombres impairs :
def impairs(n):
return [2*k-1 for k in range(1, n + 1)]
TP PYTHON - 02 - CORRECTION
3
[Qu. 5]
1) Rien à signaler :)
2) Les deux versions demandées :
2.a) Première version, avec une liste en compréhension :
def non_multiples(a, b, d):
return [k for k in range(a, b + 1) if k % d != 0]
2.b) Deuxième version, en supprimant des termes :
def non_multiples(a, b, d):
L = [k for k in range(a, b+1)]
for n in L:
if n % d == 0:
L.remove(n)
return L
PARTIE B - Questions de divisibilité
3. Nombres parfaits
[Qu. 6] Liste croissante des diviseurs stricts d’un entier :
def diviseurs_stricts(n):
L = []
for d in range(1, n // 2 + 1):
if n % d == 0:
L.append(d)
return L
ou, plus simplement :
def diviseurs_stricts(n):
return [d for d in range(1, n // 2 + 1) if n % d == 0]
Attention ! ! Ce qui suit ne donne pas le résultat escompté. Essayer de comprendre pour quelle raison
...
def divs(n):
L = [k for k in range(1, n//2 + 1)]
for k in L:
if n % k != 0:
L.remove(k)
return L
On retiendra que c’est a priori une mauvaise idée de modifier, au fur et à mesure qu’une boucle se
déroule, la liste parcourue pour établir cette boucle.
[Qu. 7] Prédicat indiquant si un entier est parfait :
def parfait(n):
return sum(diviseurs_stricts(n)) == n
La fonction (prédéfinie) sum, lorsqu’elle est appliquée à une liste, renvoie la somme de ses termes.
[Qu. 8] Nombres parfaits inférieurs ou égaux à 104 :
TP PYTHON - 02 - CORRECTION
4
def liste_parfaits(nmax):
for n in range(1, nmax + 1):
if parfait(n):
print(n)
>>> liste_parfaits(10**4)
6
28
496
8128
4. Nombres premiers, parfaits, amicaux
[Qu. 9] Crible d’Eratosthène, permière version :
def crible(n):
# On démarre avec la liste des entiers de 2 à n
primes_list = [k for k in range(2, n + 1)]
i = 0
while i < len(primes_list) - 1:
# L est la liste des multiples stricts du primes_list[i]
L = [k for k in primes_list[i+1:] if k % primes_list[i] == 0]
# on supprime de primes_list[] chaque terme de L
for k in L:
primes_list.remove(k)
i += 1
return primes_list
√
√
[Qu. 10] Si n < P, il existe des entiers a, b > 1 tels que n = ab. L’hypothèse a > n et b j> k n
√
entraînerait ab > n, et c’est absurde. Donc l’un au moins des entiers a ou b est compris entre 2 et n .
[Qu. 11] On voit par récurrence qu’à l’issue de chaque tour de boucle, les entiers cochés sont des
nombres premiers consécutifs. C’est le cas au début, puisque 2 est le plus petit nombre premier. Si
cette propriété est vraie à l’issue d’un certain tour de boucle, alors le plus petit entier non coché est
premier, sans quoi il serait multiple d’un nombre premier plus petit que lui et donc aurait été coché
auparavant. En cochant cet entier et en supprimant ses multiples stricts, on préserve donc la propriété
un cran plus loin.
j√ k
Lorsque tous les nombres premiers inférieurs ou égaux à
n ont été cochés , il devient inutile de
poursuivre car les entiers non cochés qui restent sont tous premiers (et donc la liste des nombres
premiers inférieurs
ou égaux à n est établie). En effet,
j√ k
j √ un
k entier q non coché ne possède aucun diviseur
entre 2 et
n et donc aucun diviseur entre 2 et
q : il est premier d’après le point précédent.
Deuxième version du crible d’Eratosthène :
def crible(n):
primes_list = [k for k in range(2, n + 1)]
i = 0
while primes_list[i] ** 2 <= n:
L = [k for k in primes_list[i+1:] if k % primes_list[i] == 0]
for k in L:
primes_list.remove(k)
i += 1
return primes_list
TP PYTHON - 02 - CORRECTION
5
[Qu. 12] Notons D l’ensemble des diviseurs de n supérieurs ou égaux à 2. Soit p le plus petit élément
de D. Si p n’était pas premier, il possèderait un facteur premier q qui serait donc élément de D et
strictement inférieur à p : absurde !
On en déduit que pour
si n est premier, il suffit de parcourir (dans l’ordre croissant) la liste L
j √savoir
k
des entiers entre 2 et
n : dès qu’un diviseur de n est détecté, on interrompt le parcours et l’on sait
que n < P. Et si aucun diviseur de n ne figure dans L, alors n ∈ P.
On peut gagner un facteur 2 en commençant par tester la parité de n puis (si n est impair) en ne
cherchant plus que des diviseurs impairs. Noter que la présence de l’instruction return dans la
branche “if” d’un test dispense d’utiliser “else” :
def ppfp(n):
if n % 2 == 0:
return 2
d = 3
while d * d <= n:
if n % d == 0:
return d
d += 2
return n
[Qu. 13] Facteurs premiers de n = 5888069 (avec répétition) :
while n > 1:
p = ppfp(n)
print(p)
n //= p
11
17
23
37
37
[Qu. 14] Liste “brute” des facteurs premiers d’un entier n > 1 :
def pre_dfp(n):
L = []
q = n
while q > 1:
p = ppfp(q)
L.append(p)
q = q // p
return L
>>> pre_dfp(5888069)
[11, 17, 23, 37, 37]
>>> pre_dfp(1)
[]
[Qu. 15]
1) Réduction d’une liste en une liste de couples (valeur, nb de termes successifs égaux à cette valeur) :
TP PYTHON - 02 - CORRECTION
6
def reduc_liste(L):
n = len(L)
LC = [] # LC comme Liste de Couples
i = 0
while i < n:
j = i + 1
while j < n and L[j] == L[i]:
j += 1
LC.append([L[i], j - i])
i = j
return LC
2) Décomposition en facteurs premiers, sous forme réduite :
def dfp(n):
return reduc_liste(pre_dfp(n))
[Qu. 16] Liste des produits des termes d’une liste par les pk pour 0 6 k 6 n :
def gen_prod(L, p, n):
return [a * p**k for k in range(0, n+1) for a in L]
La version ci-dessus présente l’avantage de la concision (noter l’imbrication des deux “for”) mais
chaque puissance de p est calculée séparément ! On peut faire mieux :
def gen_prod(L, p, n):
result = L[:]
q = 1
for k in range(1, n+1):
q *= p
result.extend([a * q for a in L])
return result
Dans cette seconde version, on passe d’une puissance de p déjà calculée à la suivante, en multipliant
par p. Le nombre de multiplications est ainsi considérablement réduit. Noter que l’expression L[:]
renvoie une copie de L (l’expression L.copy() ferait la même chose). Attention de ne pas utiliser la
liste L elle-même ! !
Exemple :
>>> gen_prod([1, 2, 3], 10, 2)
[1, 2, 3, 10, 20, 30, 100, 200, 300]
[Qu. 17] Si la décomposition en facteurs premiers de n est pα1 1 · · · pαr r , alors les diviseurs de n sont les
β
β
p11 · · · pr r avec, pour chaque i ∈ {1, · · · , r} : 0 6 βi 6 αi .
Pour construire la liste de ces diviseurs, on part d’une liste réduite à [1] dont on multiplie l’unique
terme par 1, p1 , p21 , ... pα1 1 ce qui donne une nouvelle liste. On multiplie alors chacun des termes de
cette dernière par 1, p2 , p22 , ... pα2 2 ce qui donne une liste un peu plus longue. Et ainsi de suite ...
def diviseurs(n):
divList = [1]
decompFP = dfp(n)
for k in range(0, len(decompFP)):
L = gen_prod(divList, decompFP[k][0], decompFP[k][1])
divList = L
return sorted(divList)
TP PYTHON - 02 - CORRECTION
7
Dans ce code source, decompFP[k][0] représente le k-ème facteur premier de n et decompFP[k][1]
représente l’exposant auquel il figure dans la décomposition en facteurs premiers de n.
La fonction (prédéfinie) sorted appliquée à une liste L renvoie la version triée de celle-ci (sans altérer
L, alors que L.sort() effectue un tri “en place” c’est-à-dire en modifiant L ... essayer sur un exemple
pour le constater).
Exemple :
>>> diviseurs(120)
[1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 24, 30, 40, 60, 120]
[Qu. 18] Nombres parfaits inférieurs à 104 (version plus rapide) :
def parfait_bis(n):
return sum(diviseurs(n)) == 2*n
def liste_parfaits_bis(nmax):
for n in range(1, nmax + 1):
if parfait_bis(n):
print(n)
>>> liste_parfaits_bis(10**4)
6
28
496
8128
[Qu. 19] Pour 2 6 n 6 31, on regarde pour n et pour 2n − 1 s’ils sont premiers ou composés :
TP PYTHON - 02 - CORRECTION
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
premier
premier
compose
premier
compose
premier
compose
compose
compose
premier
compose
premier
compose
compose
compose
premier
compose
premier
compose
compose
compose
premier
compose
compose
compose
compose
compose
premier
compose
premier
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
2** 2
2** 3
2** 4
2** 5
2** 6
2** 7
2** 8
2** 9
2**10
2**11
2**12
2**13
2**14
2**15
2**16
2**17
2**18
2**19
2**20
2**21
2**22
2**23
2**24
2**25
2**26
2**27
2**28
2**29
2**30
2**31
-
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
=
3 :
=
7 :
=
15 :
=
31 :
=
63 :
=
127 :
=
255 :
=
511 :
=
1023 :
=
2047 :
=
4095 :
=
8191 :
=
16383 :
=
32767 :
=
65535 :
=
131071 :
=
262143 :
=
524287 :
=
1048575 :
=
2097151 :
=
4194303 :
=
8388607 :
=
16777215 :
=
33554431 :
=
67108863 :
= 134217727 :
= 268435455 :
= 536870911 :
= 1073741823 :
= 2147483647 :
8
premier
premier
compose
premier
compose
premier
compose
compose
compose
compose
compose
premier
compose
compose
compose
premier
compose
premier
compose
compose
compose
compose
compose
compose
compose
compose
compose
compose
compose
premier
En examinant cette table, on peut conjecturer que si 2n − 1 ∈ P, alors n ∈ P.
Preuve de ce résultat, par contraposée : si n < P, on écrit n = ab avec 1 < a, b < n. Alors 2n − 1 = (2a )b − 1
est divisible par 2a − 1; or 3 6 2a − 1 < 2n − 1 et donc 2n − 1 < P.
Attention : la réciproque est fausse ! Le premier contre-exemple apparaît (cf. table ci-dessus) avec
n = 11; en effet : 11 ∈ P mais 211 − 1 = 2047 = 23 × 89.
Les deux contre-exemple suivants sont n = 23 et n = 29 : ces deux entiers sont premiers mais
223 − 1 = 8388607 = 47 × 178481
229 − 1 = 536870911 = 233 × 1103 × 2089
[Qu. 20]
1) Les diviseurs de Ep sont d’une part les diviseurs de 2p−1 c’est-à-dire les 2k pour 0 6 k 6 p − 1 et,
d’autre part les 2k (2p − 1) pour les mêmes k. La somme des diviseurs de Ep est donc :

 p−1 
 p−1   p−1
X  X

X 

 



sp = 
2k  + 
2k (2p − 1) = 2p 
2k  = 2p (2p − 1) = 2Ep

 



k=0
k=0
k=0
Ceci prouve que Ep est un nombre parfait.
2) En choisissant p ∈ {2, 3, 5, 7} , on retrouve les nombres parfaits E2 = 2×(4 − 1) = 6, E3 = 4×(8 − 1) =
28, E5 = 16 × (32 − 1) = 496 et E7 = 65 × (128 − 1) = 8128. Le suivant est :
E13 = 212 213 − 1 = 33 550 336
TP PYTHON - 02 - CORRECTION
9
[Qu. 21] On détecte les couples p, q de nombres amicaux en appliquant la définition : chacun est la
somme des diviseurs stricts de l’autre.
def amicaux(p, q):
dp = diviseurs(p)
dp.remove(p)
dq = diviseurs(q)
dq.remove(q)
return (sum(dp) == q and sum(dq) == p)
Ensuite, on effectue une (double) boucle de recherche :
def cherche_amicaux(nmax):
for p in range(1, nmax + 1):
for q in range(1, p):
if amicaux(p, q):
print(p, q)
>>> cherche_amicaux(300)
284 220
Téléchargement