Introduction à la Programmation Python
1. Manipulation des objets élémentaires
1.1. Entiers
((567*34 -(222 + 103*(890-888))*5*3**(3127-5**5))**4 + 17471268) // 1454586 ((567*34 -(222 + 103*(890-888))*5*3**(3127-5**5))**4 + 17471268) % 1454586
1.2. Opération Booléennes
Pour 1, 2, et 3 on obtient dans l’ordre : True, False et True. On remarque
que l’expression 1 donne le même résultat que l’expression 3. Le and est donc
prioritaire sur le or.
Pour la 4, on obtient True puis False, le not est donc prioritaire sur le
and et le or.
1.3. Flottants
(530.4709375 * 42) ** (1/6) / 3.5
150 // 12 donne un résultat entier, le quotient de la division euclienne,
alors que 150 / 12 calcule le résultat de la division flottante.
2 ** 0.5 (2 ** 0.5) ** 2
On remarque que l’utilisation des flottant implique une approximation des résultats mathématiques attendus, et que cette approximation induit une erreur sur le calcul de \(\sqrt{2}^{2}\).
1.4. Néant
On remarque que la console n’affiche aucun résultat. En effet, lorsque le
résultat est None, cela signifie qu’il n’y a pas de résultat.
1.5. Comparaisons
On remarque que les comparaisons sur les flottants ne correspondent pas aux attendus mathématiques. C’est du à la précision finie que nous permet d’avoir un flottant.
On peut observer aussi que B == True renvoie toujours B.
Il est donc inutile d’écrire B == True, mieux vaut écrire B directement.
De même B == False peut être simplifié en not B.
2. Nommage
2.1. Variables
2.2. Fonctions, Appels
2.3. Fonction, Définition
def racine(x,n) : return x ** (1/n) def ordonnees(a,b,c,d) : return a < b and b < c and c < d def quasiegal(x,y,e) : return -e < x-y and x-y < e
2.4. Fonctions, Environnement
Lien vers une simulation Python Tutor
Il y a au plus 2 environnements locaux simultanés, en plus de l’environnement global.
3. Flot d’exécution
3.1. Appels
1, 4, 7, 10, 1, 2, 7, 8, 7, 8, 4, 5, 7, 8, 5, 2, 10
3.2. Conditionnelles
Lien vers une simulation Python Tutor
f(10,20) : 1, 2, 3, 4, 11, f(5,6) : 1, 2, 3, 5, 6, 8, 11, f(3,4) : 1, 2, 3, 4, 11, f(7,10) : 1, 2, 3, 5, 10, 11, f(-3,2) : 1, 2, 3, 5, 6, 7, 8, 11,
3.3. Bissextile
def bissextile(annee) : if annee % 400 == 0 : return True elif annee % 4 == 0 and annee % 100 != 0 : return True else : return False
On peu aussi écrire…
def bissextile(annee) : return annee % 400 == 0 or (annee % 4 == 0 and annee % 100 != 0)
3.4. Boucles
f(12) : 1, 2, 3, 4, 5, 6, 7, 4, 5, 7, ~ x4 4, 5, 6, 7, 4, 5, 7, ~ x4 4, 5, 6, 7, 4, 5, 7, 4, 8, 9, 10, 11, ~ x3 9, 12, f(21) : 1, 2, 3, 4, 5, 6, 7, 4, 5, 7, ~ x4 4, 5, 6, 7, 4, 5, 7, ~ x4 4, 5, 6, 7, 4, 5, 7, ~ x4 4, 5, 6, 7, 4, 5, 7, ~ x4 4, 5, 6, 7, 4, 8, 9, 10, 11, ~ x5 9, 12, f(0) : 1, 2, 3, 4, 8, 9, 12, f(-5) : 1, 2, 3, 4, 8, 9, 12, f(30) : 1, 2, 3, 4, 5, 6, 7, | ~ x6 4, 5, 7, ~ x4 | 4, 8, 9, 10, 11, ~ x6 9, 12
4. Etude d’un algorithme
4.1. Simulation Papier
- Il y a 5 tours de boucles
Voici les différentes valeurs :
i0 1 2 3 4 5 f1 1 2 6 24 120 - On a
f = i!
4.2. Spécification de \(\cal F\)
L’algorithme \(\symcal{F}\) renvoie \(n!\).
4.3. Vérification
4.4. Somme des entiers
On change simplement la multiplication par une addition et la valeur initiale de
f par 0. On va renommer f en s pour la clareté.
def somme(n) : i = 0 s = 0 while i < n : i = i + 1 s = s + i return s
On peut construire ce code par analogie du code, ou par analogie sur la formule de récurrence.
Si on voit le code de l’algorithme F comme le calcul du terme n de la suite
récurrente :
\[ \begin{cases} F_{0} & = 1 \\ F_{i+1} & = F_{i} * (i+1) \end{cases} \]
La somme des entiers se définie aussi pas une suite récurrente dont la définition est
\[ \begin{cases} S_{0} & = 0 \\ S_{i+1} & = S_{i} + (i+1) \end{cases} \]
Il suffit donc de garder le squelette de l’algorithme et de changer uniquement le terme initial de la suite et la formule qui permet de calculer le terme suivant à partir du précédent.
5. Diviser ou ne pas Diviser ?
5.1. Divisibilité
def est_diviseur(a,b) : return b % a == 0
5.2. Premier
def P(n) : p = True d = 2 while d * d <= n : if est_diviseur(d,p) : p = False d = d + 1 return p
Lien vers une simulation Python Tutor
Il y a un environnement local pour l’appel P(11), puis un environnement local
pour chaque appel de est_diviseur, i.e. 2 environnements. Donc 3
environnements locaux au total et 2 qui existent simultanément au plus.
5.3. Euclide
def euclide(a, b) : while a != b : if a > b : a = a - b else : b = b - a return a
6. Suites récurrentes
6.1. Méthode de Halley pour le calcul de \(\sqrt 2\)
def halley(n) : i = 0 h = 1.0 # H0 while i < n : # Invariant : h est le terme de rang i de la suite. h_suiv = h * (h*h + 6.0) / (3.0 * h*h + 2.0) i = i + 1 # Rang suivant h = h_suiv # h contient le terme suivant. return H
6.2. Suite de Fibonacci
def fibonacci(n) : rang = 0 f = 0 f_suivant = 1 while rang < n : f_suivant2 = f + f_suivant rang = rang + 1 f = f_suivant f_suivant = f_suivant2 return f
6.3. Syracuse
def syracuse(s0) : # Rang 0 rang = 0 s = s0 # Calcul du rang suivant, jusqu'à ce que le terme courant soit 1 while s != 1 : if s % 2 == 0 : s_suiv = s // 2 else : s_suiv = 3 * s + 1 rang = rang + 1 s = s_suiv return rang
7. Interlude – \textsc{Pour s'entraîner}
7.1. Suite géométrique
def u(a, q, n) : rang = 0 u = a while rang < n : u = q * u rang = rang + 1 return u
7.2. Suites adjacentes
def adjacentes(epsilon) : a = 1 b = 2 i = 0 while -epsilon > a - b or a - b > epsilon : a = 2 / b b = (a + b) / 2 i = i + 1 return i
7.3. Méthode de Héron
def heron(a,n) : x = a i = 0 while i < n : x = (x + a / x) / 2 i = i + 1 return x
7.4. Nombre parfait
def parfait(n) : if n < 2 : return False somme_diviseurs = 0 diviseur = 1 while diviseur < n : if n % diviseur == 0 : somme_diviseurs = somme_diviseurs + diviseur diviseur = diviseur + 1 return somme_diviseur == n
On peut diminuer le nombre de diviseurs potentiels à vérifier en remarquant que
si d divise n alors n // d divise n et que les diviseurs vont donc par
pairs avec un diviseur plus petit que \(\sqrt n\) et un autre strictement plus
grand. Il faut exclure les cas de \(1\) allant de pair avec \(n\) et de \(\sqrt n\) si
il est entier qui va de pair avec lui même et ne doit pas être ajouté deux fois.
def parfait_opt(n) : if n < 2 : return False somme_diviseurs = 1 diviseur = 2 while diviseur * diviseur < n : if n % diviseur == 0 : somme_diviseurs = somme_diviseurs + diviseur + ( n // diviseur) diviseur = diviseur + 1 if diviseur * diviseur == n : somme_diviseurs = somme_diviseurs + diviseur return somme_diviseur == n
8. Interlude – \textsc{Pour aller plus loin}
8.1. Logarithme entier
Plusieurs visions sont possible.
- Partir de \(0\) et essayer tous les entiers \(l\) jusqu’à trouver le premier tel que \(b^l > n\).
- Transformer l’inégalité comme ceci : \[ 1 \leq \frac{n}{b^l} < b \] En conclure qu’il faut diviser \(n\) par \(b\) jusqu’à obtenir un nombre entre 1 et \(b\). \(l\) est le nombre de division effectuée
def log_entier(n, b) : l = 0 q = n while 1 > q or q > b : q = q // b l = l + 1 return l
8.2. Nombres Chanceux d’Euler
def premier(n) : """ Entrées : n un entier >= 2 Sortie : True ssi n est premier """ diviseur = 2 while diviseur * diviseur <= n : if n % diviseur == 0 : return False diviseur = diviseur + 1 return True def chanceux_euler(n) : i = 0 while i <= n - 2 : if not premier(i * i + i + n) : return False i = i + 1 return True
8.3. Fonction 91 de McCarthy
Cette fonction fait parti d’un groupe de fonction qui ont peu d’intérêt en soit, mais qui sont utilisées pour tester la performance des compilateurs.
La fonction est naturellement recursive… Mais on ne sait pas encore ce que ça veut dire…
Pour la coder de manière itérative, il faut se tordre un peu l’esprit…
On peut voir la fonction sous cet angle :
\[ \forall k \in \symbb{N^{*}, f^{k}(n) = \begin{cases} f^{k-1}(n-10) & \text{si} n>100 \\ f^{k+2}(n+11) & \text{sinon.} \end{cases} \]
Gardons un compteur profondeur qui compte le nombre de fois que la fonction doit être appliquée sur n. Au départ ce compteur sera égal à 1 vu qu’on cherche à calculer \(f(n)\).
On applique la fonction tant que le compteur n’est pas nul, cas de base de la définition.
- si l’entrée > 100 alors on applique la fonction on enlève 10 à l’entrée. On diminue le compteur de 1, et le résultat sera l’entrée de la prochaine application de fonction.
- si l’entrée <= 100 alors on ajoute 11 à l’entrée, et on augmente le compteur de 1. Le resultat sera l’entrée de la prochaine application de fonction.
def mccarthy91(n) : k = 1 e = n # Invariant : f(n) = f^k(e) while k != 0 : if e > 100 : e = e - 10 k = k - 1 else : e = e + 11 k = k + 1 # Ici k vaut 0 dans f(n) = f^0(e) = e return e
9. Manipulation des listes
9.1. Création
l0 = [1,3,6] l0[3] # IndexError x = l0[len(l0)-1] l1 = [12] * 144 l2 = l0 + l1 # Attention, ce n'est pas pareil que l1 + l0 l0 = [] l1 = l2[1:5]
9.2. Modification
l1[0] = 1 l1[1] = l1[2] // l1[1] x = l1.pop() l1[2] = len(l1) l1.append(40 % x)
10. Etude d’un algorithme
10.1. Simulation Papier
- Il y a 10 tours de boucle.
Les valeurs de
ietmsont :i1 2 3 4 5 6 7 8 9 10 m3 3 4 4 4 5 5 10 10 10 m = max(l[:i])
10.2. Spécification de nadine
Comme à la fin i est égal à la longueur de la liste l, m = max(l).
nadine renvoie le maximum de la liste donnée en entrée, ou None si celle-ci
est vide.
10.3. Vérification
def nadine(l) : n = len(l) if n == 0 : return None m = l[0] i = 1 while i < n : if m < l[i] : m = l[i] i = i + 1 return m
11. Algorithmique des listes
11.1. Minimum de liste
11.2. Somme des éléments d’une liste
def somme(l) : n = len(l) i = 0 somme = 0 while i < n : somme = somme + l[i] i = i + 1 return somme
11.3. Maximum, minimum, moyenne, variance
def imax(l) : n = len(l) if n == 0 : return None i_max = 0 i = 1 while i < n : if l[i] > l[i_max] : i_max = i i = i + 1 return i_max
def imin(l) : n = len(l) if n == 0 : return None i_min = 0 i = 1 while i < n : if l[i] < l[i_min] : i_min = i i = i + 1 return i_min
def moyenne(l) : n = len(l) i = 0 somme = 0 while i < n : somme = somme + l[i] i = i + 1 return somme / n # ou en utilisant la question précédente def moyenne(l) : return somme(l) / len(l)
def variance(l) : n = len(l) i = 0 moy = moyenne(l) somme = 0.0 while i < n : somme = somme + (l[i] - moy) ** 2 i = i + 1 return somme / n
11.4. Recherche Linéaire
def recherche(l, x) : n = len(l) i_x = -1 i = 0 while i < n and i_x == -1 : if l[i] == x : i_x = i i = i + 1 return i_x
11.5. Addition de deux listes
def somme(l1 : list[int], l2 : list[int]) -> list[int] : i = 0 n = len(l1) l = [0] * n while i < n : l[i] = l1[i] + l2[i] i = i + 1 return l
def addition(l1, l2) : n = len(l1) i = 0 l = [] while i < n : l.append(l1[i] + l2[i]) i = i + 1 return l
11.6. Liste des diviseurs
def diviseurs(n) : n = len(l1) d = 1 l = [] while d <= n : if n % d == 0 : l.append(d) d = d + 1 return l
12. \textsc{Pour s'entraîner}
12.1. Moyenne pondérée
def moyenne_ponderee(l, c) : n = len(l) somme_pond = 0 somme_coeff = 0 i = 0 while i < n : somme_pond = somme_pond + l[i] * c[i] somme_coeff = somme_coeff + c[i] i = i + 1 return somme_coeff / somme_pond
12.2. Éléments impairs
def impairs(l) : n = len(l) i = 0 impairs = [] while i < n : if l[i] % 2 == 1 : impairs.append(l[i]) i = i + 1 return impairs
12.3. Liste des multiples
def multiples(n,m) : multiple = 0 l = [] while multiple <= n : l.append(multiple) multiple = multiple + m return multiple
12.4. Encadrement
On utilise deux astuces pour initialiser a et b ici.
On sait que les entiers de la liste sont positifs. En initialisant b à -1, on
sait qu’il est plus petits que tous les éléments de la liste. Ainsi, le premier
élément de la liste plus grand que x que l’on trouve sera plus grand que b.
b deviendra cet élément, et b sera bien le plus petit élément de la liste
plus grand que x trouvé à ce moment là. Si on ne trouve aucun élément de la
liste plus grand que x, b ne change pas et on renvoie bien -1.
Pour ce qui est de a, on remarque que -a est le plus petit élément plus
grand que -x dans la liste -l, où chaque entier est remplacé par son opposé.
On peut donc le même algorithme de calcul que pour b mais en opposant chaque
valeur. Ainsi b = -1 devient a = 1, l[i] > x and l[i] > b devient -l[i] >
-x and -l[i] > a, donc l[i] < x and -a < l[i]. Il suffit de renvoyer -a à
la fin.
def encadrement(l,x) : n = len(l) a = 1 b = -1 i = 0 while i < n : if l[i] == x : return [x,x] elif l[i] < x and -a < l[i] : a = -l[i] elif l[i] > x and l[i] < b : b = l[i] i = i + 1 return [-a,b]
13. \textsc{Pour aller plus loin}
13.1. Crible d’Eratosthène
La grille d’entiers sera une liste crible.
Pour barrer les éléments il n’est pas efficace de les supprimer les éléments de la liste. Mieux vaut créer une nouvelle liste sans les éléments à barrer.
def eratosthene(n) : premiers = [] crible = [] k = 2 while k <= n : crible.append(k) k = k + 1 while len(crible) != 0 : premiers.append(crible[0]) nouveau_crible = [] i = 0 while i < len(crible) : if crible[i] % crible[0] != 0 : nouveau_crible.append(crible[i]) i = i + 1 # On associe crible à la nouvelle liste. crible = nouveau_crible # Ce n'est pas une copie ! return premiers
13.2. Tri de liste
Le tri sélection est peut-être le plus intuitif.
- Trouver l’indice
idu minimum de la listelet échanger l’élément d’incideiavec celui d’indice0. - Trouver l’indice
idu minimum de la listel[1:]et échanger l’élément d’incideiavec celui d’indice1. - … Répéter jusqu’à l’avant dernier indice.
Par exemple sur la liste [5,3,6,3,1,2] :
- Echange de 1 et 5 :
[1,3,6,3,5,2] - Echange de 2 et 3 :
[1,2,6,3,5,3] - Echange de 6 et 3 :
[1,2,3,6,5,3] - Echange de 6 et 3 :
[1,2,3,3,5,6] - Echange de 5 et 5 :
[1,2,3,3,5,6] - Fin de l’algorithme
def tri_selection(l) : n = len(l) debut = 0 while debut < n - 1 : m = debut i = debut+1 while i < n : if l[i] < l[m] : m = i i = i + 1 temp = l[m] l[m] = l[debut] l[debut] = temp debut = debut + 1 return None
13.3. Calcul de la Médiane
def mediane(l) : k = l.copy() tri_selection(k) if len(k) % 2 == 1 : return k[len(k) // 2] return (k[len(k) // 2] + k[len(k) // 2 + 1]) / 2
Une autre méthode, sans trier, mais moins efficace (surtout si on trie de manière plus efficace), consiste chercher les éléments du milieu en comptant pour chaque élément combien il y en a de plus petits.
14. Chaînes et séquences
14.1. Recherche linéaire
def recherche_lineaire(s,c): for i in range(len(s)): if s[i] == c: return i return -1
14.2. Comptage chaîne
def compter_chaine(s,c): n = 0 for car in s: if car == c: n = n + 1 return n
14.3. Addition de liste
def addition_liste(l1,l2): l = [] for i in range(len(l1)): l.append(l1[i] + l2[i]) return l # ou def addition_liste(l1,l2): return [ l1[i] + l2[i] for i in range(len(l1)) ]
14.4. Addition de tuple
def addition_tuple(t1,t2): t = () for i in range(len(t1)): t = t + (t1[i] + t[2],) return t
14.5. Deuxième minimum
def deuxieme_minimum(l): m = float("inf") m2 = float("inf") for x in l: if x < m: m = x m2 = m elif x < m2: m2 = x return m2
14.6. Voyelles
def voyelles(s): v = "" for c in s: if recherche_lineaire("aeiouyAEIOUY",c) != -1: v = v + c return v
15. Passage en deux dimensions
15.1. Pyramide
Pour produire la pyramide, on va produire chaque étage qu’on va concatener les un avec les autres, séparés par un retour à la ligne.
Pour produire le ième étage, on doit connaître :
- le nombre d’allumettes de l’étage
- la taille de la base de la pyramide pour pouvoir compléter avec des espaces
Le nombre d’allumettes à l’étage \(i\), \(0 \leq i < n\) , est \(2i + 1\). La base d’une pyramide de n étage est donc \(2n + 1\) allumettes. À chaque étage il reste \(2n + 1 − 2i + 1 = 2(n − i + 1)\) espaces. On les répartis, \((n − i + 1)\) espaces de chaque cotés pour équilibrer.
Le ième étage sera donc produit au ième tour de boucle du programme.
def pyramide(n): str_pyramide = "" etage = 0 for etage in range(n) : str_etage = (" " * (n - etage + 1) + "|" * (2 * etage + 1) + " " * ( n - etage + 1)) str_pyramide = str_pyramide + str_etage + "\n" return str_pyramide
15.2. Table ASCII
def table_ascii(): table = "" ligne_initiale = " " # Ligne initiale for col in range(10) : ligne_initiale = ligne_initiale + " " + str(col) + " " # Barre barre = "-" * len(ligne_initiale) # Tableau table = ligne_initiale + barre + "\n" # Ecriture de chaque ligne for ligne in range(0,130,10) : ligne_courante = str(ligne) # Première colonne # Ecriture de chaque colonne for col in range(10) : ligne_courante = ligne_courante + " " + chr(ligne + colonne) + " " table = table + ligne_courante + "\n" return table
15.3. Table de multiplication
def table_multiplication(n : int) -> str : table = "" colonne = 0 # Ligne initiale ligne_initiale = "" ligne_initiale = ligne_initiale + " " # Première colonne while colonne <= n : # Affichage du chiffre de la colonne ligne_initiale = ligne_initiale + " " + str(colonne) + " " colonne = colonne + 1 table = ligne_initiale + "\n" # Barre barre = "-" * (1 + 3 * (n + 1)) table = table + barre + "\n" # Tableau ligne = 0 # Ecriture de chaque ligne while ligne <= n : colonne = 0 ligne_courante = "" ligne_courante = ligne_courante + str(ligne) # Première colonne # Ecriture de chaque colonne while colonne <= n : ligne_courante = ligne_courante + " " + str(ligne * colonne) + " " colonne = colonne + 1 table = table + ligne_courante + "\n" ligne = ligne + 1 # On passe à la dizaine suivante return table