DS 2 : X PSI-PT 2015¶
2ème DS, algorithmique sur des tableaux 1D et 2D, et simulations probabilistes.
Correction du DS de vendredi 11 décembre 2015.
Voici ci-dessous la documentation de la correction complète du sujet d’écrit d’informatique (Polytechnique et ENS, 2015), pour PSI et PT.
Ce sujet d’écrit a été posé en devoir écrit surveillé (DS) pour le cours d’informatique pour tous en prépa MP au Lycée Lakanal.
Veuillez consulter le code source pour tous les détails svp. Ci dessous se trouve simplement la documentation de ce programme.
- Date : Vendredi 11 décembre 2015,
- Auteur : Lilian Besson, pour le cours d’informatique pour tous en prépa MP (http://perso.crans.org/besson/infoMP/),
- Licence : MIT Licence (http://lbesson.mit-license.org).
Note
Cette correction contient beaucoup de documentation, des exemples et des détails. Pour une correction beaucoup plus concise, voir cette page.
Avertissement
Les fonctions sont ici listées par ordre alphabétique, et pas dans l’ordre du fichier source. D’habitude, j’arrive à corriger ça, mais là non. Désolé. Je trouverai comment régler ce problème !
-
X_PSI_PT__2015.
creerTableau
(n)[source]¶ (Préliminaires) On implémente cette fonction avec un tableau Python (de type
list
).- Créé un tableau vide de taille
n
et le renvoie. - Elle n’était pas demandée, mais supposée donnée.
- Les valeurs initiales du tableau sont arbitraires (
None
en pratique). - Exemple :
>>> n = 5; t = creerTableau(n) >>> for i in range(n): ... t[i] = i + 2 >>> affiche(t) [2, 3, 4, 5, 6]
- Créé un tableau vide de taille
-
X_PSI_PT__2015.
creerTableau2D
(p, q)[source]¶ (Préliminaires) Exemple de création d’un tableau bi-dimensionnel, de taille \(p \times q\), ie. un tableau
a
dep
tableaux de tailleq
.- Elle n’était pas demandée, mais est donnée ici à titre d’illustration,
- Exemple :
>>> p = 2; q = 2; >>> a = creerTableau2D(p, q) >>> for i in range(p): ... for j in range(q): ... a[i][j] = i+j >>> affiche(a) [[0, 1], [1, 2]]
-
X_PSI_PT__2015.
affiche
(*args)[source]¶ (Préliminaires) Affiche les arguments un à un, séparés par des espaces.
- Elle n’était pas demandée, mais supposée donnée,
- Exemple :
>>> x = 1; y = x+1 >>> affiche("x = ",x," et y = ",y) x = 1 et y = 2
-
X_PSI_PT__2015.
creerListeVide
(n)[source]¶ (Question 1) Crée, initialise et renvoie un tableau de longueur
n + 1
correspondant à la liste vide ayant une capacité den
éléments.- Chaque case du tableau est initialisé à
None
pour garder trace qu’elle est vide, - Exemple :
>>> n = 5; liste = creerListeVide(n) >>> affiche(liste) [0, None, None, None, None, None]
- Chaque case du tableau est initialisé à
-
X_PSI_PT__2015.
estDansListe
(liste, x)[source]¶ (Question 2) Teste si l’élement x apparaît dans la représentée par le tableau
liste
(répondTrue
ouFalse
).- Complexité en temps : \(O(n)\) (dans le pire des cas, car on doit alors parcourir tout le tableau),
- Complexité en mémoire : \(O(1)\) (toujours),
- Exemple :
>>> # Exemple d'une liste non ordonnée contenant 1,2,3,4,5 >>> liste = [5, 5, 4, 2, 1, 3] >>> affiche(estDansListe(liste, 0)) False >>> affiche(estDansListe(liste, 5)) True >>> affiche(estDansListe(liste, 194652)) False
-
X_PSI_PT__2015.
ajouteDansListe
(liste, x)[source]¶ (Question 3) Modifie de façon appropriée le tableau
liste
pour y ajouterx
, si l’entierx
n’appartient pas déjà à la liste, et ne fait rien sinon.- Cette procédure ne fait rien du tout si la liste est pleine initialement (Le sujet ne demandait pas de traiter ce cas),
- Complexité en temps : \(O(n)\) (dans le pire des cas, car on doit alors parcourir tout le tableau),
- Complexité en mémoire : \(O(1)\) (toujours),
- Exemple :
>>> n = 5; liste = creerListeVide(n) >>> for i in range(1, n+1): ... ajouteDansListe(liste, 4**i) >>> affiche(liste) [5, 4, 16, 64, 256, 1024] >>> # Très similaire à un ensemble défini par compréhension : >>> ensemble = { 4**i for i in range(1, n+1)} >>> affiche(ensemble) # L'ordre est arbitraire ! set([16, 64, 4, 256, 1024]) {16, 64, 256, 1024, 4}
- On peut vérifier qu’ajouter un élément quand la liste est déjà pleine ne marche pas (la liste n’est pas modifiée) :
>>> ajouteDansListe(liste, 2015) Impossible d'ajouter l'élément x = 2015 à la liste [5, 4, 16, 64, 256, 1024] qui est déjà pleine (n = 5).
- Note : ce message d’erreur n’était pas demandée.
-
X_PSI_PT__2015.
creerPlanSansRoute
(n)[source]¶ (Question 5) Crée, remplit et renvoie le tableau de tableaux correspondant au plan à
n
villes n’ayant aucune route.- Complexité en temps : \(O(n)\) (toujours),
- Complexité en mémoire : \(O(n)\) (toujours),
- Exemple :
>>> n = 4; plan = creerPlanSansRoute(n) >>> affiche(plan) [[4, 0], [0, None, None, None, None, None], [0, None, None, None, None, None], [0, None, None, None, None, None], [0, None, None, None, None, None]]
- Note : les
None
affichées plus haut correspondent aux*
dans le sujet.
-
X_PSI_PT__2015.
estVoisine
(plan, x, y)[source]¶ (Question 6) Renvoie
True
si les villesx
ety
sont voisines dans le plan codé par le tableau de tableauxplan
, et renvoieFalse
sinon.- On utilise une structure qui peut représenter des graphes dirigés (ie. non symétrique, on peut avoir \(x \to y\) sans avoir \(y \to x\)), mais tout le reste du sujet n’utilisera que des graphes non dirigés (tous les chemins sont symétriques, donc \(x \sim y \Leftrightarrow x \to y \;\;\text{et}\;\; y \to x\)),
- Complexité en temps : \(O(n)\) (dans le pire des cas, car on doit alors parcourir toutes les voisines de
x
et dey
), - Complexité en mémoire : \(O(1)\) (toujours),
- Exemple :
>>> plan = [[5, 4], [1, 2, None, None, None], [3, 1, 3, 4, None], [1, 2, None, None, None], [2, 2, 5, None, None], [1, 4, None, None, None]] # plan2, cf ci-dessus >>> x = 1; y = 2 >>> affiche(estVoisine(plan, x, y)) True >>> x = 1; y = 5 >>> affiche(estVoisine(plan, x, y)) False
-
X_PSI_PT__2015.
ajouteRoute
(plan, x, y)[source]¶ (Question 7) Modifie le tableau de tableaux
plan
pour ajouter une route entre les villesx
ety
si elle n’était pas déjà présente et ne fait rien sinon.- Le sujet disait “On prendra garde à bien mettre à jour toutes les cases concernées dans le tableau de tableaux ``plan``”, ce qui montre bien qu’ils veulent garder le graphe symétrique (cf. explication de la fonction précédente).
- Le sujet demandait “Y a-t-il un risque de dépassement de la capacité des listes ?”, et non car on les a initialisé avec une capacité de
n+1
(cf.creerPlanSansRoute()
), - Complexité en temps : \(O(n)\) (dans le pire des cas, car on doit alors parcourir toutes les voisines de
x
et dey
), - Complexité en mémoire : \(O(1)\) (toujours),
- Exemple :
>>> plan = [[5, 4], [1, 2, None, None, None], [3, 1, 3, 4, None], [1, 2, None, None, None], [2, 2, 5, None, None], [1, 4, None, None, None]] # plan2, cf ci-dessus >>> affiche("Ce plan a m = ", plan[0][1], " route(s).") Ce plan a m = 4 route(s). >>> x = 1; y = 5 >>> affiche(estVoisine(plan, x, y)) False >>> ajouteRoute(plan, x, y) >>> affiche("Ce plan a m = ", plan[0][1], " route(s).") Ce plan a m = 5 route(s). >>> affiche(estVoisine(plan, x, y)) True
-
X_PSI_PT__2015.
afficheToutesLesRoutes
(plan)[source]¶ (Question 8) Affiche à l’écran la liste des routes du plan codé par le tableau de tableaux
plan
, où chaque route apparaît exactement une seule fois.- Par exemple, pour le graphe codé par le tableau de tableaux de la figure 1, cette procédure affiche à l’écran :
>>> plan = [[5, 4], [1, 2, None, None, None], [3, 4, 1, 5, None], [0, None, None, None, None], [2, 2, 5, None, None], [2, 4, 2, None, None]] >>> afficheToutesLesRoutes(plan) Ce plan contient 4 route(s): (1-2) (2-4) (2-5) (4-5)
Le sujet demandait “Quelle est la complexité en temps de votre procédure dans le pire cas en fonction de n et m ?” :
- Complexité en temps : \(O(n^3)\) (dans le pire des cas, car on doit alors parcourir tous les couples de sommets
(x, y)
, vérifier à chaque fois s’ils sont connectés en \(O(n)\)), - Note : il me semble possible de pouvoir faire mieux, mais l’algorithme deviendrait bien plus compliqué (sans utiliser
estVoisine()
mais un parcours malin des listes d’adjacence), - Complexité en mémoire : \(O(m)\) (on doit stocker la chaîne de caractère des
m
routes(i-j)
).
- Complexité en temps : \(O(n^3)\) (dans le pire des cas, car on doit alors parcourir tous les couples de sommets
Plan 1 :
Plan 2 :
-
X_PSI_PT__2015.
entierAleatoire
(k)[source]¶ (Préliminaires partie III) Renvoie un entier aléatoire uniforme entre
1
etk
.C’est à dire qu’on a \(\mathbb{P}r\{ \mathtt{entierAleatoire}(k) = c \} = 1/k\) pour tout entier \(c \in \{ 1, \dots, k \}\)), exactement comme demandé.
- Elle n’était pas demandée, mais supposée donnée,
- (Hypothèse) Complexité en temps : \(O(1)\) (toujours),
- (Hypothèse) Complexité en mémoire : \(O(1)\) (toujours),
- C’est exactement
random.randint()
... renommée en français. - Exemple :
>>> random.seed(0) # Pas d'aléa ici, pour la reproductibilité de l'exemple >>> entierAleatoire(10) 7 >>> entierAleatoire(2) 2
- Hypothèse : on suppose pour cette fonction et la suite qu’on dispose d’un “bon” générateur de nombres aléatoires (PRNG, Pseudo-Random Number Generator) qui assure (mathématiquement) que ces entiers sont tous indépendants, et que les tirages sont effectivement identiquement distribués. Voir la documentation Python du module random ou cette page Wikipédia pour plus d’informations.
-
X_PSI_PT__2015.
coloriageAleatoire
(plan, couleur, k, s, t)[source]¶ (Question 9) Génère un coloriage aléatoire pour le plan
plan
.- Prend en argument un plan
plan
den
villes, un tableaucouleur
de taillen + 1
, un entierk
, et deux villess
ett
(\(s,t \in \{ 1, \dots, n \}\)), - Remplit le tableau
couleur
avec une couleur aléatoire uniforme dans\{ 1, \dots, k \}
, choisie indépendamment pour chaque ville \(i \in \{ 1, \dots, n \} \setminus \{s, t\}\), - On impose les couleurs
0
etk + 1
pours
ett
respectivement, - La case
couleur[0]
n’est pas utilisée (elle reste égale àNone
, ou n’importe quelle valeur arbitraire). - Complexité en temps : \(O(n)\) (toujours),
- Complexité en mémoire : \(O(n)\) (toujours),
- Exemple :
>>> plan = [[5, 4], [1, 2, None, None, None], [3, 1, 3, 4, None], [1, 2, None, None, None], [2, 2, 5, None, None], [1, 4, None, None, None]] # plan2, cf ci-dessus >>> n = plan[0][0] >>> couleur = creerTableau(n+1) # Tableau vide >>> affiche(couleur) [None, None, None, None, None, None] >>> k = 2; s = 1; t = 5 >>> random.seed(0) # Pas d'aléa ici, pour la reproductibilité de l'exemple >>> coloriageAleatoire(plan, couleur, k, s, t) >>> affiche(couleur) [None, 0, 2, 2, 1, 3]
- Prend en argument un plan
-
X_PSI_PT__2015.
voisinesDeCouleur
(plan, couleur, i, c)[source]¶ (Question 10) Crée et renvoie un tableau codant la liste (sans redondance) des villes de couleur
c
voisines de la villei
dans le planplan
colorié par le tableaucouleur
.- Complexité en temps : \(O(n + m)\). On doit créer en \(O(n)\) la liste initialement vide des possibles voisines de i de couleurs c. Ensuite, pour chaque ville
j
voisine aveci
, on doit vérifier sicouleur[j] = c
(en temps constant \(O(1)\)), et le nombre \(\delta(i)\) de voisines de i est un \(O(m)\), cf.estVoisine()
, - Complexité en mémoire : \(O(n)\) (toujours),
- Exemple :
>>> plan = [[5, 4], [1, 2, None, None, None], [3, 1, 3, 4, None], [1, 2, None, None, None], [2, 2, 5, None, None], [1, 4, None, None, None]] # plan2, cf ci-dessus >>> n = plan[0][0] >>> couleur = [None, 0, 2, 2, 2, 3] # Un coloriage >>> i = 2 >>> c = 2 >>> affiche(voisinesDeCouleur(plan, couleur, i, c)) [2, 3, 4, None, None, None] >>> couleur = [None, 0, 2, 1, 2, 3] # Autre coloriage >>> i = 4 >>> c = 3 >>> affiche(voisinesDeCouleur(plan, couleur, i, c)) [1, 5, None, None, None, None]
- Complexité en temps : \(O(n + m)\). On doit créer en \(O(n)\) la liste initialement vide des possibles voisines de i de couleurs c. Ensuite, pour chaque ville
-
X_PSI_PT__2015.
voisinesDeLaListeDeCouleur
(plan, couleur, liste, c)[source]¶ (Question 11) Crée et renvoie un tableau codant la liste (sans redondance) des villes de couleur
c
voisines d’une des villes présente dans la liste (sans redondance)liste
dans le planplan
colorié par le tableaucouleur
.- Cette seconde fonction semble compliquée, mais c’est simplement une union sur les \(i \in \mathtt{liste}\) des
voisinesDeCouleur(plan, couleur, i, c)
donnée par la fonction d’avant ! - Complexité en temps : \(O(\# liste \times (n + m))\) (pour chaque ville
k
de la listeliste
, on appelle la fonction précédentevoisinesDeCouleur()
, qui est en temps \(O(n + m)\)), - Complexité en mémoire : \(O(n)\) (toujours),
- Exemple :
>>> plan = [[5, 4], [1, 2, None, None, None], [3, 1, 3, 4, None], [1, 2, None, None, None], [2, 2, 5, None, None], [1, 4, None, None, None]] # plan2, cf ci-dessus >>> n = plan[0][0] >>> couleur = [None, 0, 2, 2, 1, 3] # Un coloriage >>> liste = creerListeVide(n+1) # liste = {} ensemble vide >>> ajouteDansListe(liste, 1) # liste = {1} >>> ajouteDansListe(liste, 2) # liste = {1, 2} >>> c = 2 >>> affiche(voisinesDeLaListeDeCouleur(plan, couleur, liste, c)) [2, 2, 3, None, None, None]
- Dans ces deux exemples, on regarde tous les voisines de
1
ou de2
(en fait,liste
représente l’ensemble \(\{1,2\}\)), et on affiche ceux de couleurc = 2
(soit2
comme voisine de1
et3
comme voisine de2
), - Et ci-dessous, on affiche les voisines de
1
ou de2
qui sont de couleurc = 1
(soit uniquement4
comme voisine de2
).
>>> c = 1 >>> affiche(voisinesDeLaListeDeCouleur(plan, couleur, liste, c)) [1, 4, None, None, None, None]
- Cette seconde fonction semble compliquée, mais c’est simplement une union sur les \(i \in \mathtt{liste}\) des
-
X_PSI_PT__2015.
existeCheminArcEnCiel
(plan, couleur, k, s, t)[source]¶ (Question 12) Renvoie
True
s’il existe dans le planplan
, un chemin \(s \sim v_1 \sim \dots \sim v_k \sim t\), tel que \(\mathtt{couleur}[v_j] = j\) pour tout \(j \in \{ 1, \dots, k\}\), et renvoieFalse
sinon.- L’objectif de cette partie est d’écrire une procédure qui détermine s’il existe un chemin de longueur
k + 2
, allant des
àt
, dont laj
-ème ville intermédiaire a reçu la couleurj
. - Complexité en temps : \(O(n (n+m))\). On fait des appels à la fonction précédente
voisinesDeLaListeDeCouleur()
(qui était en \(O(n+m)\)) avec des listesliste
de tailles variables, mais dont la somme ne sera pas plus que le nombre de villesn
(en effet une ville ne peut être qu’une seule fois dans uneliste
). (C’est ce qu’on appelle une complexité cumulée.) - Complexité en mémoire : \(O(n)\) (toujours).
- Premier exemple, avec le plan de la figure 2, qui trouve un chemin arc-en-ciel (de taille
k = 3
) entres = 6
ett = 4
: \(6 \sim 7 \sim 8 \sim 3 \sim 4\) :
>>> plan = [[11, 13], [2, 2, 6], [2, 1, 7], [2, 4, 8], [2, 3, 8], [2, 6, 10], [3, 1, 5, 7], [3, 2, 6, 8], [4, 3, 4, 7, 9], [2, 8, 11], [2, 5, 11], [2, 9, 10]] >>> s = 6 >>> t = 4 >>> k = 3 >>> couleur = [None, 1, 2, 3, k+1, 1, 0, 1, 2, 3, 3, 1] >>> affiche(existeCheminArcEnCiel(plan, couleur, k, s, t)) True
- Le même exemple mais dans l’autre sens, qui trouve un chemin arc-en-ciel (de taille
k = 3
) entres = 4
ett = 6
: \(4 \sim 3 \sim 8 \sim 7 \sim 6\) :
>>> plan = [[11, 13], [2, 2, 6], [2, 1, 7], [2, 4, 8], [2, 3, 8], [2, 6, 10], [3, 1, 5, 7], [3, 2, 6, 8], [4, 3, 4, 7, 9], [2, 8, 11], [2, 5, 11], [2, 9, 10]] >>> s = 4 >>> t = 6 >>> k = 3 >>> couleur = [None, 3, 2, 1, 0, 3, k+1, 3, 2, 1, 1, 3] >>> affiche(existeCheminArcEnCiel(plan, couleur, k, s, t)) True
- Un autre exemple similaire, qui trouve un aussi chemin arc-en-ciel (de taille
k = 3
), entres = 11
ett = 7
: \(11 \sim 10 \sim 5 \sim 6 \sim 7\) :
>>> plan = [[11, 13], [2, 2, 6], [2, 1, 7], [2, 4, 8], [2, 3, 8], [2, 6, 10], [3, 1, 5, 7], [3, 2, 6, 8], [4, 3, 4, 7, 9], [2, 8, 11], [2, 5, 11], [2, 9, 10]] >>> s = 11 >>> t = 7 >>> k = 3 >>> couleur = [None, 1, 2, 3, 3, 2, 3, k+1, 2, 1, 1, 0] >>> affiche(existeCheminArcEnCiel(plan, couleur, k, s, t)) True
- Un autre exemple similaire, qui trouve un aussi chemin arc-en-ciel (de taille
k = 2
), entres = 11
ett = 7
: \(11 \sim 9 \sim 8 \sim 7\) :
>>> plan = [[11, 13], [2, 2, 6], [2, 1, 7], [2, 4, 8], [2, 3, 8], [2, 6, 10], [3, 1, 5, 7], [3, 2, 6, 8], [4, 3, 4, 7, 9], [2, 8, 11], [2, 5, 11], [2, 9, 10]] >>> s = 11 >>> t = 7 >>> k = 2 >>> couleur = [None, 1, 2, 3, 3, 2, 3, k+1, 2, 1, 1, 0] >>> affiche(existeCheminArcEnCiel(plan, couleur, k, s, t)) True
- Un autre exemple similaire, qui ne pas trouve de chemin arc-en-ciel (de taille
k = 3
), car le coloriage est “mauvais” :
>>> plan = [[11, 13], [2, 2, 6], [2, 1, 7], [2, 4, 8], [2, 3, 8], [2, 6, 10], [3, 1, 5, 7], [3, 2, 6, 8], [4, 3, 4, 7, 9], [2, 8, 11], [2, 5, 11], [2, 9, 10]] >>> s = 11 >>> t = 7 >>> k = 3 >>> couleur = [None, 1, 2, 3, 3, 2, 3, k+1, 2, 1, 1, 0] >>> # On change la couleur de la ville 6 (qui était dans le chemin arc-en-ciel précédent) >>> couleur[6] = 1 >>> affiche(existeCheminArcEnCiel(plan, couleur, k, s, t)) False
- Notez que le graphe étant complet, il y a toujours un chemin entre
s
ett
, mais pas forcément de chemin arc-en-ciel pour un certainsk
, etcouleur
.
- L’objectif de cette partie est d’écrire une procédure qui détermine s’il existe un chemin de longueur
-
X_PSI_PT__2015.
existeCheminSimple
(plan, k, s, t)[source]¶ (Question 13) Renvoie
True
avec probabilité au moins \(1 - 1/e\) s’il existe un chemin des
àt
passant par exactementk
villes intermédiaires toutes distinctes dans le planplan
, et renvoie toujoursFalse
sinon.- Exécute \(k^k\) fois la fonction
existeCheminArcEnCiel()
et renvoieTrue
dès qu’une de ses exécution a trouvé un coloriage (aléatoire) qui permet de trouver un chemin “arc-en-ciel”. - Complexité en temps : \(O(k^k n (n+m))\). Dans le pire des cas on fait \(k^k = \mathtt{nombre\_executions}\) essais avec
existeCheminArcEnCiel()
qui est en temps \(O(n (n+m))\) (et générer un coloriage est en temps \(O(n)\) donc négligeable), - Note : c’est bien la complexité annoncée, avec \(f(k) = k^k\) (le nombre d’essai, dépandant uniquement de
k
) et \(g(n,m) = n (n+m)\) (la taille duplan
). - Complexité en mémoire : \(O(n)\) (toujours).
Avertissement
C’est un algorithme probabiliste !
Il faut garder en tête que cette fonction n’est pas exacte mais probabiliste (on parle aussi d’un algorithme randomisé, et c’est un sujet à part entière, voir cette page par exemple ou celle-là) :
- Si elle dit
True
, c’est qu’il existe un chemin, et on en est sûr (voir la question d’après pour en trouver un, on dit alors qu’on fournit un certificat de réponse), et ça a pris au plus \(O(k^k n (n+m))\), - Si elle dit
False
, c’est peut-être vrai, avec une bonne probabilité, mais aucune garantie 100% (et ça a pris exactement \(O(k^k n (n+m))\)). - S’il existe un chemin, on a une bonne probabilité de le trouver (mais si on le trouve pas ce n’est pas une garantie qu’il n’y en a pas),
- S’il n’y a pas de chemin, on a une bonne probabilité de répondre
False
(mais jamais une garantie qu’il n’y en a pas).
- Un exemple, avec le plan de la figure 2, qui va trouver un chemin arc-en-ciel (de taille
k = 3
) entres = 6
ett = 4
: \(6 \sim 7 \sim 8 \sim 3 \sim 4\). On peut donc faire confiance à 100% à cette réponse positive !
>>> plan = [[11, 13], [2, 2, 6], [2, 1, 7], [2, 4, 8], [2, 3, 8], [2, 6, 10], [3, 1, 5, 7], [3, 2, 6, 8], [4, 3, 4, 7, 9], [2, 8, 11], [2, 5, 11], [2, 9, 10]] >>> s = 6 >>> t = 4 >>> k = 3 >>> random.seed(0) # Pas d'aléa ici, pour la reproductibilité de l'exemple >>> # Ca peut prendre du temps... >>> affiche(existeCheminSimple(plan, k, s, t)) True >>> # Mais on n'est pas sûr de cette réponse !
- Un deuxième exemple, avec un plan légèrement modifié inspiré de celui de la figure 2, où on enlève le lien \(8 \sim 3\), et donc on ne trouvra pas un chemin arc-en-ciel (de taille
k = 3
) entres = 6
ett = 4
. Notez qu’avec cette méthode, on ne peut pas faire confiance à 100% à cette réponse négative !
>>> plan = [[11, 12], [2, 2, 6], [2, 1, 7], [1, 4], [2, 3, 8], [2, 6, 10], [3, 1, 5, 7], [3, 2, 6, 8], [3, 4, 7, 9], [2, 8, 11], [2, 5, 11], [2, 9, 10]] >>> s = 6 >>> t = 4 >>> k = 3 >>> random.seed(0) # Pas d'aléa ici, pour la reproductibilité de l'exemple >>> # Ca peut prendre du temps... >>> affiche(existeCheminSimple(plan, k, s, t)) False >>> # Mais on n'est pas sûr de cette réponse !
- Hypothèse : on dispose d’un bon générateur de nombres aléatoires (PRNG, Pseudo-Random Number Generator) qui assure mathématiquement que ces exécutions sont toutes indépendantes, et que les coloriages sont effectivement uniformément distribués. Voir la documentation Python du module random ou cette page Wikipédia pour plus d’informations.
- Exécute \(k^k\) fois la fonction
-
X_PSI_PT__2015.
question14
()[source]¶ (Question 14) Le programme à modifier est existeCheminArcEnCiel. En un mot : il faut faire en sorte de garder en mémoire les listes liste successives.
Plus de détails :
- Voici une solution possible pour calculer le chemin, plus détaillée : utiliser un tableau des prédécesseurs
pred
, permettant de reconstituer le chemin a posteriori. - Plus précisément : on utilise un tableau
pred
àn
cases tel que pour toute villex
,pred[x]
est le numéro d’une villey
reliée àx
et qui précèdex
dans un éventuel chemin des `` à ``t
. - Ce tableau est créé et complété au fur et à mesure par la fonction
existeCheminArcEnCiel()
: lors de chaque appel à la fonction auxiliairevoisinesDeLaListeDeCouleur()
appliquée(plan, couleur, liste, j)
on met à jour le tableaupred
: pour chaque villex
de couleurj
voisine d’une villey
de la liste, on affectepred[x] = y
(il y a une route dey
àx
, et si le chemin passe parx
, il sera passé pary
juste avant). (pour cela, on ajoute le tableaupred
dans les arguments de la fonction auxiliairevoisinesDeLaListeDeCouleur()
et celle-ci effectue elle-même la mise à jour, en même temps qu’elle calcule la nouvelle liste de voisines). - À la fin, si un chemin est détecté, on le reconstitue
en partant de la fin (c’est classique) grâce au tableau
pred
:pred[t] = y1
,pred[y1] = y2
,pred[y2] = y3
, etc, jusqu’à retomber surs : pred[yk] = s
, alors le chemin ests -> yk -> ... -> y2 -> y1 -> t
(ce chemin est de longueurk+2
par construction).
Note
Exemple avec le plan de la figure 2 :
- À la 1e étape,
liste
contient1, 5, 7
(les voisins des=6
de couleur1
), on affectepred[1] = pred[5] = pred[7] = 6
. - À la 2e étape,
liste
contient2, 8
(les voisins de1, 5, 7
de couleur2
), on affectepred[2] = 1
(ou7
au choix),pred[8] = 7
. - À la 3e étape,
liste
contient3, 9
, on affectepred[3] = pred[9] = 8
. - À la 4e étape,
liste
contient4
, on affectepred[4] = 3
.
Pour notre exemple : en partant de la fin (
t=4
) on apred[4] = 3
,pred[3] = 8
,pred[8] = 7
,pred[7] = 6
le chemin reconstitué est6 -> 7 -> 8 -> 3 -> 4
, et on peut vérifier sur le plan de la figure 2 que c’est bien un chemin valide :Note
Cette fonction ne fait rien, elle donne juste la correction de la dernière question (14) du DS.
- Voici une solution possible pour calculer le chemin, plus détaillée : utiliser un tableau des prédécesseurs
Conseils¶
- Améliorez la présentation de vos copies,
- Essayez d’être encore plus attentif à la syntaxe de Python (il y a eu trop d’erreurs d’indentation et de
:
manquants), - Vous devez être plus attentif aux consignes de l’énoncé (certains élèves oublient de donner la complexité dans les dernières questions),
- Comme dans chaque concours/DS, vous devez essayer de “grapiller” des points là où vous peuvez (la Q09 (
X_PSI_PT__2015.coloriageAleatoire()
) et Q13 (X_PSI_PT__2015.existeCheminSimple()
) étaient faciles, par exemple), - Enfin, vous devez vous forcer à n’utiliser que les structures de données de l’énoncé (c’est flagrant, certains commencent par utiliser
X_PSI_PT__2015.creerTableau()
ouX_PSI_PT__2015.creerListeVide()
au début, puis finissent avec des[]
etlist.append()
à tout va), probablement par manque de concentration et à cause de la fatigue.
Sortie du script¶
$ python3 X_PSI_PT__2015.py
Correction du DS de vendredi 11 décembre 2015.
Voici ci-dessous la documentation de la correction complète du sujet d'écrit d'informatique (Polytechnique et ENS, 2015), pour PSI et PT.
Ce sujet d'écrit a été posé en devoir écrit surveillé (DS) pour le cours d'informatique pour tous en prépa MP au Lycée Lakanal.
Veuillez `consulter le code source pour tous les détails <_modules/X_PSI_PT__2015__minimal_solution.html>`_ svp.
Ci dessous se trouve simplement la *documentation* de ce programme.
- *Date :* Vendredi 11 décembre 2015,
- *Auteur :* Lilian Besson, pour le cours d'informatique pour tous en prépa MP (http://perso.crans.org/besson/infoMP/),
- *Licence :* MIT Licence (http://lbesson.mit-license.org).
.. note::
Cette correction contient beaucoup de documentation, des exemples et des détails.
Pour une correction beaucoup plus concise, voir `cette page <_modules/X_PSI_PT__2015__minimal_solution.html>`_.
.. warning::
Les fonctions sont ici listées par ordre alphabétique, et pas dans l'ordre du fichier source.
D'habitude, j'arrive à `corriger ça <http://sphinx-doc.org/ext/autodoc.html#confval-autodoc_member_order>`_, mais là non. Désolé.
Je trouverai comment régler ce problème !
Partie 0 : Fonctions supposées données
Partie I : Préliminaires : Listes dans redondance
Note : une liste non-ordonnée et sans redondance est exactement un set de Python, mais nous ne devons pas nous en servir.
Partie II. Création et manipulation de plans
Plan de la figure 1 :
plan0 = [[5, 4],
[1, 2, None, None, None],
[3, 4, 1, 5, None],
[0, None, None, None, None],
[2, 2, 5, None, None],
[2, 4, 2, None, None],
]
[[5, 4], [1, 2, None, None, None], [3, 4, 1, 5, None], [0, None, None, None, None], [2, 2, 5, None, None], [2, 4, 2, None, None]]
Premier plan :
plan1 = [[5, 7],
[2, 2, 3, None, None],
[3, 1, 3, 5, None],
[4, 1, 2, 4, 5],
[2, 3, 5, None, None],
[3, 2, 3, 4, None],
]
[[5, 7], [2, 2, 3, None, None], [3, 1, 3, 5, None], [4, 1, 2, 4, 5], [2, 3, 5, None, None], [3, 2, 3, 4, None]]
Deuxième plan :
plan2 = [[5, 4],
[1, 2, None, None, None],
[3, 1, 3, 4, None],
[1, 2, None, None, None],
[2, 2, 5, None, None],
[1, 4, None, None, None],
]
[[5, 4], [1, 2, None, None, None], [3, 1, 3, 4, None], [1, 2, None, None, None], [2, 2, 5, None, None], [1, 4, None, None, None]]
Partie III. Recherche de chemins arc-en-ciel
Plan de la figure 2 :
plan3 = [[11, 13],
[2, 2, 6],
[2, 1, 7],
[2, 4, 8],
[2, 3, 8],
[2, 6, 10],
[3, 1, 5, 7],
[3, 2, 6, 8],
[4, 3, 4, 7, 9],
[2, 8, 11],
[2, 5, 11],
[2, 9, 10],
]
[[11, 13], [2, 2, 6, None, None, None, None, None, None, None, None, None], [2, 1, 7, None, None, None, None, None, None, None, None, None], [2, 4, 8, None, None, None, None, None, None, None, None, None], [2, 3, 8, None, None, None, None, None, None, None, None, None], [2, 6, 10, None, None, None, None, None, None, None, None, None], [3, 2, 6, 8, None, None, None, None, None, None, None, None], [3, 1, 5, 7, None, None, None, None, None, None, None, None], [4, 3, 4, 7, 9, None, None, None, None, None, None, None], [2, 8, 11, None, None, None, None, None, None, None, None, None], [2, 5, 11, None, None, None, None, None, None, None, None, None], [2, 9, 10, None, None, None, None, None, None, None, None, None]]
Ce plan contient 13 route(s): (1-2) (3-4) (3-8) (4-8) (5-10) (8-9) (9-11) (10-11)
Partie IV. Recherche de chemin passant par exactement k villes intermédiaires distinctes
Test automatique de toutes les doctests écrites dans la documentation (docstring) de chaque fonction :
Trying:
x = 1; y = x+1
Expecting nothing
ok
Trying:
affiche("x = ",x," et y = ",y)
Expecting:
x = 1 et y = 2
ok
Trying:
plan = [[5, 4], [1, 2, None, None, None], [3, 4, 1, 5, None], [0, None, None, None, None], [2, 2, 5, None, None], [2, 4, 2, None, None]]
Expecting nothing
ok
Trying:
afficheToutesLesRoutes(plan)
Expecting:
Ce plan contient 4 route(s): (1-2) (2-4) (2-5) (4-5)
ok
Trying:
n = 5; liste = creerListeVide(n)
Expecting nothing
ok
Trying:
for i in range(1, n+1):
ajouteDansListe(liste, 4**i)
Expecting nothing
ok
Trying:
affiche(liste)
Expecting:
[5, 4, 16, 64, 256, 1024]
ok
Trying:
ensemble = { 4**i for i in range(1, n+1)}
Expecting nothing
ok
Trying:
affiche(ensemble) # L'ordre est arbitraire ! set([16, 64, 4, 256, 1024])
Expecting:
{16, 64, 256, 1024, 4}
**********************************************************************
File "X_PSI_PT__2015.py", line 165, in __main__.ajouteDansListe
Failed example:
affiche(ensemble) # L'ordre est arbitraire ! set([16, 64, 4, 256, 1024])
Expected:
{16, 64, 256, 1024, 4}
Got:
{16, 256, 64, 4, 1024}
Trying:
ajouteDansListe(liste, 2015)
Expecting:
Impossible d'ajouter l'élément x = 2015 à la liste [5, 4, 16, 64, 256, 1024] qui est déjà pleine (n = 5).
ok
Trying:
plan = [[5, 4], [1, 2, None, None, None], [3, 1, 3, 4, None], [1, 2, None, None, None], [2, 2, 5, None, None], [1, 4, None, None, None]] # plan2, cf ci-dessus
Expecting nothing
ok
Trying:
affiche("Ce plan a m = ", plan[0][1], " route(s).")
Expecting:
Ce plan a m = 4 route(s).
ok
Trying:
x = 1; y = 5
Expecting nothing
ok
Trying:
affiche(estVoisine(plan, x, y))
Expecting:
False
ok
Trying:
ajouteRoute(plan, x, y)
Expecting nothing
ok
Trying:
affiche("Ce plan a m = ", plan[0][1], " route(s).")
Expecting:
Ce plan a m = 5 route(s).
ok
Trying:
affiche(estVoisine(plan, x, y))
Expecting:
True
ok
Trying:
plan = [[5, 4], [1, 2, None, None, None], [3, 1, 3, 4, None], [1, 2, None, None, None], [2, 2, 5, None, None], [1, 4, None, None, None]] # plan2, cf ci-dessus
Expecting nothing
ok
Trying:
n = plan[0][0]
Expecting nothing
ok
Trying:
couleur = creerTableau(n+1) # Tableau vide
Expecting nothing
ok
Trying:
affiche(couleur)
Expecting:
[None, None, None, None, None, None]
ok
Trying:
k = 2; s = 1; t = 5
Expecting nothing
ok
Trying:
random.seed(0) # Pas d'aléa ici, pour la reproductibilité de l'exemple
Expecting nothing
ok
Trying:
coloriageAleatoire(plan, couleur, k, s, t)
Expecting nothing
ok
Trying:
affiche(couleur)
Expecting:
[None, 0, 2, 2, 1, 3]
ok
Trying:
n = 5; liste = creerListeVide(n)
Expecting nothing
ok
Trying:
affiche(liste)
Expecting:
[0, None, None, None, None, None]
ok
Trying:
n = 4; plan = creerPlanSansRoute(n)
Expecting nothing
ok
Trying:
affiche(plan)
Expecting:
[[4, 0], [0, None, None, None, None, None], [0, None, None, None, None, None], [0, None, None, None, None, None], [0, None, None, None, None, None]]
ok
Trying:
n = 5; t = creerTableau(n)
Expecting nothing
ok
Trying:
for i in range(n):
t[i] = i + 2
Expecting nothing
ok
Trying:
affiche(t)
Expecting:
[2, 3, 4, 5, 6]
ok
Trying:
p = 2; q = 2;
Expecting nothing
ok
Trying:
a = creerTableau2D(p, q)
Expecting nothing
ok
Trying:
for i in range(p):
for j in range(q):
a[i][j] = i+j
Expecting nothing
ok
Trying:
affiche(a)
Expecting:
[[0, 1], [1, 2]]
ok
Trying:
random.seed(0) # Pas d'aléa ici, pour la reproductibilité de l'exemple
Expecting nothing
ok
Trying:
entierAleatoire(10)
Expecting:
7
ok
Trying:
entierAleatoire(2)
Expecting:
2
ok
Trying:
liste = [5, 5, 4, 2, 1, 3]
Expecting nothing
ok
Trying:
affiche(estDansListe(liste, 0))
Expecting:
False
ok
Trying:
affiche(estDansListe(liste, 5))
Expecting:
True
ok
Trying:
affiche(estDansListe(liste, 194652))
Expecting:
False
ok
Trying:
plan = [[5, 4], [1, 2, None, None, None], [3, 1, 3, 4, None], [1, 2, None, None, None], [2, 2, 5, None, None], [1, 4, None, None, None]] # plan2, cf ci-dessus
Expecting nothing
ok
Trying:
x = 1; y = 2
Expecting nothing
ok
Trying:
affiche(estVoisine(plan, x, y))
Expecting:
True
ok
Trying:
x = 1; y = 5
Expecting nothing
ok
Trying:
affiche(estVoisine(plan, x, y))
Expecting:
False
ok
Trying:
plan = [[11, 13], [2, 2, 6], [2, 1, 7], [2, 4, 8], [2, 3, 8], [2, 6, 10], [3, 1, 5, 7], [3, 2, 6, 8], [4, 3, 4, 7, 9], [2, 8, 11], [2, 5, 11], [2, 9, 10]]
Expecting nothing
ok
Trying:
s = 6
Expecting nothing
ok
Trying:
t = 4
Expecting nothing
ok
Trying:
k = 3
Expecting nothing
ok
Trying:
couleur = [None, 1, 2, 3, k+1, 1, 0, 1, 2, 3, 3, 1]
Expecting nothing
ok
Trying:
affiche(existeCheminArcEnCiel(plan, couleur, k, s, t))
Expecting:
True
ok
Trying:
plan = [[11, 13], [2, 2, 6], [2, 1, 7], [2, 4, 8], [2, 3, 8], [2, 6, 10], [3, 1, 5, 7], [3, 2, 6, 8], [4, 3, 4, 7, 9], [2, 8, 11], [2, 5, 11], [2, 9, 10]]
Expecting nothing
ok
Trying:
s = 4
Expecting nothing
ok
Trying:
t = 6
Expecting nothing
ok
Trying:
k = 3
Expecting nothing
ok
Trying:
couleur = [None, 3, 2, 1, 0, 3, k+1, 3, 2, 1, 1, 3]
Expecting nothing
ok
Trying:
affiche(existeCheminArcEnCiel(plan, couleur, k, s, t))
Expecting:
True
ok
Trying:
plan = [[11, 13], [2, 2, 6], [2, 1, 7], [2, 4, 8], [2, 3, 8], [2, 6, 10], [3, 1, 5, 7], [3, 2, 6, 8], [4, 3, 4, 7, 9], [2, 8, 11], [2, 5, 11], [2, 9, 10]]
Expecting nothing
ok
Trying:
s = 11
Expecting nothing
ok
Trying:
t = 7
Expecting nothing
ok
Trying:
k = 3
Expecting nothing
ok
Trying:
couleur = [None, 1, 2, 3, 3, 2, 3, k+1, 2, 1, 1, 0]
Expecting nothing
ok
Trying:
affiche(existeCheminArcEnCiel(plan, couleur, k, s, t))
Expecting:
True
ok
Trying:
plan = [[11, 13], [2, 2, 6], [2, 1, 7], [2, 4, 8], [2, 3, 8], [2, 6, 10], [3, 1, 5, 7], [3, 2, 6, 8], [4, 3, 4, 7, 9], [2, 8, 11], [2, 5, 11], [2, 9, 10]]
Expecting nothing
ok
Trying:
s = 11
Expecting nothing
ok
Trying:
t = 7
Expecting nothing
ok
Trying:
k = 2
Expecting nothing
ok
Trying:
couleur = [None, 1, 2, 3, 3, 2, 3, k+1, 2, 1, 1, 0]
Expecting nothing
ok
Trying:
affiche(existeCheminArcEnCiel(plan, couleur, k, s, t))
Expecting:
True
ok
Trying:
plan = [[11, 13], [2, 2, 6], [2, 1, 7], [2, 4, 8], [2, 3, 8], [2, 6, 10], [3, 1, 5, 7], [3, 2, 6, 8], [4, 3, 4, 7, 9], [2, 8, 11], [2, 5, 11], [2, 9, 10]]
Expecting nothing
ok
Trying:
s = 11
Expecting nothing
ok
Trying:
t = 7
Expecting nothing
ok
Trying:
k = 3
Expecting nothing
ok
Trying:
couleur = [None, 1, 2, 3, 3, 2, 3, k+1, 2, 1, 1, 0]
Expecting nothing
ok
Trying:
couleur[6] = 1
Expecting nothing
ok
Trying:
affiche(existeCheminArcEnCiel(plan, couleur, k, s, t))
Expecting:
False
ok
Trying:
plan = [[11, 13], [2, 2, 6], [2, 1, 7], [2, 4, 8], [2, 3, 8], [2, 6, 10], [3, 1, 5, 7], [3, 2, 6, 8], [4, 3, 4, 7, 9], [2, 8, 11], [2, 5, 11], [2, 9, 10]]
Expecting nothing
ok
Trying:
s = 6
Expecting nothing
ok
Trying:
t = 4
Expecting nothing
ok
Trying:
k = 3
Expecting nothing
ok
Trying:
random.seed(0) # Pas d'aléa ici, pour la reproductibilité de l'exemple
Expecting nothing
ok
Trying:
affiche(existeCheminSimple(plan, k, s, t))
Expecting:
True
ok
Trying:
plan = [[11, 12], [2, 2, 6], [2, 1, 7], [1, 4], [2, 3, 8], [2, 6, 10], [3, 1, 5, 7], [3, 2, 6, 8], [3, 4, 7, 9], [2, 8, 11], [2, 5, 11], [2, 9, 10]]
Expecting nothing
ok
Trying:
s = 6
Expecting nothing
ok
Trying:
t = 4
Expecting nothing
ok
Trying:
k = 3
Expecting nothing
ok
Trying:
random.seed(0) # Pas d'aléa ici, pour la reproductibilité de l'exemple
Expecting nothing
ok
Trying:
affiche(existeCheminSimple(plan, k, s, t))
Expecting:
False
ok
Trying:
plan = [[5, 4], [1, 2, None, None, None], [3, 1, 3, 4, None], [1, 2, None, None, None], [2, 2, 5, None, None], [1, 4, None, None, None]] # plan2, cf ci-dessus
Expecting nothing
ok
Trying:
n = plan[0][0]
Expecting nothing
ok
Trying:
couleur = [None, 0, 2, 2, 2, 3] # Un coloriage
Expecting nothing
ok
Trying:
i = 2
Expecting nothing
ok
Trying:
c = 2
Expecting nothing
ok
Trying:
affiche(voisinesDeCouleur(plan, couleur, i, c))
Expecting:
[2, 3, 4, None, None, None]
ok
Trying:
couleur = [None, 0, 2, 1, 2, 3] # Autre coloriage
Expecting nothing
ok
Trying:
i = 4
Expecting nothing
ok
Trying:
c = 3
Expecting nothing
ok
Trying:
affiche(voisinesDeCouleur(plan, couleur, i, c))
Expecting:
[1, 5, None, None, None, None]
ok
Trying:
plan = [[5, 4], [1, 2, None, None, None], [3, 1, 3, 4, None], [1, 2, None, None, None], [2, 2, 5, None, None], [1, 4, None, None, None]] # plan2, cf ci-dessus
Expecting nothing
ok
Trying:
n = plan[0][0]
Expecting nothing
ok
Trying:
couleur = [None, 0, 2, 2, 1, 3] # Un coloriage
Expecting nothing
ok
Trying:
liste = creerListeVide(n+1) # liste = {} ensemble vide
Expecting nothing
ok
Trying:
ajouteDansListe(liste, 1) # liste = {1}
Expecting nothing
ok
Trying:
ajouteDansListe(liste, 2) # liste = {1, 2}
Expecting nothing
ok
Trying:
c = 2
Expecting nothing
ok
Trying:
affiche(voisinesDeLaListeDeCouleur(plan, couleur, liste, c))
Expecting:
[2, 2, 3, None, None, None]
ok
Trying:
c = 1
Expecting nothing
ok
Trying:
affiche(voisinesDeLaListeDeCouleur(plan, couleur, liste, c))
Expecting:
[1, 4, None, None, None, None]
ok
2 items had no tests:
__main__
__main__.question14
15 items passed all tests:
2 tests in __main__.affiche
2 tests in __main__.afficheToutesLesRoutes
7 tests in __main__.ajouteRoute
8 tests in __main__.coloriageAleatoire
2 tests in __main__.creerListeVide
2 tests in __main__.creerPlanSansRoute
3 tests in __main__.creerTableau
4 tests in __main__.creerTableau2D
3 tests in __main__.entierAleatoire
4 tests in __main__.estDansListe
5 tests in __main__.estVoisine
31 tests in __main__.existeCheminArcEnCiel
12 tests in __main__.existeCheminSimple
10 tests in __main__.voisinesDeCouleur
10 tests in __main__.voisinesDeLaListeDeCouleur
**********************************************************************
1 items had failures:
1 of 6 in __main__.ajouteDansListe
111 tests in 18 items.
110 passed and 1 failed.
***Test Failed*** 1 failures.
**********************************************************************
File "X_PSI_PT__2015.py", line 165, in __main__.ajouteDansListe
Failed example:
affiche(ensemble) # L'ordre est arbitraire ! set([16, 64, 4, 256, 1024])
Expected:
{16, 64, 256, 1024, 4}
Got:
{16, 256, 64, 4, 1024}
**********************************************************************
1 items had failures:
1 of 6 in __main__.ajouteDansListe
***Test Failed*** 1 failures.
Plus de détails sur ces doctests sont dans la documentation de Python:
https://docs.python.org/3/library/doctest.html (en anglais)
Le fichier Python se trouve ici : X_PSI_PT__2015.py
.