Lycée Berthollet MP/MP* 2016-17 TP d`algorithmique NOTA BENE

publicité
Lycée Berthollet
MP/MP* 2016-17
TP d’algorithmique
NOTA BENE : Pour chaque algorithme décrit, on évaluera rapidement un ordre de grandeur de la
complexité temporelle dans le pire des cas et de la complexité spatiale.
Exercice 1 (Syntaxe Python). Pour se remettre en mémoire la syntaxe du langage, on propose l’activité
suivante : charger dans pyzo le fichier pythex.py qui se trouve dans le répertoire Cours de la page web
http://mathspcsi1bertho.free.fr/IPT2/, puis l’exécuter ligne par ligne en essayant de deviner le
résultat à chaque étape et comprendre les différences avec le résultat réel.
Exercice 2 (Numération). Le but est ici d’écrire des fonctions permettant d’afficher l’écriture d’un entier
naturel dans une base b et de lire un entier naturel écrit en base b. Pour alléger la rédaction, on appellera
simplement nombres les nombres entiers naturels.
Conventions : on distinguera l’écriture sous forme de chaîne de caractères, c’est-à-dire qu’on pourra lire
au clavier ou afficher à l’écran (comme par exemple la chaîne “1F” qui représente le nombre trente-et-un en
base seize) de l’écriture théorique en base b qui sera une liste de chiffres, les chiffres étant eux-mêmes des
nombres compris au sens large entre 0 et b − 1. Reprenant l’exemple précédent, l’écriture théorique en base
seize du nombre trente-et-un sera la liste Python [15,1], avec la convention que le chiffre correpondant à
bk se trouve en position k dans la liste (se rappeler que les listes commencent à l’indice 0 en Python). Pour
simplifier les choses, l’écriture sous forme de chaîne sera simplement appelée chaîne et celle sous forme de
liste sera appelée liste. De plus, pour nous l’écriture en base b d’un nombre aura toujours un chiffre “de plus
haut poids” (i.e. correspondant à la plus grande puissance de b) non nul, sauf pour le cas de zéro qui s’écrira
avec un seul chiffre, 0. Par ailleurs, pour des raisons pratiques de représentation des chiffres strictement
plus grands que 9 par des lettres, on se limitera à des bases b inférieures ou égales à 36.
On supposera dans un premier temps que les arguments donnés aux fonctions sont valides et on remet à
plus tard le traitement des erreurs correspondant à des arguments invalides.
1. Écrire une fonction lireListe(liste,b) qui lise une liste liste et retourne le nombre dont l’écriture
théorique en base b est la liste en question. On utilisera pour cela la factorisation de Hörner.
Tester cette fonction sur les listes suivantes : [15,1] en base 16, [1,0,1,1] en base 2, [0] en base 7,
[0,1,2,3,4,5,6,7,8,9] en base 10.
2. Écrire une fonction ecrireListe(nombre,b) qui retourne une liste représentant le nombre nombre
en base b. La tester en la composant avec la fonction précédente dans un sens et dans l’autre sur différents
exemples.
On s’intéresse maintenant à la traduction des chaînes de caractères en listes de chiffres et vice versa.
3. Écrire une fonction convCaract(caract) qui convertisse un caractère caract qui est supposé être soit
un chiffre décimal (’0’–’9’), soit une lettre capitale (’A’–’Z’), en un nombre compris entre 0 et 35, suivant
la convention : A représente 10, B représente 11, etc. Le résultat de cette fonction est donc le nombre en
question. Pour cela, on utilisera la fonction ord(caract) qui retourne le code ASCII du caractère caract,
en se référant à une table ASCII pour trouver les valeurs qui nous intéressent. Tester cette fonction.
4. Écrire une fonction lireChaine(chaine) qui lise une chaîne de caractères chaine et retourne la liste
correspondant à cette chaîne. Remarquez que la chaîne et la liste sont dans l’ordre inverse l’une de l’autre.
Tester cette fonction.
5. Écrire une fonction convChiffre(nombre) qui convertisse un nombre compris entre 0 et 35 en un
caractère qui soit un chiffre décimal (’0’–’9’) ou une lettre capitale (’A’–’Z’) suivant la convention évoquée
précédemment. On utilisera la fonction chr(nb) qui retourne le caractère de code ASCII nb. Tester cette
fonction à l’aide de la fonction convCaract.
6. Écrire une fonction ecrireChaine(liste) qui lise une liste liste et retourne la chaîne correspondant
à cette liste. Tester alors toutes les fonctions par des compositions multiples et écrire un convertisseur
permettant de passer de l’écriture dans une base à l’écriture dans une autre base.
7. Introduire pour chaque fonction un contrôle de la validité des arguments ainsi que le traitement des
erreurs correspondantes (messages d’erreurs).
Exercice 3 (Relations-applications). Le but est ici de tester les différentes propriétés d’une part de relations
binaires sur des ensembles finis et d’autre part d’applications entre ensembles finis.
Tout ensemble fini de cardinal n étant en bijection avec l’intervalle d’entiers [[0, n − 1]], on se limitera
aux ensembles de ce type.
Important : Toutes les fonctions devront être testées sur des exemples que vous construirez vousmême afin de parcourir les différents cas de figure. Ces exemples seront écrits dans le même fichier que les
fonctions Python, pour ne pas avoir à les rentrer manuellement à chaque test.
1. On représente les relations binaires par des listes de listes (tableaux bidimensionnels) de nombres entiers
valant 0 ou 1 suivant que les éléments sont en relation ou pas. Par exemple, le tableau bidimensionnel
R = [[1,0,1],[0,0,0],[1,1,0]]
représente la relation binaire R définie sur [[0, 2]] par 0R 0, 0R
6 1, 0R 2, 1R
6 0, 1R
6 1, 1R
6 2, 2R 0, 2R 1 et 2R
6 2.
Le (i, j)-ème élément du tableau R[i][j] vaut donc 0 si iR
6 j et 1 si iR j.
Écrire des fonctions prenant en argument un tableau carré R de 0 et de 1 et retournant True ou False
suivant que la relation est ou n’est pas :
— réflexive (∀x ∈ [[0, n − 1]], xR x)
— symétrique (∀x, y ∈ [[0, n − 1]], (xR y =⇒ yR x))
— antisymétrique (∀x, y ∈ [[0, n − 1]], ((xR y et yR x) =⇒ x = y))
— transitive (∀x, y, z ∈ [[0, n − 1]], ((xR y et yR z) =⇒ xR z))
— une relation d’équivalence (réflexive, symétrique et transitive)
— une relation d’ordre (réflexive, antisymétrique et transitive)
— une relation d’ordre total (relation d’ordre telle que ∀x, y ∈ [[0, n − 1]], (xR y ou yR x))
— une application (∀x ∈ [[0, n − 1]], ∃ !y ∈ [[0, n − 1]], xR y)
2. Une application entre deux ensembles est définie par son ensemble de départ [[0, n − 1]], son ensemble
d’arrivée [[0, p − 1]] et les images des éléments de l’ensemble de départ. Plutôt qu’avec des tableaux bidimensionnels comme dans la question précédente, il est plus efficace de représenter les applications par
des listes unidimensionnelles, le premier élément étant l’image de 0, le deuxième l’image de 1, etc. Pour
signifier l’ensemble d’arrivée, il faut alors passer aux fonctions Python un deuxième argument : l’entier p.
Ainsi la liste et l’entier
F = [2,3,4,2,7,5,6,8,8,8,0,1], P = 9
représentent l’application f de [[0, 11]] vers [[0, 8]] telle que f (0) = 2, f (1) = 3,. . . f (11) = 1.
Écrire des fonctions prenant en argument une liste F d’entiers naturels et un entier naturel P et retournant
True ou False suivant que l’application est ou n’est pas :
— surjective (∀y ∈ [[0, p − 1]], ∃ x ∈ [[0, n − 1]], f (x) = y)
— injective (∀x, y ∈ [[0, n − 1]], ( f (x) = f (y) =⇒ x = y))
— bijective (∀y ∈ [[0, p − 1]], ∃ !x ∈ [[0, n − 1]], f (x) = y, ce qui équivaut à être surjective et injective)
3. Écrire une fonction calculant la composée de deux applications passées en arguments et en écrire une
autre calculant l’application réciproque d’une bijection, puis tester la deuxième à l’aide de la première.
Exercice 4 (Problème de Caligula (qui comme chacun le sait était un fou sanguinaire)).
Caligula décide de s’amuser : il prend n personnes au hasard et décide de les tuer toutes sauf une. Pour
faire durer le plaisir, il décide de procéder de la manière suivante : il numérote les personnes de 0 à n − 1
et les fait disposer en cercle autour de lui dans l’ordre des numéros. Il choisit un nombre q quelconque
puis, partant du numéro zéro, se décale q fois d’une personne en partant dans l’ordre croissant et faisant
éventuellement plusieurs tours. Il tue alors la personne en face de lui. Le corps enlevé, il recommence le
2
procédé jusqu’à ce qu’il ne reste plus qu’une personne en vie, qu’il épargne alors généreusement. Écrire
une fonction survivant(n,q) qui retourne le numéro de la personne qui sera épargnée.
Exercice 5 (Graphes). On travaille ici sur les graphes finis non orientés : un tel graphe est la donnée d’un
ensemble fini de n sommets (qu’on prendra toujours égal à [[0, n − 1]]) associée à la donnée d’arêtes non
orientées pouvant joindre deux sommets différents. Deux sommets joints par une arête sont dits voisins.
On se limitera au cas sans arêtes multiples entre deux sommets, donc ce graphe pourra être représenté
par une matrice A = (ai, j )(i, j)∈[[0,n−1]]2 de taille n × n à coefficients valant 0 ou 1 de la manière suivante : le
coefficient ai, j vaut 1 si les sommets i et j sont reliés par une arête et 0 sinon. Les élément diagonaux sont
nuls et la matrice est symétrique. Cette matrice est appelée la matrice d’adjacence du graphe.
On représente ici les matrices en Python par des listes de listes (tableaux bidimensionnels) comme
a = [[0,1,1,0],[1,0,0,1],[1,0,0,0],[0,1,0,0]]
1. Dessiner à la main le graphe représenté par cette matrice d’adjacence. Dessiner un graphe quelconque,
expliciter sa matrice d’adjacence et faites-là vérifier par votre voisin.
2. Écrire une fonction adjacence(a) qui retourne True si l’objet python nommé a est une matrice d’adjacence et False sinon.
3. On appelle valence d’un sommet son nombre de voisins. Écrire une fonction val(a,s) qui retourne la
valence du sommet s du graphe dont la matrice d’adjacence est a, puis une fonction valMax(a) qui retourne
la valence maximale des sommets de ce graphe. Écrire aussi une fonction regulier(a) qui retourne True
si le graphe est régulier, i.e. si tous les sommets ont la même valence, et False sinon.
4. ? On dit que le graphe est connexe si deux sommets quelconques peuvent toujours être joints par une
suite finie d’arêtes (un telle suite est appellée chemin). Écrire une fonction connexe(a) qui retourne True
si le graphe est connexe et False sinon.
5. ? On dit que le graphe est simplement connexe s’il est connexe et s’il n’existe aucun chemin sans allerretours joignant un point à lui-même (un chemin comporte un aller-retour s’il utilise deux fois de suite la
même arête). Écrire une fonction simplementConnexe(a) qui retourne True si le graphe est simplement
connexe et False sinon.
Exercice 6
1. Écrire une fonction intersect(list1,list2) qui compare deux listes list1 et list2 et retourne
TRUE si les deux listes ont un élément en commun et FALSE sinon.
2. ?? On ne considère ici que des listes de nombres entiers pris dans l’intervalle [[1, 8]]. On suppose définie
la fonction de la question précédente dont on se servira directement ici. Écrire une fonction f(list) qui
affiche toutes les listes de même taille que la liste list qui n’ont aucun élément en commun avec list.
Exercice 7 ? ? ? (Permutations). Écrire une fonction permutations(n) qui liste à l’écran les permutations
de n éléments, chaque permutation étant listée exactement une fois. Par commodité, chaque permutation
sera décrite sur une ligne séparée.
3
Téléchargement