Répétition d'une action
Une légende de l’Inde ancienne raconte que le jeu d’échecs a été inventé par un
vieux sage, que son roi voulut remercier en lui affirmant qu’il lui accorderait
n’importe quel cadeau en récompense. Le vieux sage demanda qu’on lui fournisse
simplement un peu de riz pour ses vieux jours, et plus précisément un nombre de
grains de riz suffisant pour que l’on puisse en déposer 1 seul sur la première
case du jeu qu’il venait d’inventer, deux sur la suivante, quatre sur la
troisième, et ainsi de suite jusqu’à la 64ième case.
Combien de cases peut-on compléter avec 1 000 000 de grains de riz ? Combien de
grains nous restera-t-il alors ?
À vous de jouer !
Réponse possible à la première question
1
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
32
33
34
35
36
|
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
Enigme de l'échiquier
Combien de cases peut-on compléter avec 1000000 de grains de riz ?
"""
def case_atteinte(grains):
"""
Renvoie le numéro de la case atteinte lorsque l'on dispose
initialement d'un nombre de grains de riz donné
:param grains: (int) nombre de grains de riz en tout
:return case: (int) numéro de la case atteinte
>>> case_atteinte(12)
3
"""
case = 1
grains_case = 1 # nombre de grains de riz à placer dans la case courante
grains_poses = 1 # nombre de grains de riz déjà placés
while grains_poses < grains:
case = case + 1
grains_case = 2 * grains_case
grains_poses = grains_poses + grains_case
case = case - 1 # la case suivante n'a pas pu être complété
return case
if __name__ == '__main__':
import doctest
doctest.testmod(optionflags = doctest.NORMALIZE_WHITESPACE, verbose = True)
grains = 1000000
print(case_atteinte(grains))
|
Réponse possible à la seconde question
1
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
32
33
34
35
36
37
38
39
40
|
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
Enigme de l'échiquier
Combien de cases peut-on compléter avec 1`000`000 de grains de riz ?
"""
def case_atteinte(grains):
"""
Renvoie le numéro de la case atteinte lorsque l'on dispose
initialement d'un nombre de grains de riz donné
:param grains: (int) nombre de grains de riz en tout
:return (case, grains restants): (tuple) le numéro de la case atteinte
et le nombre de grains de riz restants
>>> case_atteinte(12)
(3, 5)
"""
case = 1
grains_case = 1 # nombre de grains de riz à placer dans la case courante
grains_poses = 1 # nombre de grains de riz déjà placés
while grains_poses < grains:
case = case + 1
grains_case = 2 * grains_case
grains_poses = grains_poses + grains_case
case = case - 1
grains_restants = grains - grains_poses + grains_case # on récupère le riz \
# sur la case partiellement complétée
return case, grains_restants # c'est un uplet, appelé tuple en Python
# Ceci est équivalent d'écrire: return (case, grains_restants)
if __name__ == '__main__':
import doctest
doctest.testmod(optionflags = doctest.NORMALIZE_WHITESPACE, verbose = True)
grains = 1000000
print(case_atteinte(grains))
|
Version avec un affichage si vous le souhaitez
1
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
32
33
34
35
36
37
38
39
40
41
42
|
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
Enigme de l'échiquier
Combien de cases peut-on compléter avec 1`000`000 de grains de riz ?
"""
def case_atteinte(grains):
"""
Renvoie le numéro de la case atteinte lorsque l'on dispose
initialement d'un nombre de grains de riz donné
:param grains: (int) nombre de grains de riz en tout
:return (case, grains restants): (tuple) le numéro de la case atteinte
et le nombre de grains de riz restants
:effet de bord: affiche un message faisant un bilan de la situation
>>> case_atteinte(12)
Avec 12 grains de riz, on peut compléter complétement 3 cases. \
Il nous restera 5 grains de riz.
(3, 5)
"""
case, grains_case, grains_poses = 1, 1, 1 # on peut utiliser aussi les tuples
# pour des affectations multiples
while grains_poses < grains:
case = case + 1
grains_case = 2 * grains_case
grains_poses = grains_poses + grains_case
case = case - 1
grains_restants = grains - grains_poses + grains_case
print("Avec {:,} grains de riz, on peut compléter complétement {} cases. \
Il nous restera {:,} grains de riz.".format(grains, case, grains_restants))
return case, grains_restants
if __name__ == '__main__':
import doctest
doctest.testmod(optionflags = doctest.NORMALIZE_WHITESPACE, verbose = True)
grains = 1000000
print(case_atteinte(grains))
|
Revenons au problème vu en introduction avec les bidons:
On dispose de deux bidons de contenance 5 galons et 3 galons et
d’autant d’eau que l’on souhaite. On cherche à obtenir un bidon contenant 4
galons d’eau. Décrire les actions à réaliser.
Pour rappel, on a déjà écrit les fonctions remplir()
(pour remplir le bidon
1), vider()
(pour vider le bidon 2) et transvaser()
(pour transvaser du
bidon 1 vers le bidon 2) qui peuvent être utilisée.
Plus de détails ?
1
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
32
33
34
35
36
37
38
39
40
41
42
43
44
|
#!/usr/bin/python3
# -*- coding: utf-8 -*-
def vider():
"""
Vider le contenu du bidon 2
:param: (None)
:return: (int) le nouveau volume d'eau dans le bidon 2
"""
print("On vide le bidon 2.") # on affiche l'action réalisée
return 0
def remplir(contenance1 = 5): # on donne une valeur au paramètre contenance1
# par défaut
"""
Remplir le bidon 1
:param: (None)
:return contenance1: (int) le nouveau volume d'eau dans le bidon 1
"""
print("On remplit le bidon 1.") # on affiche l'action réalisée
return contenance1
def transvaser(bidon1, bidon2, contenance2 = 3): # les paramètres ayant une valeur
# par défaut sont toujours placés
# dans les dernières positions
# parmi les paramètres
"""
La quantité d'eau transvasée du bidon 1 vers le bidon 2
:param bidon1: (int) le volume d'eau contenu dans le bidon 1
:param bidon2: (int) le volume d'eau contenu dans le bidon 2
:param contenance2: (int) la contenance maximale du bidon 2
:return transvase: (int) la quantité d'eau transvasée du bidon 1 vers le bidon 2
"""
volume_manquant2 = contenance2 - bidon2
if bidon1 > volume_manquant2:
transvase = volume_manquant2
else:
transvase = bidon1
print("On transvase du bidon 1 vers le bidon 2 un volume de {} L
d'eau.".format(transvase)) # on affiche l'action réalisée
return transvase
|
Réaliser la dernière fonction qui lie celles-ci pour finaliser le script qui
résout le problème.
Script finalisé
1
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
Enigme des bidons
Si cela est possible, combien et quelles manipulations sont à réaliser
pour obtenir le volume désiré ?
"""
def vider():
"""
Vider le contenu du bidon 2
:param: (None)
:return: (int) le nouveau volume d'eau dans le bidon 2
>>> bidon2 = vider()
On vide le bidon 2.
>>> bidon2
0
"""
print("On vide le bidon 2.") # on affiche l'action réalisée
return 0
def remplir(contenance1 = 5):
"""
Remplir le bidon 1
:param: (None)
:return contenance1: (int) le nouveau volume d'eau dans le bidon 1
>>> bidon1 = remplir()
On remplit le bidon 1.
>>> bidon1
5
"""
print("On remplit le bidon 1.") # on affiche l'action réalisée
return contenance1
def transvaser(bidon1, bidon2, contenance2 = 3):
"""
La quantité d'eau transvasée du bidon 1 vers le bidon 2
:param bidon1: (int) le volume d'eau contenu dans le bidon 1
:param bidon2: (int) le volume d'eau contenu dans le bidon 2
:param contenance2: (int) la contenance maximale du bidon 2
:return transvase: (int) la quantité d'eau transvasée du bidon 1 vers le bidon 2
>>> transvase = transvaser(5, 1, 4)
On transvase du bidon 1 vers le bidon 2 un volume de 3 L d'eau.
>>> transvase
3
>>> transvase = transvaser(4, 2)
On transvase du bidon 1 vers le bidon 2 un volume de 1 L d'eau.
>>> transvase
1
"""
volume_manquant2 = contenance2 - bidon2
if bidon1 > volume_manquant2:
transvase = volume_manquant2
else:
transvase = bidon1
print("On transvase du bidon 1 vers le bidon 2 un volume de {} L d'eau.".format(transvase)) # on affiche l'action réalisée
return transvase
import time
def actions_a_realiser(volume_desire, contenance1 = 5, contenance2 = 3):
"""
Affiche les actions et les décompte le nombre d'actions à réaliser
afin d'obtenir le volume souhaité dans le bidon 1.
:param volume_desire: (int) le volume d'eau que l'on cherche à obtenir
:param contenance1: (int) la contenance maximale du bidon 1
:param contenance2: (int) la contenance maximale du bidon 2
:return: (int) le nombre d'actions réalisées
>>> nb_actions = actions_a_realiser(4, 5, 3)
Les bidons sont vides.
On remplit le bidon 1.
On transvase du bidon 1 vers le bidon 2 un volume de 3 L d'eau.
On vide le bidon 2.
On transvase du bidon 1 vers le bidon 2 un volume de 2 L d'eau.
On remplit le bidon 1.
On transvase du bidon 1 vers le bidon 2 un volume de 1 L d'eau.
Terminé ! Le bidon 1 contient le volume désiré.
>>> nb_actions
6
"""
bidon1, bidon2 = 0, 0
print("Les bidons sont vides.")
action = 0
while bidon1 != volume_desire:
if bidon1 == 0: # le bidon 1 est vide
bidon1 = remplir(contenance1)
action = action + 1
transvase = transvaser(bidon1, bidon2, contenance2)
bidon1, bidon2 = bidon1 - transvase, bidon2 + transvase
action = action + 1
if bidon2 == contenance2 and bidon1 != volume_desire: # le bidon 2 est plein
bidon2 = vider()
action = action + 1
time.sleep(3) # pour se laisser le temps de lire les actions réalisées, donc cette ligne est optionnelle.
print("Terminé ! Le bidon 1 contient le volume désiré.")
return action
if __name__ == '__main__':
import doctest
doctest.testmod(optionflags = doctest.NORMALIZE_WHITESPACE, verbose = True)
actions_a_realiser(4)
|
Remarque: vous pouvez vérifier que notre script permet de résoudre le problème
avec d’autres contenances pour les deux bidons.
Essayer avec contenance1 = 7
, contenance = 5
, volume_desire = 3
par
exemple ou même pourquoi pas avec des flottants.
Attention, toutefois avec certaines contenances, le problème n’a pas de solution
(par exemple avec contenance1 = 4
, contenance2 = 2
et volume_desire = 1
).
Dans ce cas, le programme ne s’arrête pas car la condition d’arrêt dans le
while
ne sera jamais vérifiée. Donc, c’est à utiliser avec précaution.
La boucle TantQue permet de répéter l’action jusqu’à ce que la condition soit
vérifiée.
Il est possible de sortir prématurément d’une telle boucle avec le mot clé break
.
Exemple: sortir prématurément d’une boucle TantQue
Vous disposez d’un fichier source
que vous souhaitez copier intégralement vers
un autre fichier destination
. Voici comment on peut procéder en Python:
1
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
#!/usr/bin/python3
# -*- coding: utf-8 -*-
#première possibilité:
def copier_fichier1(source, destination):
"""
Créée une copie du fichier source nommée destination.
:param source: (file) le fichier de départ
:param destination: (file) le fichier d'arrivée
:return: (None)
:effet de bord: modifie le contenu du fichier destination
"""
depart = open(source, 'r') # ouvre en lecture le fichier source
arrivee = open(destination, 'w') # ouvre en écriture le fichier destination
# en écrasant son contenu au fur et à mesure
while True: # permet de répéter indéfiniment les actions suivantes
txt = depart.read(50) # lit les 50 caractères suivants l'endroit
# où on est arrivé dans la lecture de depart
# txt forme une chaîne de caractères appelée string
if txt == "": # teste si la chaîne lue est vide, cela arrive si l'on est
# à la fin du fichier.
break # permet de sortir immédiatement de la boucle while
arrivee.write(txt) # écrit la chaîne lu dans le fichier arrivee
depart.close()
arrivee.close()
#seconde possibilité:
def copier_fichier2(source, destination):
"""
Créée une copie du fichier source nommée destination.
:param source: (file) le fichier de départ
:param destination: (file) le fichier d'arrivée
:return: (None)
:effet de bord: modifie le contenu du fichier destination
"""
with open(source, 'r') as depart, open(destination, 'w') as arrivee:
while True: # permet de répéter indéfiniment les actions suivantes
txt = depart.read(50) # lit les 50 caractères suivants l'endroit
# où on est arrivé dans la lecture de depart
# txt forme une chaîne de caractères appelée string
if txt == "": # teste si la chaîne lue est vide, cela arrive si l'on
# est à la fin du fichier.
break # permet de sortir immédiatement de la boucle while
arrivee.write(txt) # écrit la chaîne lu dans le fichier arrivee
|
Vous pouvez le tester. Il suffira d’ajouter dans le script ou dans la console
(après l’exécution du script):
1
2
|
copier_fichier1("nom_du_fichier_a_copier", "nom_de_la_copie") # notez la présence
# des guillemets lors de l'appel aux noms des fichiers
|
ou
1
2
|
copier_fichier2("nom_du_fichier_a_copier", "nom_de_la_copie") # notez la présence
# des guillemets lors de l'appel aux noms des fichiers
|
Dans le langage Python, encore de nouveaux types de données: string, file
Langage courant |
Python |
Fonctions liées |
Chaîne de caractères |
str (string) |
str() |
Fichier |
file |
open() , close() |
On approfondira leurs usages dans de prochains chapitres.