Jore / Daudet MPSI Concours blanc 016 Concours blanc IPT 016 Corrigé / Barème Partie I Échauement Q1 Quelle application est représentée par la liste [1, 1, 1, 1]? Il s'agit de l'application constante { {0, 1,, 3} {0, 1,, 3} x 1. Q Écrire une fonction Python constante(a,n) qui prend en argument un entier n et un entier a E n et renvoie la liste représentant l'application constante On peut proposer : def constante(a,n): L=[] for k in range(n): L.append(a) return(l) { En E n x a Ou plus simplement : def constante(a,n): return([a]*n). Q3 a. Écrire une fonction Python max(l) qui prend en argument une liste L d'entiers, non nécessairement triée, et renvoie le maximum des éléments de L. b. Écrire une fonction Python recherche_dichotomique(a,lt) qui prend en argument un entier a et une liste triée d'entiers Lt, et renvoie True si l'élément a est dans la liste Lt et False sinon, en utilisant l'algorithme de recherche dichotomique. Donner sans démonstration la complexité de cet algorithme. Il s'agit d'algorithmes vu en cours ou en TP. Le max : def max(l): M=L[0] for i in range(1,len(l)): if L[i]>M: M=L[i] return(m) La recherche dichotomique (une des nombreuses variantes possibles) : def recherche_dichotomique(a,lt): # Lt est supposee triee debut = 0 fin = len(lt)-1 while fin-debut>1: milieu = (debut+fin)// if Lt[milieu] >= a: fin = milieu debut = milieu return(lt[debut]==a or Lt[fin]==a) L'algorithme de recherche dichotomique est de complexité logarithmique. Partie II Recherche de point xe : cas général Q4 Écrire une fonction Python admet_point_fixe(l) qui prend en argument une liste L et renvoie True si l'application f : E n E n représentée par L admet un point xe, False sinon (n est donc ici la longueur de L).
Jore / Daudet MPSI Concours blanc 016 On peut proposer (attention à l'indentation!) : def admet_point_fixe(l): for i in range(len(l)): if L[i]==i: Q5 Écrire une fonction Python nb_points_fixes(l) qui prend en argument une liste L et renvoie le nombre de points xes de l'application f : E n E n représentée par L (n est donc ici la longueur de L). Il sut de rajouter un compteur à la fonction précédente : def nb_points_fixes(l): res=0 for i in range(len(l)): if L[i]==i: res=res+1 return(res) E n E n Dans la suite, on note f k l'itérée k-ième de f, autrement dit l'application f k : x f(f(... f(x))... ) }{{} k fois Q6 Écrire une fonction Python itere(l,x,k) qui prend en premier argument une liste L représentant une fonction f : E n E n (n est donc ici la longueur de L), en deuxième et troisième arguments des entiers x, k de E n, et qui renvoie f k (x). On peut proposer : def itere(l,x,k): res=x for i in range(k): res=l[res] return(res) Q7 Écrire une fonction Python nb_points_fixes_iteres(l,k) qui prend en premier argument une liste L représentant une application f : E n E n (n est donc ici la longueur de L), en deuxième argument un entier k 0, et qui renvoie le nombre de points xes de f k. Il sut d'utiliser les fonctions dénies en Q5 et Q6 : def nb_points_fixes_iteres(l,k): Lk=[] for x in range(len(l)): Lk.append(itere(L,x,k)) return(nb_point_fixe(lk)) Ou encore plus expéditivement : def nb_points_fixes_iteres(l,k): return(nb_point_fixe([itere(l,x,k) for x in range(len(l))])) Q8 Écrire une fonction Python admet_attracteur_principal(l) qui prend en argument une liste L et renvoie True si et seulement si l'application f : E n E n représentée par L admet un attracteur principal, False sinon (n est donc ici la longueur de L). On n'impose ici aucune complexité particulière. Privilégions ici la lisibilité du code à l'ecacité. Procédons en deux temps : si l'application n'a pas exactement un point xe, alors elle n'a pas d'attracteur principal ; si l'application a exactement un point xe z, z est attracteur principal si et seulement si f n (x) = z pour tout x E n.
Jore / Daudet MPSI Concours blanc 016 def admet_attracteur_principal(l): n=len(l) if nb_points_fixes(l)!=1: z=itere(l,0,n) for x in range(1,n): if itere(l,x,n)!=z: Q9 Écrire une fonction Python temps_de_convergence(l,x) qui prend en premier argument une liste L représentant une application f : E n E n qui admet un attracteur principal (n est donc ici la longueur de L), en deuxième argument un entier x de E n, et renvoie le temps de convergence de f en x. Par dénition tc(f, x) vaut 0 si x est un point xe de f, et 1 + tc(f, f(x)) si x n'est pas un point xe de f. Ce qui se traduit immédiatement avec une boucle conditionnelle de la façon suivante : def temps_de_convergence(l,x): temps=0 value=x while (L[value]!= value): value = L[value] temps=temps+1 return(temps) On peut aussi proposer la variante récursive suivante : def temps_de_convergence(l,x): if L[x]==x: return(0) return(1+temps_de_convergence(l,l[x])) Q10 Écrire une fonction Python temps_de_convergence_max(l) qui prend en argument une liste L représentant une application f : E n E n qui admet un attracteur principal, et renvoie max tc(f, x). x E n On impose dans cette question un temps de calcul au plus quadratique en la longueur n de la liste (i. e. en O(n )). On ne demande pas de démonstration de la complexité de la solution proposée, mais il est impératif d'expliquer clairement le fonctionnement de votre fonction Python. La solution naïve consiste à calculer tous les temps de convergence puis à en prendre le max. Elle est quadratique car chaque calcul de temps de convergence est linéaire et il y a n tels calculs à faire. def temps_de_convergence_max(l): tmax = 0 for x in range(len(l)): t = temps_de_convergence(l,x) if t > tmax: tmax = t return(tmax) Q11 Écrire une fonction Python temps_de_convergence_max(l) qui prend en argument une liste L représentant une application f : E n E n qui admet un attracteur principal (n est donc ici la longueur de L), et renvoie max tc(f, x). x E n On impose dans cette question un temps de calcul linéaire en la longueur n de la liste (i. e. en O(n)). Le manque de performance de la solution naïve résulte du fait que les mêmes calculs sont eectués plusieurs fois pour les diérentes valeurs de x. Pour y remédier : utilisons un tableau temps ultimement destiné à contenir les temps de convergence des diérentes valeurs. Initialement, on met 1 pour toutes les valeurs (on ne connaît pas encore les temps de convergence). On utilise aussi une liste auxilaire aux comme accumulateur de valeurs. Si on traite l'élément i, on va remplir la liste auxiliaire avec, successivement, i, f(i), f (i), f 3 (i),... f k (i) jusqu'à arriver à une valeur dont on connait le temps t, ou bien sur un point xe (dont on connaît aussi le temps, c'est 0). On donnera alors à f k 1 (i) le temps t + 1, puis à f k (i) le temps t + et ainsi de suite. En faisant parcourir à i les diérentes valeurs de E n, il y aura au plus un calcul eectué une fois en trop pour chaque valeur de i. Chaque valeur f(x) est donc calculée au plus deux fois. D'où une complexité linéaire.
Jore / Daudet MPSI Concours blanc 016 def temps_de_convergence_max(l): n = len(l) temps = n*[-1] aux = [] for i in range(n): # Traitement de l'élément i aux.append(i) # On remplit la liste aux : deux cas d'arret (point fixe ou element deja traite) while temps[aux[-1]] == -1 and L[aux[-1]]!= aux[-1]: aux.append(l[aux[-1]]) # Si on est tombe sur un point fixe, son temps d'attente est 0 if L[aux[-1]] == aux[-1]: temps[aux[-1]] = 0 # On vide la liste aux en traitant les elements correspondant while len(aux) > 0: precedent = aux.pop() temps[aux[-1]] = temps[precedent]+1 # On a maintenant obtenu tous les temps avec une complexite lineaire # On calcule le max par un simple parcours lineaire max = 0 for i in range(n): if temps[i] > max: max = temps[i] # C'est fini return(max) Partie III Recherche logarithmique dans un cas particulier On se place ici dans le cas d'une application croissante de E n dans E n. On admet que pour toute partie A E n, une application croissante de A dans A admet toujours un point xe. Q1 Écrire une fonction Python est_croissante(l) qui prend en argument une liste L et renvoie True si l'application représentée par L est croissante, et False sinon. On impose un temps de calcul linéaire en la longueur n de la liste. On sait bien (sinon, on mérite au plus 0.5 point) qu'une application f de E n dans E n est croissante si et seulement si k {1,,..., n 1}, f(k 1) f(k). Si on aime les boucles inconditionnelles : def est_croissante(l): for k in range(1,len(l)): if L[k-1] < L[k]: Si on préfère les conditionnelles : def est_croissante(l): n=len(l) k=1 while k<n and L[k-1]<=L[k]: k=k+1 return(k==n) Q13 Écrire une fonction point_fixe(l) qui prend en argument une liste L représentant une application croissante f : E n E n (n est donc ici la longueur de L), et retourne un entier x E n tel que f(x) = x. On impose un temps de calcul logarithmique en la taille n de la liste. On ne demande pas ici de démonstration du fait que le temps de calcul de la solution proposée est logarithmique. Comme l'ordre est total, on peut eectuer une brave dichotomie. Ainsi, en notant m = n 1 on a ou bien f(m) m, auquel cas A = {0,..., m} est stable par f par croissance, ou bien f(m) m, auquel cas A = {m,..., n 1} est stable par f par croissance. On peut ainsi en une opération diviser par deux le nombre d'éléments à examiner, ce qui conduit à une complexité logarithmique (car d'après le résultat admis, la restriction a encore un point xe). Cela donne :
Jore / Daudet MPSI Concours blanc 016 def point_fixe(l): # Initialement A=En. a=0 b=len(l)-1 # On coupe en deux jusqu'à obtenir une paire while a+1<b: c=(a+b)// # Mise a jour de A if L[c]<=c: b=c a=c # Quand A est une paire, on a le point fixe if L[a]==a: return(a) return(b) Q14 Démontrer que la fonction Python de la question III termine. Il sut de montrer que l'unique boucle while du programme termine. Notons a n, b n, c n les valeurs respectives des variables a, b, c à l'issue de n tours de boucle. Montrons que = b a est un variant de boucle, c'est-à-dire que la suite nie ou innie ( n ) n = (b n a n ) n est à valeurs dans N et strictement décroissante, donc nie. Comme on sort de la boucle dès qu'on n'a plus a + 1 < b, on a bien n 0 et même n > 1. Pour la stricte décroissance, on traite deux cas : si f(c n ) c n alors on a n+1 = b n+1 a n+1 = bn+an a n bn+an a n = bn an = n < n (car n > 0) ; si f(c n ) > c n alors on a n+1 = b n+1 a n+1 = b n bn+an b n bn+an 1 = n + 1 < n (car n > 1). D'où la terminaison. Q15 Justier que le temps de calcul est logarithmique en la taille n de la liste. Une justication informelle du type suivant sera jugée susante : Chaque tour boucle prend un temps constant. À chaque itération, l'entier b-a est approximativement divisé par deux, et le programme s'arrête quand il vaut 0 ou 1. Initialement, b-a vaut n, donc il est réduit à 1 en approximativement lg(n) = log (n) opérations, d'où la complexité attendue.