Vous êtes sur la page 1sur 6

D.S. 1 I.P.

T MP : solutions
Exercice 1. a) Il faut faire attention au cas particulier des points de l’axe (Ox), à part cela
il suffit de lire le schéma :
def Cantor(x,y):
if x==0 and y==0:
return 0
elif y==0: # le cas part. des points de l’axe Ox
return Cantor(0,x-1)+1
else:
return Cantor(x+1,y-1)+1
Remarque mathématique : Comment trouver une formule explicite pour la fonction
Cantor ?
Idée : pour chaque couple (x, y) ∈ N2 , pour trouver son numéro, on considère d’abord sur
quelle diagonale de la forme x + y =cste il est, i.e. on considère la somme x + y.
Alors on sait qu’avant (x, y), dans notre numérotation, il y a tous les éléments des diagonales
précédentes.
Précisément si on note Dk = {(p, q) ∈ N2 , p + q = k}, on voit que Card(Dk ) = k + 1.
Donc avant (x, y) ∈ DN avec N = x + y, il a 1 + 2 + ⋅ ⋅ ⋅ + (x + y) éléments des diagonales
D0 , . . . , DN −1 , auxquels il faut ajouter les éléments avant (x, y) sur DN : il y en y (en
regardant la numérotation par ordonnées croissantes).
(x + y)(x + y + 1)
Par conséquent : Cantor ∶ N2 → N, (x, y) ↦ y + (1 + 2 + ⋯ + (x + y)) = y + .
2
b) Voici une ≪ mauvaise ≫ fonction récursive, écrite ≪ comme on pense ≫ avec une récursion
dans le if ce qui fait vraiment beaucoup d’appels récursifs ! Moralité : cette fonction peine
à trouver la réponse à partir de n=20 (hum) !
def CantorInverse(n):
if n==0:
return (0,0)
elif CantorInverse(n-1)[0]!=0:
return CantorInverse(n-1)[0]-1,CantorInverse(n-1)[1]+1
else : # cas où l’antécédent de n-1 est sur l’axe Oy
return CantorInverse(n-1)[1]+1,0
Comme indiqué par l’énoncé, on doit bien sûr diminuer le nombre d’appels récursifs en
n’appelant la fonction CantorInverse qu’une seule fois et en stockant le résultat, comme
suit :
def CantorInverse(n):
if n==0:
return (0,0)
else :
precedent=CantorInverse(n-1)
if precedent[0]!=0:
return precedent[0]-1,precedent[1]+1
else :
return precedent[1]+1,0
c) (i) La suite (Tk ) est définie par T0 = 0, Tk+1 = Tk + (k + 1) (nombres triangulaires).
(ii) def Tk(n):
k=0
T=0
while T<n:
k=k+1
T=T+k
return n==T

1
Exercice 2. a) L’indication permet de penser à un algorithme récursif. Dans les deux cas, il y a
quelque chose qui diminue : ou bien n est remplacé par n − L[−1] ou bien la liste L diminue. Quand
L est de longueur 1, il y a une ou zéro décomposition possible de n suivant que l’unique entrée de
L divise n.
def compteavecListe(n,L):
if len(L)==1:
if n%L[0]==0:
return 1
else:
return 0
elif n<0:
return 0
elif n==0:
return 1
else :
return compteavecListe(n-L[-1],L)+compteavecListe(n,L[:len(L)-1])

Ensuite il suffit de faire :

def comptepiece(n):
return compteavecliste(n,[1,2,5,10])

b) On reprend l’idée donnée au a). Cependant c’est un peu plus compliqué à mettre en oeuvre.
L’idée est toujours d’utiliser la récursion en faisant retourner à decompose(n,L) la concaténation
de decompose(n,L[0:-1]) avec ... le résultat de decompose(n-L[-1],L) dans lequel il faut néanmoins
rajouter la valeur L[-1] à chaque sous liste....
Pour mieux comprendre prenons un exemple, celui de decompose(5,[1,2]) : on peut l’écrire
comme decompose(5,[1])=[[1,1,1,1,1]] concaténé avec [[1,1,1,2],[1,2,2]] qui est la valeur
de retour de decompose(5-2,[1,2])=decompose(3,[1,2])=[[1,1,1],[1,2]] dans laquelle on
rajoute 2 dans chaque sous-liste.
On écrit donc une fonction qui complète les sous-listes avec un argument : cette fonction ne
modifie pas la liste en entrée
def completesousliste(a,L):
M=[]
for i in range(len(L)):
M.append(L[i]+[a])# concatène la sous-liste
return M

Puis on écrit la fonction récursive attendue, le traitement des cas de base étant un peu plus délicat :

def decompose(n,L):
if n<0 or L==[]:
return []
elif n==0:
return [[]]
else :
a=L[-1]
return decompose(n,L[0:-1])+completesousliste(a,decompose(n-a,L))

2
Problème : suites de Farey
0) Quelques outils pour la suite :
a) Créer une fonction chaine qui prend en argument une liste [a,b] et renvoie la chaı̂ne de
caractère "a/b".
def chaine(r):
return str(r[0])+"/"+str(r[1])
b) En déduire une fonction chaineListe qui reçoit une liste de rationnels (toujours écrits
chacun sous la forme [a,b]), ne renvoie rien mais affiche la chaı̂ne de caractères formée de
ces rationnels écrits sous la forme "a/b", séparés par des virgules.
En récursif :
def chaineListe(L):
if len(L)==1:
return chaine(L[0])
else :
return chaine(L[0])+","+chaineListe(L[1:])
En impératif :
def chaineListeI(L):
if len(L)==0:
return None
else :
S=chaine(L[0]) # on ne doit pas commencer par une virgule..
for valeur in L[1:]:
S=S+","+chaine(valeur)
print(S)

c) Ecrire une fonction récursive pgcd qui reçoit deux entiers a et b et renvoie le pgcd de a et
b.
def pgcd(a,b):
if b==0:
return a
else:
return pgcd(b,a%b)
d) En déduire une fonction simplifieListe qui reçoit une liste L de couples [a,b] codant des
rationnels et qui renvoie une autre liste M obtenue à partir de L en enlevant les couples tels
que pgcd(a,b)≠ 1.
def simplifieliste(L):
M=[]
for valeur in L:
if pgcd(valeur[0],valeur[1])==1:
M.append(valeur)
return M
e) Créer une fonction elimine qui reçoit une liste L de rationnels irréductibles et un entier
positif n et renvoie une liste M obtenue à partir de L en enlevant les rationnels dont le
dénominateur est strictement supérieur à n.
def elimine(L,n):
M=[]
for i in range(len(L)):
if L[i][1]<=n:
M.append(L[i])
return M

3
1) Tri par insertion : question de cours
def triInsertion(T):
for i in range(1,len(T)):
insere(i,T)
# reste alors à définir la fonction insère
def insere(i,T):
"""insère T[i] dans le tableau T[0:i] déjà trié"""
j=i
while j>0 and T[j-1]>T[j]:
T[j],T[j-1]=T[j-1],T[j]
j=j-1

2) La méthode brutale pour obtenir la suite de Farey


a) Ecrire une fonction listebrute qui reçoit un entier n et renvoie la liste de tous les couples
[i,j] avec 0 ≤ i ≤ j ≤ n.
def listebrute(n):
L=[]
for j in range(n+1):
for i in range(j):
L.append([i,j])
L.append([1,1])
return L
b) A l’aide de toutes les fonctions précédentes déduire de la fonction listebrute une fonction
Fareybrut qui renvoie la suite de Farey F.
Idée : avec listebrute on a tous les couples [i,j] codant les fractions i < j avec 0 ≤ i ≤
j ≤ n. Par exemple :
>>> listebrute(5)
[[0, 1], [0, 2], [1, 2], [0, 3], [1, 3], [2, 3], [0, 4], [1, 4], [2, 4], [3, 4], [
0, 5], [1, 5], [2, 5], [3, 5], [4, 5]]
Mais certaines écritures (comme [2,4] dans l’exemple) ne sont pas irréductibles. On peut
simplement les éliminer ici, car si une écriture est réductible, la fraction irréductible cor-
respondant est déjà fabriquée avant par listebrute par exemple pour [2,4] la fraction
irréductible [1,2] est fabriquée avant.
Donc on utilise la fonction simplifieliste du § 0.
Reste à trier ces valeurs pour les mettre dans l’ordre. Le seul problème est que le tri doit
comprendre quel est l’ordre entre les données [a, b] du tableau. Une façon de faire est de
modifier le script du tri par insertion, comme suit :
def TriInsertionBis(T):
for i in range(1,len(T)):
insereBis(i,T)
def insereBis(i,T):
j=i
while j>0 and T[j-1][0]/T[j-1][1]>T[j][0]/T[j][1]: # c’est là qu’on a changé !
T[j],T[j-1]=T[j-1],T[j]
j=j-1
Avec ce tri, et les fonctions précédentes, on obtient :
def Fareybrut(n):
L=listebrute(n)
L=simplifieListe(L)
TriInsertionBis(L)
return L

4
3) Un peu de mathématique pour trouver un meilleur algorithme !
Soient a1 /b1 , a2 /b2 et a3 /b3 trois termes consécutifs de Fn . Par la prop. 3.1, on a :

a2 b1 − a1 b2 = 1,
a3 b2 − a2 b3 = 1.

(M1) Avec l’égalité a2 b1 −a1 b2 = a3 b2 −a2 b3 , on obtient a2 (b1 +b3 ) = b2 (a1 +a3 ) et donc l’égalité
a2 a1 + a3
voulue = .
b2 b1 + b3
(M2) plus longue mais instructive pour la dernière partie bonus :
Si on voit ces deux équations comme un système d’inconnues a2 et b2 , on le réécrit :


⎪b1 a2 − a1 b2 = 1,


⎪−b a + a3 b2 = 1
⎩ 3 2
b −a1
Le déterminant de ce système vaut ∣ 1 ∣ = b1 a3 − a1 b3 = ∆ > 0 et donc les solutions sont :
−b3 a3

1 −a1 b 1
∣ ∣ ∣ 1 ∣
1 a3 a1 + a3 −b3 1 b1 + b3
a2 = = b2 = = .
∆ ∆ ∆ ∆
a2 a1 + a3
Finalement, on obtient bien = .
b2 b1 + b3
N.B. Avec la méthode 2, on voit qu’on aura, mieux, l’égalité a2 = a1 + a3 et b2 = b1 + b3 ssi ∆ = 1.
4) Algorithme plus efficace
A l’aide de la propriété 3.2, on peut construire F2 à partir de F1 = [ 10 , 11 ] : il suffit de rajouter
0+1 1
= au milieu.
1+1 2
a) Obtenir ainsi, à la main, sur votre copie, les suites F2 , F3 , F4 , F5 .
L’énoncé explique déjà que F2 = [ 01 , 12 , 11 ].
En prenant deux fractions médiantes : F3 = [ 01 , 31 , 12 , 23 , 11 ].
En prenant encore cette fois les quatre fractions médiantes on arrive à la liste : L4 =
[ 01 , 14 , 13 , 25 , 12 , 35 , 23 , 34 , 11 ]. Mais cette liste n’est pas F4 car on a des fractions de dénominateur 5.
On les enlève pour obtenir : F4 = [ 01 , 14 , 13 , 12 , 32 , 34 , 11 ].
Enfin pour F5 : avec les fractions médiantes de F4 on arrive à la liste L5 = [ 01 , 51 , 14 , 27 , 13 , 25 , 12 , 35 , 23 , 57 , 34 , 54 , 11 ].
On enlève encore les fractions de dénominateur strictement plus grand que 5, ce qui donne :
F5 = [ 01 , 15 , 14 , 13 , 25 , 12 , 35 , 23 , 34 , 45 , 11 ].
b) Ecrire une fonction ajouteMediante qui reçoit une liste L dont les entrées sont des couples
de la forme [a,b] et modifie cette liste L pour ajouter entre chaque entrées [a,b] et [a’,b’]
successives la médiante [a+a’,b+b’].
Une version impérative qui ne respecte pas complètement la consigne de l’énoncé :
car L n’est pas modifiée et on retourne une autre liste.
def ajouteMedianteI(L):
M=[]
for i in range(len(L)-1):
M.append(L[i])
M.append([L[i][0]+L[i+1][0],L[i][1]+L[i+1][1]])
M.append(L[-1])
return M
Une version récursive (qui en Python demande vite d’augmenter la profondeur
de récursion)
def ajouteMediante(L):
if len(L)<=1:

5
return L
else :
x=L.pop() # on pope la dernière
y=L[-1] # nlle derniere
z=[x[0]+y[0],x[1]+y[1]]
ajouteMediante(L)# appel sur la liste
# avec une entrée de moins
# après l’appel récursif :
L.append(z)
L.append(x)
# L est modifiée, en plus on met un return si on veut
return L # facultatif, non demandé dans l’énoncé, utile ci-dessous

c) Déduire de ce qui précède une fonction Farey qui reçoit un entier n au moins égal à 2, (on
ne traitera pas les cas n ≤ 2) et renvoie la liste Fn toujours sous la forme d’une liste de
couples.
def Farey(n):
F=[[0,1],[1,1]]
for i in range(n-1):
ajouteMediante(F)
return elimine(F,n)

Si on préfère une version récursive


def FareyR(n):
if n==1:
return [[0,1],[1,1]]
else :
return elimine(ajouteMediante(FareyR(n-1)),n)
# N.B. on utilise le return de la fonction ajouteMediante ci-dessus.
5) Un petit dessin pour finir
pl.clf()
F=pl.gca()
Fn=Farey(10) # 10 par exemple.
for (a,b) in Fn:
cercle(a/b,1/(2*b**2),1/(2*b**2))
pl.axis(’scaled’)
pl.show()
N.B. Je n’ai pas écrit de fonctions parce que j’ai du mal avec le F=pl.gca() dans une fonction...

Vous aimerez peut-être aussi