Info 11
Info 11
Info 11
PYTHON ET SPYDER
ALGORITHME
Un algorithme est une suite finie d’opérations élémentaires constituant un schéma de calcul ou de résolution
de problème.
Faire de l’algorithmique, c’est donc décomposer un problème complexe en des sous-problèmes de plus en
plus simples jusqu’à arriver aux actions élémentaires que connaı̂t l’ordinateur.
L’INTERFACE SPYDER
Pour travailler en Python, il suffit d’écrire de simples fichiers textes (extension « *.py ») et de les interpréter.
Cependant, pour faciliter l’interface Python-utilisateur on utilise souvent un environnement de développement
pour faciliter la programmation comme les logiciels Pyzo, Spyder, Emacs ou Idle.
L’environnement Spyder se décompose comme tous les autres environnements en plusieurs fenêtres.
Il faut bien faire la différence entre les différentes fenêtres, l’interpréteur et l’éditeur :
I Sauvegarder votre programme avec un nom sans espace, sans tiret et sans accents, apostrophes, symboles...
I Une fois votre programme écrit, n’oubliez pas de le sauvegarder avant de l’exécuter (avec F5 ou I ).
I Si les commentaires ont lieu sur plusieurs lignes, on les écrit entre """. . .""" (triples guillemets).
I La commande \ sert pour aller à la ligne dans l’éditeur sans que l’interpréteur lui aille à la ligne.
(cela sert surtout lorsque nous avons une longue ligne de code et que nous voulons la voir complète à l’écran.)
I La commande print() permet d’écrire un texte ou d’afficher un résultat.
Attention : par défaut un programme n’affiche pas ce qu’il calcule sans la commande print().
I Parfois, lors d’écriture de scripts Python on utilise des fichiers annexes pour importer leur contenu, voir même
créer des fichiers directement avec Python. Il est nécessaire que les fichiers utilisés soient dans le répertoire de
travail indiqué par Spyder.
Il suffit de le régler sur le bon dossier comme indiqué ci-dessous.
ENTIERS ET FLOTTANTS
ENTIERS
I [[−2147483648, 2147483647]] = [[−232 , 232 − 1]], ce qui fait 264 nombres relatifs représentables sur 64 bits.
Néanmoins tout entier relatif x n’étant pas dans cet intervalle est quand même codé sans problème par Python
(pas de dépassement de capacité) sous la forme « d’entier long » (cet entier relatif sera stocké sous la forme
d’une collection de nombres représentatifs sous 64 bits qui seront concaténées pour représenter ce nombre x).
I La commande int(x) transforme le type float en type int (int(2.5) retourne 2)
FLOTTANTS
I Tout nombre réel x est en fait représenté sur 64 bits à l’aide de sa forme normalisée (signe, exposant
et mantisse), la taille étant limité à 64 bits, le code associé est donc très souvent seulement une (bonne)
approximation du réel x.
I La commande float(x) transforme le type int en type float (float(2) retourne 2.0)
Le résultat de la dernière ligne du tableau peut paraitre étonnant car mathématiquement l’égalité est vraie et
on s’attend à True mais la représentation de ces deux flottants n’étant qu’approchée, on n’a pas égalité de leur
représentation sur 64 bits ce qui explique le résultat False.
I C’est la raison pour laquelle on bannit le test d’égalité entre deux flottants.
TABLEAUX ET LISTES
LISTES
I La commande list(x) transforme le type str en type list (list("texte") retourne ["t","e","x","t","e"])
I La commande [x] transforme le type int ou float en type list (par exemple [3] ou [3.0])
3 print ( x ) 3 print ( L [ k ])
Remarque : Ne confondez pas l’élément du tableau T[i] avec son indice i.
(pour la première méthode de parcours d’un tableau, il est impossible de récupérer les indices des éléments.)
Vous remarquez qu’il y a des crochets à l’intérieur d’autres crochets, ceci veut dire qu’il y a des listes
imbriquée, il s’agit donc d’une liste ayant d’autres listes comme valeurs.
Chaque imbrication ajoute une dimension à la liste. (listes à trois dimensions, etc...)
Accéder aux éléments d’une liste de listes : L[i][j] (élément d’indice j de la liste d’indice i de L)
Exemple : L[0][0] est le premier élément de la première liste
Exemple : L[1][-2] est l’avant-dernier élément de la seconde liste (liste d’indice 1)
Exemple : L[0] est la première « élément-liste » de L en entier
Parcourir les éléments d’une liste de listes (forcément une double boucle) :
1 L =[[2 ,5 ,4 ,1] ,[5 ,8 ,9] ,[2] ,[4 ,7 ,8 ,6 ,4 ,7 ,8] ,[3 ,6] ,[5] ,[2 ,6 ,5 ,7 ,4]]
TABLEAUX
I Les listes sont des tableaux, ce qui les diffèrent, c’est que les valeurs des listes peuvent être de types
différents (une liste peut contenir un nombre et un texte.)
Ce qui a été expliqué précédemment sur les listes reste valable pour les tableaux.
1 liste =[ " mercredi " ,19 , " septembre " ,2015]
2 tableau =[ " mercredi " ," 19 " ," septembre " ," 2015 " ]
TUPLES
Type de variable Syntaxe Python Description Exemple
Tuple tuple n-uplets (couple, triplet) de variables (True,"e",3.14)
I Les éléments d’un tuple sont ordonnés et on accède à un élément grâce à sa position en utilisant un numéro
qu’on appelle l’indice de l’élément comme une liste.
I Contrairement aux listes, les tuples ne sont pas modifiables. (moins modulables que les listes, moins utilisés)
CHAÎNES DE CARACTÈRES
Type de variable Syntaxe Python Description Exemple
Chaı̂ne de caractères str Tableaux formés uniquement de lettres "Essouriau"
I Les chaı̂nes de caractères sont des variables contenants du texte. Elles se disent de type str.
I Les chaı̂nes de caractères s’écrivent entre guillemets " ou apostrophes ’.
I La plupart des opérations définies pour les tableaux s’appliquent aux chaı̂nes de caractères mais pas toutes.
I On remarque que l’espace " " compte pour un caractère à part entière ! Quelques autres caractères spéciaux.
Utilité Caractère
Utilité Caractère
Aller à la ligne \n
Afficher un anti-slash \\
Afficher un guillemet \"
Saut de page \f
Afficher une apostrophe \’
I Quelques commandes sur les chaı̂nes :
Chaı̂ne vide : chaine=""
Définir une chaı̂ne de petite taille par ses éléments : chaine="Voilà ce que je voulais vous dire!".
Longueur d’une chaı̂ne : len(chaine)
Élément n◦ k d’une chaı̂ne : chaine[k] , le dernier élément : chaine[-1] (l’index commence à 0 !)
On ne peut pas modifier une chaine, ajouter/retirer des éléments à une chaine !
Exemple : chaine.append("x") renvoie une erreur, comme chaine[k]="z" ou encore chaine.pop().
Même si les chaı̂nes de caractères sont immuables ou non modifiables, pour contourner ce problème
on peut effacer l’ancienne chaı̂ne au profit d’une nouvelle contenant les modifications.
Exemple : chaine="Bomjour!"
Modifier un caractère : chaine=chaine[:2]+"n"+chaine[3:]
Supprimer le dernier caractère : chaine=chaine[:-1]
DICTIONNAIRES
Type de variable Syntaxe Python Description Exemple
Dictionnaire dict collections d’éléments avec clés et valeurs {clef:valeur,...}
I Un dictionnaire (type dict) est une collection d’éléments composés d’une clé associée à une valeur.
I Contrairement aux listes qui sont délimitées par des crochets, on utilise des accolades pour les dictionnaires.
I Un dictionnaire en Python va permettre de rassembler des éléments (comme des listes ou tuples) mais ceux-ci
seront identifiés par une clé. Analogie à un dictionnaire où on accède à une définition avec un mot.
Commandes Description
val=dico[key] Stocke dans val la valeur associée à la clé key dans dico
dico[key]=val Remplace la valeur associée à la clé key par val dans dico (si key était déjà présente)
dico[key]=val Ajoute la clé key et sa valeur associée val dans dico (si key n’était pas présente)
if key in dico: Teste si la clé key est dans dico (complexité en O(1))
if dico[key]==val: Teste si la valeur associée à la clé key est égale à val
COMPARATIF LISTES/TUPLES/CHAÎNES/DICTIONNAIRES
Le tableau ci-dessous regroupe les principales spécificités et différences entre ces 4 types de variables.
On utilisera le plus adapté en fonction de ce qui est demandé.
INSTRUCTIONS CONDITIONNELLES
Une instruction conditionnelle permet d’exécuter des instructions seulement sous une certaine condition.
En Python, on utilise les mots clefs if, else et elif qui consistent à tester (avec des opérateurs de test) si
une affirmation est vraie ou non.
INSTRUCTIONS CONDITIONNELLES
I L’instruction if ... :
Une condition s’effectue avec le mot clé if ... : (mot anglais qui veut dire « si »).
I L’instruction else:
Le mot clef else: permet d’exécuter une instruction lorsque que la condition du if n’est pas vérifiée. (« else »
signifie « sinon » en anglais.)
1 age = eval ( input ( " Tu as quel ^
a ge ? " ))
2
3 if age ==18:
4 print ( " Tu es majeur . " )
5 else :
6 print ( " Tu n ’ as pas 18 ans . " )
I Instructions imbriquées
Attention au niveau d’indentation lorsque l’on utilise plusieurs boucles imbriquées. Par exemple :
1 age = eval ( input ( " Tu as quel ^
a ge ? " )) 1 age = eval ( input ( " Tu as quel ^
a ge ? " ))
2 2
I Astuces
Utiliser and ou or permet d’éviter plusieurs if ! par exemple pour x entier, 1 6 x 6 100 et impair on a :
1 if 0 < x and x <=100 and x %2==1:
Si une variable Test est un booléen True ou False, inutile d’utiliser un opérateur d’égalité dans un test.
On peut directement écrire :
1 if Test :
2 print ( " C ’ est vrai ! " )
La valeur du booléen True est reconnue par Python comme égale à celle de l’entier 1 !
Si on fait True==1 on obtient étonnamment True. De même False==0 donne True.
INSTRUCTIONS ITÉRATIVES
Les structures de contrôle itératives ou boucles permettent d’exécuter plusieurs fois de suite une ou
plusieurs instructions. Les deux types à maitriser sont : for et while .
Exemples : range(12) donne dans l’ordre les entiers {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
range(2,12,2) donne {2, 4, 6, 8, 10} et range(9,2,-1) donne dans l’ordre {9, 8, 7, 6, 5, 4, 3}.
Notez la nécessité d’une variable aléatoire « muette » i qui s’appelle un compteur . Elle représente en
général le numéro de la boucle que l’on est en train de parcourir et intervient parfois dans le test d’arrêt.
Il est nécessaire de l’incrémenter (i+=1) à chaque boucle afin de pouvoir compter le nombre de boucles.
Un autre exemple où while est incontournable cette fois, sans nécessité d’avoir un compteur :
1 u0 =2000
2 while u0 >0 :
3 print ( " Le terme de la suite vaut " , u0 )
4 u0 = np . log ( u0 )
FONCTIONS PYTHON
FONCTION
I Pour optimiser un algorithme, on va remplacer des bouts de codes qui se répètent par une fonction.
Ainsi, en apprenant à faire des fonctions, vous rendrez votre code plus lisible.
I Quasiment toutes les questions des sujets de concours demandent d’écrire ou d’examiner des fonctions.
I En programmation, les fonctions sont identiques aux fonctions mathématiques.
À une valeur x (ou plusieurs), la fonction va retourner une valeur y (ou plusieurs).
Une fonction en informatique est donc une séquence finie d’instructions, dépendant de paramètres d’entrée
(appelés arguments), et retournant un résultat (renvoi).
1 def fonction ( argument ):
2 intructions
3 return resultat
Le mot clé def pour définir une fonction, puis on nomme la fonction et on termine par (): .
Dans la parenthèse, on a le choix entre mettre une/des variable(s) d’entrée ou aucune !
Tout ce qui appartient à la fonction sera décalé avec une tabulation.
La fonction se termine par return suivi de ce que l’on souhaite retourner.
Noter que lors de l’exécution de l’instruction return la fonction obligatoirement prend fin.
1 def exemple (x ,y , z ):
I Entrées et sorties multiples. 2 w1 , w2 , w3 = x + y +z , x * y + y * z + x *z , x * y * z
Voici un exemple qui permet de faire connaissance 3 return w1 , w2 , w3
avec une affectation multiple (triple ici) : 4
5 x ,y , z = exemple (1 ,2 ,3)
PROCÉDURE
I Les procédures sont des fonctions sans return car elles n’ont pas besoin de renvoyer quelque chose.
En fait, elles ne font que réaliser des actions, elles s’arrêtent quand elles les ont toutes accomplies.
I Une procédure ne renvoie rien, donc lors d’un affichage il s’affiche None .
VARIABLES LOCALES/GLOBALES
I Lors de la création d’une fonction Python on est amené à utiliser des variables autres que les entrées pour cal-
culer la sortie attendue. Dans ce cas le programme reconnait ces variables dans la fonction mais pas en dehors.
Ces variables sont dites locales. Si vous voulez utiliser une variable définie dans une fonction après exécu-
tion, il faut le stipuler au début de la fonction à l’aide du mot clef global . (commande hors-programme et
possible que pour une variable définie dans une fonction qui n’est pas en entrée.)
Dans cet exemple, si la variable c est utilisée dans la 1 def fonction ( argument ):
2 global c
fonction, alors le programme reconnaitra la valeur qui
3 intructions
lui a été associée même en dehors de la fonction. 4 return resultat
ALGORITHMES CLASSIQUES
I Calcul de la somme des éléments d’une liste I Calcul du produit des éléments d’une liste
1 def somme ( L ): 1 def somme ( L ): 1 def produit ( L ): 1 def produit ( L ):
2 s =0 2 s =0 2 p =1 2 p =1
3 for k in L : 3 for k in range ( len ( L )): 3 for k in L : 3 for k in range ( len ( L )):
4 s += k 4 s += L [ k ] 4 p *= k 4 p *= L [ k ]
5 return s 5 return s 5 return s 5 return p
I Calcul de la moyenne des éléments d’une liste I Calcul de l’écart-type des éléments d’une liste
1 def moyenne ( L ): 1 def Ecart - Type ( L ):
2 s =0 2 m = moyenne ( L )
3 for k in L : 3 e =0
4 s += k 4 for k in L :
5 m = s / len ( L ) 5 e +=( k - m )**2
6 return m 6 return e **0.5
I Présence d’un élément dans une liste/chaı̂ne I Présence d’un élément dans une liste/chaı̂ne
1 def Presence (L , x ): 1 def Presence (L , x ):
2 if x in L : 2 for l in L :
3 return True 3 if l == x :
4 return False 4 return True
5 return False
I Recherche du 2nd maximum dans un tableau I Recherche des deux valeurs les plus proches
(sans le modifier) dans un tableau
1 def Second_Max ( T ): 1 def D e u x _ p l u s _ p r o c h e s _ v a l e u r s ( L ):
2 M = Maximum ( T ) 2 d = abs ( L [0] - L [1])
3 debut =0 3 ind1 , ind2 =0 ,1
4 while T [ debut ]== M : 4 for i in range ( len ( L )):
5 debut +=1 5 for j in range ( i ):
6 M2 = T [ debut ] 6 e = abs ( L [ i ] - L [ j ])
7 for k in range ( debut , len ( T )): 7 if e < d :
8 if T [ k ] > M2 and T [ k ]!= M : 8 d=e
9 M2 = T [ k ] 9 ind1 , ind2 =i , j
10 return M2 10 return [( L [ ind1 ] , L [ ind2 ])
I Calcul des termes d’une suite récurrente I Dépassement d’un seuil s pour une quantité
un+2 = 3un+1 − 2un avec u0 = 4 et u1 = −5 Par exemple trouver n tel que un > s.
1 def u ( n ): 1 def seuil (u , s ):
2 u0 =4 2 n =0
3 u1 = -5 3 while u ( n ) < s :
4 if n ==0: 4 n +=1
5 return u0 5 return n
6 if n ==1:
7 return u1
8 for k in range (2 , n +1):
9 u2 =3* u1 -2* u0
10 u0 = u1
11 u1 = u2
12 return u2
BIBLIOTHÈQUES ET MODULES
Voici certaines bibliothèques/modules de Python. Aucune connaissance n’est exigible (toute commande
sera rappelé) mais il ne faut pas les découvrir aux concours. En particulier, lors de l’épreuve oral Maths 2 de
Centrale.
Afin d’utiliser une bibliothèque ou module dans Python il faut au préalable l’importer dans votre
environnement Spyder. Il y a plusieurs façons de faire, exemple avec la bibliothèque math :
I Importation d’une fonction particulière. from math import sin par exemple, permet d’importer
spécifiquement la fonction sin, (ou cos, ou la constante e, sous ces noms là).
I Importation de toutes les fonctions. from math import * est similaire à l’importation précédente,
mais le joker * est utilisé à la place des noms explicites des fonctions.
I Importation de la bibliothèque. import math importe la bibliothèque, les fonctions sont alors ac-
cessibles par math.sin, math.cos, par exemple.
I Importation de la bibliothèque et alias. On abrège le nom de la bibliothèque avec un alias.
Avec import math as m les fonctions sont alors accessibles par m.sin, m.cos...
La documentation Python est assez bien fournie, pour y accéder il suffit d’écrire help(nom_du_module). Ceci
fonctionne également avec les alias. Par exemple, import math as m ; help(m) fournira de l’aide sur toutes les
fonctions du module. Bien sûr, il est possible d’obtenir de l’aide sur une fonction spécifique comme m.sqrt.
• Module math
Ce module contient les fonctions mathématiques usuelles, ainsi que les constantes e et π.
• Module matplotlib
On utilisera principalement le sous-module pyplot, qui sert à tracer des courbes. On l’importera comme
suit : import matplotlib.pyplot as plt.
• Module numpy
On importera ce module avec l’alias np : import numpy as np. Ce module permet la construction de
tableaux, listes, matrices et récupération de données propres aux listes (taille, nombre d’éléments...)
Sous-module linalg
Le sous-module numpy.linalg (importé comme import numpy.linalg as alg). Ce sous-module
permet de faire de l’algèbre linéaire (espaces vectoriels et leurs applications) du programme de PCSI et
PSI que vous pourrez découvrir ultérieurement. Il permet déjà d’effectuer les opérations matricielles.
Sous-module random
Le sous-module random permet de générer des nombres aléatoires, et plus généralement de traiter de
probabilités. On l’importe ici comme import numpy.random as rd.
• Module time
Permet d’importer la fonction time() qui relève le temps depuis une date donnée.
• Module scipy (Sous-modules optimize, integrate, odeint)
Le module scipy est le module de calcul scientifique. Trois points au programme : résolution d’équations
numériques, intégration de fonctions et résolution d’équations différentielles.
On utilise le sous-module optimize de scipy : import scipy.optimize as sco. La fonction fsolve
est tout indiquée pour résoudre une équation de la forme f (x) = 0.
On utilise le sous-module integrate de scipy : import scipy.integrate as sci. La fonction que
l’on va utiliser est quad. Il suffit d’indiquer la fonction à intégrer et les bornes pour calculer la valeur
de l’intégrale.
L’intégration d’équations différentielles se fait avec le même sous-module, et la fonction odeint (pour
ordinary differential equations integration).
• Autres modules : copy, fractions, polynomial, itertools, tkinter...
BIBLIOTHÈQUE matplolib.pyplot
Le programme de CPGE est clair : aucune des commandes ci-dessous n’est exigible par les élèves de CPGE.
Elle seront toutes rappelées lors des concours.
On se contente ici d’exhiber 3 usages possibles de cette bibliothèque, avec le sous-module pyplot.
Pour tracer une courbe, définir un liste de points X 1 import numpy as np # j ’ importe les bibliothèques
discrétisant l’intervalle [a, b] de définition de f et 2 import matplotlib . pyplot as plt
3
définir la liste des images Y.
4 def f ( x ): # je définis ma fonction
Pour représenter sin sur [−2π, π] ont peut écrire : 5 return np . sin ( x )
6
7 a = -2* np . pi # je définis l ’ intervalle [a , b ]
8 b =2* np . pi
9 n =100 # je définis le nombre de points
10 # je définis ma liste d ’ abscisses et d ’ ordonnées
11 X =[ a + i *( b - a )/ n for i in range ( n +1)]
12 # je définis ma liste d ’ ordonnées
13 Y =[ f ( x ) for x in X ]
14
15 plt . plot (X , Y ) # je réalise le tracé de f
16 plt . grid () # j ’ affiche le quadrillage
17 plt . show () # j ’ affiche le tracé
• Il existe aussi le module pylab combine la fonctionnalité pyplot (pour le tracé) avec numpy.
HISTOGRAMME
• Possibilité de créer des diagrammes en bâton (boite à moustaches), circulaires (camembert) et bien d’autres !
ALGORITHMES DICHOTOMIQUES
I Les algorithmes dichotomiques qui utilisent le principe du « Diviser pour régner » afin de diminuer la
complexité temporelle (en calcul) d’algorithmes.
I L’idée est de passer de la résolution d’un problème de « taille » n à un (ou plusieurs) problème de « taille »
n n n
2 plus rapide à résoudre et d’itérer ce principe. (passer à des problèmes de taille 4 , puis 8 , ...)
Bien entendu, on espère que la résolution de l’ensemble de ces sous-problèmes de taille « petite » soit plus
rapide que le problème initial.
EXPONENTIATION RAPIDE
I Soit a un réel et n un entier naturel. Sous Python, la commande a**n renvoie la valeur du nombre an .
On pourrait penser que cette valeur est calculée par Python avec le principe que an = a
| × .{z
. . × a} ainsi :
n fois
1 def Exp (a , n ):
2 y =1
1 def Exp (a , n ):
serait équivalent à 3 for k in range (1 , n +1):
2 return a ** n
4 y *= a
5 return y
Fonction itérative
Fonction récursive
1 def Exp (a , n ):
1 def Exp (a , n ):
2 y ,z , b =a ,1 , n
2 if n ==1:
3 while m >0:
3 return a
4 q = m //2
4 if n %2==0:
Qui se traduit en Python par : 5 y = Exp (a , n //2)
5 r = m %2
6 if r ==1:
6 return y * y
7 z *= y
7 else :
8 y *= y
8 y = Exp (a ,( n -1)//2)
9 m=q
9 return y * y * a
10 return z
ALGORITHMES RÉCURSIFS
Le programme de CPGE est clair :
• Version récursive d’algorithmes dichotomiques.
• Fonctions produisant à l’aide de print successifs des figures alphanumériques.
• Dessins de fractales.
• Énumération des sous-listes ou des permutations d’une liste.
• On évite de se cantonner à des fonctions mathématiques (factorielle, suites récurrentes).
• On peut montrer le phénomène de dépassement de la taille de la pile.
COEFFICIENTS BINOMIAUX
Le but est de calculer les coefficients binomiaux à Une fonction récursive possible est :
l’aide de la formule du triangle de Pascal :
1 def binome (n , p ):
if p ==0 or p == n :
n+1 n n 2
∀n ∈ N, ∀p ∈ [[0, n]], = + 3 return 1
p p p−1
4 return binome (n -1 , p )+ binome (n -1 ,p -1)
:
Remarque : Là encore on voit la dangerosité de la récursivité car dans le pire des cas le nombre d’appels
double pour passer de l’étape n à l’étape n − 1 et donc la complexité asymptotique est exponentielle en O(2n ).
n!
I Il y a Akn = (n−k)! de sous-listes à k éléments parmi n éléments de type V1.
n n!
Il y a k = k!(n−k)! de sous-listes à k éléments parmi n éléments de type V2.
I Le principe de l’algorithme consiste à traiter le cas de base lorsque k = 1 puis à créer par récursivité
l’énumération de plusieurs sous-listes de L en k − 1 éléments et de leur ajouter ce qui leur manque.
On ajoute alors tous les résultats attendus en k éléments dans une liste qui est renvoyée.
TRACÉE DE FRACTALES
On veut tracer la figure fractale (objet mathématique qui présente une structure similaire à toutes les échelles)
appelée triangle de Sierpinski.
ALGORITHMES DE TRI
ISelon le programme officiel, aucune connaissance n’est exigible sur les algorithmes de tri.
Il n’est pas nécessaire d’apprendre le contenu de ce paragraphe par cœur. Néanmoins comprendre le fonction-
nement des tris permet de contrôler que qu’on a bien acquis les différents principes de programmation.
I Les tris ont chacun leur fonctionnement propre que l’on peut visualiser avec la vidéo YouTube https:
//www.youtube.com/watch?v=kPRA0W1kECg réalisée par Timo Bingmann.
I On s’intéresse surtout à comparer leur complexité temporelle (temps maximal asymptotique d’exécution)
mais aussi leur complexité spatiale (place mémoire maximale prise).
Les algorithmes de tri les moins performants ont une complexité quadratique (en O(n2 )) et les plus performants
en O(n ln(n)) où n est la taille de la liste donnée en entrée.
I On dit qu’un tri est comparatif s’il n’utilise que des comparaisons binaires pour trier la liste. Un bon nombre
de tris sont comparatifs.
I On dit qu’un tri est en place s’il n’utilise qu’un nombre très limité de variables et qu’il modifie directement
la structure qu’il est en train de trier. Ce caractère peut être très important si on ne dispose pas de beaucoup
de mémoire.
I On dit qu’un tri est dit stable s’il préserve l’ordonnancement initial des éléments que l’ordre considère comme
égaux.
Le tri bulle est une stratégie de tri locale, qui consiste à trier une liste d’objets
en portant des œillères. Le principe est de regarder les éléments consécutifs de
la liste deux par deux et de les mettre dans l’ordre.
On part de la fin de la liste et l’on regarde les éléments consécutivement. A la
fin d’un premier passage sur la liste, l’élément le plus petit se retrouve en tête
de liste.
C’est de là que provient le nom : l’algorithme imite la remontée de bulles. Si la
liste est de taille n, après n passages, les n bulles seront remontées et classées
dans l’ordre.
Les flèches représentent les échanges réalisés entre les contenus de cases. Une
croix indique que les éléments étaient déjà triés et qu’il n’y a donc pas eu
d’échange.
Soit L une liste de nombres non triée, on code le tri bulle en deux temps (un algorithme pour effectuer une
étape puis un autre pour la répéter ).
I La fonction remontee_Bulle(L) déplace l’élément le plus petit en première position en suivant le principe
du tri bulle.
1 def remontee_Bulle ( L ):
2 n = len ( L )
3 for i in range (n -2 , -1 , -1):
4 if L [ i +1] < L [ i ]:
5 L [ i ] , L [ i +1]= L [ i +1] , L [ i ]
6 return L
Le tri par insertion est un algorithme efficace de tri d’un petit nombre d’éléments.
Son principe de fonctionnement est celui qui est souvent utilisé pour trier un jeu de cartes. On commence
avec la main gauche vide et le paquet de cartes retourné sur la table. On retourne ensuite la première carte du
paquet avec la main droite, on l’ajoute à la fin du paquet et on l’amène progressivement à la bonne position
dans la main gauche.
Pour trouver cette bonne position, on compare la carte piochée aux cartes de la main gauche une par une,
de droite à gauche.
Ici la main gauche dans laquelle on insère la nouvelle carte retournée numérotée 5.
A chaque instant, les cartes dans la main gauche sont triées et la main est composée des cartes qui étaient
au-dessus du paquet.
I Fonction insertion(L,x) qui prend en entrée une liste L (triée) et un entier ou flottant x, ajoute x à la liste
et l’amène progressivement à la bonne place selon le principe du tri par insertion.
1 def insertion (L , x ):
2 L . append ( x )
3 place = len ( L ) -1
4 while L [ place ] < L [ place -1] and place >0:
5 L [ place ] , L [ place -1]= L [ place -1] , L [ place ]
6 place -=1
I Fonction Tri_insertion(L) qui effectue le tri par insertion sur une liste L.
On commence par définir une nouvelle liste vide L_triee que l’on triera au fur et à mesure.
1 def Tri_insertion ( L ):
2 L_triee =[]
3 for x in L :
4 insertion ( L_triee , x )
5 return L_triee
I La complexité de insertion est linéaire.
La complexité de Tri_insertion est quadratique : on fait appel n fois à un algorithme de complexité linéaire
pour une liste de taille k pour k allant de 0 à n − 1, ce qui équivaut à une double boucle triangulaire en fait.
Il y a donc au maximum n(n−1) 2 boucles et une complexité quadratique en O(n2 ).
L’algorithme écrit ici n’est pas en « en place » (vu qu’on utilise des tableaux auxiliaires), néanmoins il peut
être écrit de sorte à être en place)
ALGORITHMES GLOUTONS
Le but des algorithmes gloutons est de résoudre des problèmes d’optimisation à l’aide d’algorithmes. Un
problème d’optimisation consiste à minimiser ou maximiser une fonction/quantité sur un ensemble.
On peut citer comme exemple :
• le problème du sac à dos : maximiser le contenu (en valeur) d’un sac à dos de volume fixé en le
remplissant d’objets ayant chacun un volume et une valeur qui leur est propre.
• le problème du rendu de monnaie : comment rendre la monnaie à un client avec le moins de pièces/-
billets possible ?
• le problème d’allocation de salles : comment caser le maximum d’heures de cours dans le moins de
salles possibles tout en restant dans une plage horaire la moins ample possible.
• optimisation de trajet : minimiser le temps de trajet entre deux points A et B sur un réseau routier
Tous ces problèmes n’ont pas nécessairement une unique solution et on ne connait pas toujours d’algorithme
optimal pour répondre au problème dans toutes les situations de départ. On développe néanmoins une tactique
via un certain type d’algorithmes appelés algorithmes gloutons dont le principe est de prendre en priorité les
objets « les plus efficaces » pour répondre au problème.
Remarque.
Dans un système monétaire [10,9,2,1] et pour un montant 18, cet algorithme n’est pas optimal, en effet, on
a été trop gourmand en prenant directement une pièce de 10e (la plus grande pièce possible), et du coup on ne
pourra plus tomber sur la solution optimale (2 pièces de 9e) car il reste 8e à payer (avece 4 pièces de 2e).
Pour cette raison, on dit que c’est un algorithme glouton.
Selon le programme de CPGE, aucune des commandes ci-dessous n’est à connaitre par cœur.
Ouvrir un fichier avec Python se fait à l’aide de la commande open . Techniquement, on créé un objet
Python (dont le nom est complètement indépendant de celui du fichier texte) qui est un pointeur vers le fichier.
contenu=open("nom_fichier.txt",options à ajouter )
La commande open est assorti d’options, qui dépend de ce que l’on veut faire avec le fichier une fois ouvert :
• mode lecture (option "r") si on souhaite uniquement lire le fichier, accéder au contenu sans le modifier ;
• mode écriture (option "w") si on souhaite uniquement écrire des choses dans le fichier ;
• mode en ajout (option "a") si on veut uniquement ajouter du contenu à un fichier en contenant déjà.
Remarque. ATTENTION !
• On ne peut pas ouvrir un fichier simultanément en mode lecture et écriture : il faut le refermer avant.
Remarque. ATTENTION !
• L’option "w" créé automatiquement un nouveau fichier texte dans lequel écrire.
• Mais surtout va écraser un fichier texte déjà existant portant le même nom.
• Si on veut modifier le contenu d’un fichier existant, il faut absolument utiliser l’option "a").
Selon le programme de CPGE, aucune des commandes ci-dessous n’est à connaitre par cœur.
En revanche vous devez savoir sous quelle forme est stockée une image dans un fichier.
Image png en résolution 390 × 250 Image png en résolution 195 × 125
1 Img = imageio . imread ( " noir_et_blanc . png " ). tolist () # Convertit une image en liste
2
3 # Nombre de colonnes ( w ) et nombre de lignes ( h )
4 print ( ’w = ’ , len ( Img [0]) , ’ h = ’ , len ( Img ))
5
6 plt . imshow ( Img , cmap = ’ gray ’ , clim =(0 ,255)) # Création de l ’ image comme figure
7 plt . title ( " Titre à compléter " ) # Titre de la figure
8 plt . show () # Ouvre une fen^ e tre et affiche l ’ image
1 Img =[[178 ,234 ,... ,97] ,[56 ,0 ,... ,111] ,... ,[154 ,2 ,... ,255]]
I La couleur de chaque pixel est codée sur un octet (c’est-à-dire 8 bits donc 28 = 256 octets possibles), et
représente donc une nuance de gris parmi 256 possibles. On appelle cette valeur la luminosité du pixel :
• la valeur 0 code la couleur noire ,
• la valeur 255 code la couleur blanche ,
• les valeurs intermédiaires codent des nuances de gris de plus en plus claires à mesure que la valeur
augmente.
I Un pixel est donc un entier accessible via la commande Img[i][j] (i-ème ligne et j-ème colonne).
I Ce codage s’appelle RGB pour Red Green Blue et où pour chacune de ces couleurs :
• la valeur 0 indique qu’on met 0% du « pot de cette couleur ».
• la valeur 255 indique qu’on met 100% du « pot de cette couleur ».
Chaque pixel est donc représenté par un triplet d’octets codant un entier entre 0 et 255.
Ces algorithmes ne sont pas à apprendre par cœur mais sont classiques lors de la manipulation d’image.
1 def dim ( Img ):
I Récupérer la résolution de l’image
2 return ( len ( Img [0]) , len ( Img ))
REPRÉSENTATION DE NOMBRES
ENTIERS NATURELS
ENTIERS RELATIFS
NOMBRES RÉELS
MÉTHODE DE PROGRAMMATION
La signature est l’ensemble des paramètres (avec leurs noms, positions, valeurs par défaut et annotations),
ainsi que l’annotation de retour d’une fonction. (informations décrites à droite du nom de fonction.)
Exemple. Voici une même fonction mais dont les types des variables ont été modifiées dans les spécifications.
Spécification 1 : Spécification 2 :
1 def addition ( a : int , b : int ) -> int : 1 def addition ( a : list , b : list ) -> list :
2 return a + b 2 return a + b
La signature des deux fonctions ci-dessus sont (a:int, b:int) -> int et (a:list, b:list) -> list.
Cela ne gêne en rien l’exécution de la fonction si les variables entrées ne sont pas du bon type.
Les annotations sont des commentaires permettant d’expliquer la démarche/logique du code écrit et
rappelant le but du script écrit dans le but d’être bien compris par une personne extérieur.
Placé en tête d’une fonction, l’annotation sert à décrire l’usage de la fonction (accessible avec la commande
help).
Exemple. Voici une même fonction mais dont les types des variables ont été précisés dans les annotation.
Sans annotation : Avec annotation
1 def addition (a , b ): 1 def addition (a , b ):
2 return a + b 2 " Return the sum of the two numbers ‘a ‘ and ‘b ‘ "
3 return a + b
La commande help(addition) renverra alors le texte Return the sum of the two numbers ‘a‘ and ‘b‘.
La commande d’annotation assert est l’un des outils qui peuvent permettre de détecter en cours d’exé-
cution d’un code des erreurs qui conduiraient à un mauvais résultat via un test à réaliser.
Cela permet détecter les erreurs et de sortir de la fonction en indiquant quel type d’erreur a été commis.
Exemple.
Lorsque la liste est vide, il y a une erreur. On peut faire en sorte de vérifier si la liste est vide avant d’appeler
L[0] et de renvoyer un message souhaité si elle est vide, ce qui arrêtera l’exécution du code.
1 assert booleen , " message si False "
On modifie alors la fonction et le message d’erreur renvoyé est celui de l’échec du test de assert :
TERMINAISON ET CORRECTION
To be completed...
COMPLEXITÉ
√
f (n) ln(n) n n n2 n3 2n n! nn
nmax très très gros 3, 6 × 1021 6 × 1010 244948 3914 35 13 10
Voici le temps d’exécution pour un problème de taille n = 106 sur un ordinateur à 109 opérations par seconde :
O(1) Temps constant 1 ns Le temps d’exécution ne dépend pas des données traitées, ce qui est
assez rare !
O(ln(n)) Logarithmique 1 µs En pratique, cela correspond à une exécution quasi instantanée. Bien
souvent, pour des algorithmes dichotomiques c’est log2 (n) que l’ont
voit apparaı̂tre.
O(n) Linéaire 1 ms Le temps d’un tel algorithme ne devient supérieur à 1 min que pour
des données de taille comparable à celle des mémoires vives actuelles.
O(n ln(n)) Quasi-linéaire 10 ms Le temps d’exécution est légèrement supérieur au cas linéaire.
O(n2 ) Quadratique 1/4h Cette complexité est acceptable pour n < 100 mais pas au delà.
O(nk ) Polynômial 30 ans Il n’est pas rare de voir des complexités en O(n3 ) ou O(n4 ).
O(2n ) Exponentielle 1030000 Un algorithme de telle complexité est impraticable, sauf pour des
ans données très petites.
Remarque. En pratique, la complexité temporelle est plus importante que la complexité spatiale.
I On exprime la complexité en fonction de n, la taille de l’entrée (longueur de la liste, de la chaı̂ne de
caractères, de l’entier lui-même ou bien du nombre de chiffres de l’entier entré).
I Les notations de Landau (O(ln(n)), O(n), O(nk ) où k ∈ N, O(2n )) sont les mieux adaptées pour l’exprimer.
I On note C(n) la complexité d’un algorithme (nombre d’opérations élémentaires effectuées dans l’algorithme).
Remarque. Ce n’est pas parce que la syntaxe d’une instruction est courte qu’elle a une complexité en O(1) ! ! !
Remarque. Pour de très grands entiers n, le temps d’exécution de *,//,% est légèrement plus élevé que +,-.
Néanmoins dans la pratique on ne compte qu’une « opération » pour chacune de ces commandes.
Remarque. De même, le temps d’exécution de ** sur les flottants est plus élevé que celui des autres opérations
(mais raisonnable grâce à l’algorithme d’exponentiation rapide).
Remarque.
I On préfèrera donc L.append(x) à L+=[x] et on évitera d’utiliser L.pop(i) alors que L.pop() on peut.
I Idem pour les tranches, ou les copies de listes, on évite si on peut faire autrement.
(Dans les algorithmes de tri par exemple.)
I Les commandes analogues sur les chaı̂nes de caractères ou dictionnaires ont la même complexité.
Attention à l’exception de la commande x in D de complexité O(1) grâce aux fonctions de hachages (voir cours
de PSI).
COMPLEXITÉ SPATIALE
I C’est la mesure de l’espace mémoire maximal occupé durant l’exécution de l’algorithme.
I On définit l’unité d’espace mémoire comme la taille d’une structure de données particulières.
I On mesure la complexité en espace par le nombre n d’unités de mémoire occupées lors de l’exécution de
l’algorithme.
Tableau de nombres de longueur Unité d’espace mémoire Espace mémoire occupé par le tableau
n taille d’un entier/flottant O(n)
5n taille d’un entier/flottant O(n)
m×n taille d’un flottant/flottant O(n × m)
Exemple. Une image en 4K est une image rectangulaire de 4000 pixels sur 8000 pixel. Chaque pixel a un code
RGB stocké sur un triplet d’entiers entre 0 et 255.
La complexité spatiale de cette image (en bits) est : 4000 × 8000 × 3 × 8 = 7, 68 × 108 bits.
La complexité spatiale de cette image (en Mo) est : 4000 × 8000 × 3 : 106 = 7, 68 × 108 = 96 Mo.
• Un premier exemple montrant 4 façons de calculer la même quantité avec différentes complexités :
Calcul mathématique Code Python Complexité
n × 2 × (1 + n) × n 1 n = 100
Sn = Res = n *2*(1+ n )* n /2 O(1)
2 2
Sn = n2 (n + 1) 3 print ( Res )
n
X
Sn = 2×n×i
1 Res = 0
i=1
n
X 2 n = 100
Sn = 2n × i 3 for i in range (1 , n +1): O(n)
i=1 4 # Somme des termes de 1 à n
n(n + 1) 5 Res = Res + 2* n * i
Sn = 2n × 6 print ( Res )
2
Sn = n2 (n + 1)
Xn X n
Sn = (i + j)
i=1 j=1 1 Res = 0
n X
n n X
n
X X 2 n = 100
Sn = i+ j 3 for i in range (1 , n +1):
i=1 j=1 i=1 j=1 4 # Somme des termes de 1 à n O(n2 )
n n
X X n(n + 1) 5 for j in range (1 , n +1):
Sn = n×i+ 6 Res = Res + i + j
2
i=1 i=1 7 print ( Res )
Sn = n × n(n+1)
2 +n× n(n+1)
2
Sn = n2 (n + 1)
1 Res = 0
n n
X X 2 n = 100
Sn = n×i+ n×j 3 for i in range (1 , n +1):
i=1 j=1 4 Res += n * i O(n)
Sn = n × n(n+1)
2 +n× n(n+1)
2
5 for j in range (1 , n +1):
Sn = n2 (n + 1) 6 Res += n * j
7 print ( Res )
1 def rec ( n ):
2 if n ==0: Soit C(n) la complexité de rec à l’appel n :
3 return 1
4 else : C(0) = 1
5 if rec (n -1) < 1: C(n + 1) = 2C(n) + 5
6 U = rec (n -1) + 1 (Suite arithmético-géométrique)
7 else : C(n) + 5 = 2n (C(0) + 5) ⇔ C(n) = 6 × 2n + 5
8 U = rec (n -1) / 2 ⇒ Complexité exponentielle en O(2n )
9 return U
Remarque.
I Tout commande compte pour une ou plusieurs « opération(s) » (même l’affectation =).
I Le but n’est pas d’évaluer exactement le nombre d’opérations mais de savoir si celui-ci est en O(1), O(ln(n)),
O(n), O(n2 ), ou O(2n ).
I Pour les algorithme récursifs, l’idée est surtout de savoir combien de fois ils font appel à eux-même à l’étape
précédente mais aussi combien d’opérations sont faites entre deux appels successifs.
Remarque.
I L’idée est compter ici le nombre total d’appels p à la fonction en fonction de n.
n
I L’algorithme s’appelle jusqu’à ce que 2p ≈ 1 soit p ≈ log2 (n) donc la complexité est en O(ln(n)).
GRAPHES : GÉNÉRALITÉS
I Graphes orientés
• Un graphe orienté d’ordre n noté G est un
ensemble de n points ou sommets, reliés entre
eux ou non par des flèches appelées arc.
• Un sommet peut être relié à lui-même par une
boucle.
• Un graphe sans boucle est appelé graphe simple.
• Le même graphe admet une infinité de représen-
Trois représentations d’un même graphe
tations.
• Les arcs sont donc des arêtes orientées.
I Graphes pondérés
Un graphe pondéré est un graphe, orienté ou non, pour
lequel chaque arête possède un poids.
Remarque.
• Généralement le poids est un nombre réel positif ou nul.
• On peut mettre le poids à 0 pour signifier que deux sommets
ne sont pas reliés.
• Le poids peut représenter le temps de parcours entre deux
sommets, le prix du parcours... Un exemple de graphe pondéré
• Le degré d(s) d’un sommet s est est le nombre d’arêtes/d’arcs (quel que soit leur sens) auxquels il est relié
(une boucle compte deux fois).
Pour un graphe orienté, on définit le degré entrant et le degré sortant.
− On note d− (s) le degré entrant du sommet s : nombre d’arcs ayant s comme extrémité finale.
− On note d+ (s) le degré sortant du sommet s : nombre d’arcs ayant s comme extrémité initiale.
− Le degré du sommet s est donc d(s) = d+ (s) + d− (s)
Dans un graphe, la somme des degrés de chaque sommet est égale au double du nombre d’arêtes.
• Un graphe est complet lorsque toutes les sommets sont deux à deux adjacentes entre eux.
• Dans un graphe non orienté, une chaine de longueur n est une séquence finie de sommets reliés entre eux
par n arêtes. (L’extrémité de chacune est l’origine de la suivante, sauf pour la dernière.)
• Dans un graphe orienté, on parle de chemin et il suit le sens des flèches.
• Un chemin ou une chaı̂ne est qualifié de simple s’il n’emprunte pas deux fois la même arête.
• Un chemin est élémentaire s’il ne passe pas deux fois par le même sommet.
• Si une chaı̂ne/un chemin possède le même sommet de départ et d’arrivée, on dit qu’elle/il est fermé(e).
• Un chemin simple fermé s’appelle une cycle/circuit.
• Pour un graphe pondéré, la longueur/poids d’un(e) chaı̂ne/chemin est la somme des poids qui la compose.
• La distance entre deux sommets d’un graphe est la longueur du chemin/circuit le plus court (s’il y en a un)
reliant ces deux sommets.
• Un graphe non orienté est connexe s’il existe une chaı̂ne entre chaque paire de sommets distincts.
• Pour les graphes orientés on parle de : (mais il semble que ce n’est pas au programme)
− graphe orienté connexe si le graphe non orienté associé est connexe (graphe obtenu en ne tenant pas
compte du sens des arêtes).
− graphe orienté fortement connexe si pour toute paire (A, B) de sommets, il existe un chemin de A
vers B et un chemin de B vers A.
Exemple. Soit G le graphe ci-contre.
• G est non orienté d’ordre 64.
• Le sommet 3 est isolé.
• Le sommet 11 est de degré 3.
• 1 − 9 − 10 − 11 − 19 − 20 − 28 − 29 est une chaine
de longueur 7.
• 54 − 55 − 56 − 64 − 63 − 62 − 54 est un cycle.
• 1 − 2 − 10 − 9 − 1 − 2 − 10 − 9 − 1 n’est pas un
cycle.
• La distance entre 1 et 8 est de 11
• G n’est pas connexe.
• G admet 8 composantes connexes.
(On dit que deux sommets sont dans la même
« composante connexe » lorsqu’il existe un chemin
reliant ses deux sommets.
Le graphe est ainsi séparé en un certain nombre
de composantes connexes distinctes.)
Remarque.
• On a besoin d’une liste de noms de sommets et d’une liste des arêtes/arcs/sommets voisins.
• Notez que pour {si , sj } (arête) l’ordre ne compte pas alors que pour (si , sj ) (arc) l’ordre compte.
I Matrice d’adjacence
Soit un graphe de n sommets, d’indices 0, . . . , n − 1.
• La matrice d’adjacence de ce graphe est un tableau G à deux dimensions, de taille n × n, contenant des
booléens G[i][j] (0 ou 1) indiquant si il y a adjacence entre les sommets d’indices i et j.
(Attention : G[i][j] pour un arc de i vers j : i → j et G[j][i] pour un arc de j vers i : j → i)
• Pour les graphes pondérés, on parle de matrice des distances, où le coefficient G[i][j] vaut le poids de
l’arête/arc reliant le somme si au sommet sj .
I Dictionnaire d’adjacence
Soit un graphe de n sommets ayant des noms distincts.
Le dictionnaire d’adjacence de ce graphe est un dictionnaire où chaque élément est un sommet et la clef
associée est la liste des sommets adjacents à celui-ci.
Remarque.
• On n’a pas besoin de stocker les noms des sommets dans un dictionnaire à part.
• S’il y a peu d’arête, la taille du dictionnaire est bien inférieur à celle de la matrice d’adjacence.
• Pour un graphe pondéré le dictionnaire d’adjacence est un dictionnaire où chaque élément est un sommet
et la clef associée est la liste des sommets adjacents à celui-ci couplé avec le poids de l’arête correspondante.
I Degré entrant d’un sommet (matrice) I Degré entrant d’un sommet (dictionnaire)
1 def dmoins (G , s ): 1 def dmoins (G , s ):
2 dm =0 2 dm =0
3 for i in range ( Ordre ( G )): 3 for g in G :
4 if G [ i ][ s ]!=0: 4 if s in G [ g ]:
5 dm +=1 5 dm +=1
6 return dm 6 return dm
I Exemple
• Début : Sommet A (père). Parcours :
fils de A : B et D (distance 1) A
• fils de B : C (distance 2) A−B−D
fils de D : E (distance 2) A−B−D−C
• fils de C : F (distance 3) A−B−D−C −E
fils de E : F déjà parcouru. A−B−D−C −E−F
• G n’est pas atteignable.
Remarque.
• L’essentiel est de comprendre que la variable file créé avec la commande deque se gère comme une liste sauf
que la commande file.popleft() a une complexité en O(1) tandis que celle de L.pop(0) est en O(len(L)).
• Il existe également des commandes pour enfiler à gauche ou défiler.
I Algorithme itératif
I Algorithme itératif
Remarque.
• Cet algorithme ne renvoie pas exactement le même parcours en profondeur que la version récursive : en effet
dans la boucle for v in Voisins(G,s), l’instruction pile.append(v) ajoute les sommets par ordre croissant
d’indice, alors que les appels récursifs les ajoute par ordre décroissant.
• Au final cela ne change pas grand chose : on a quand même parcouru le graphe en profondeur...
ALGORITHME DE DIJKSTRA
ALGORITHME A?