Vous êtes sur la page 1sur 11

Corrigé Informatique Mines Ponts 2021

March 11, 2022

1 Randonnée
1.1 Q1
select count(*)
from participant
where ne >= 1999 and ne >= 2003

1.2 Q2
select diff, avg(duree)
from rando
group by diff

1.3 Q3
select p.pnom
from participant p
join rando r on r.ff > p.diff_max
where r.rid = 42

1.4 Q4
select distinct r1.rid
from rando r1
join rando r2 on r1.rnom = r2.rnom and r1.rid <> r2.rid

1.5 Q5
Une version concise qui utilise map. Attention, map renvoie un itérateur, donc il faut convertir en
liste…
[1]: def importe_rando(nom_fichier:str)->[[float], [float], [float], [float]]:
fichier = open(nom_fichier, 'r')
lignes = fichier.readlines()
fichier.close()
return [list(map(float, ligne.split(','))) for ligne in lignes[1:]]

Une version plus classique, sans map :

1
[2]: def importe_rando(nom_fichier:str)->[[float], [float], [float], [float]]:
fichier = open(nom_fichier, 'r')
lignes = fichier.readlines()
fichier.close()
coords = []
for ligne in lignes[1:]:
lat, long, height, time = ligne.split(',')
coords.append([float(lat), float(long), float(height), float(time)])
return coords

[3]: coords = importe_rando('suivi_rando.csv')


coords

[3]: [[45.461516, 6.44461, 1315.221, 1597496965.0],


[45.461448, 6.444426, 1315.702, 1597496980.0]]

1.6 Q6
C’est une recherche de maximum, il faut juste tester sur la hauteur et stocker la latitude et la
longitude.
[4]: def plus_haut(coords:[[float], [float], [float], [float]])->[float, float]:
cmax = coords[0]
hmax = cmax[2]
for c in coords:
h = c[2]
if h > hmax:
cmax = c
hmax = c[2]
return list(cmax[:2])

[5]: plus_haut(coords)

[5]: [45.461448, 6.444426]

1.7 Q7
On calcule pas à pas toutes les variations d’altitudes et on les ajoute à la bonne variable en fonction
du signe obtenu :
[6]: def deniveles(coords:[[float], [float], [float], [float]])->[float, float]:
d_plus = 0
d_moins = 0
for i in range(len(coords)-1):
dh = coords[i+1][2] - coords[i][2]
if dh > 0:
d_plus += dh

2
else:
d_moins += (-dh)
return [d_plus, d_moins]

[7]: deniveles(coords)

[7]: [0.48099999999999454, 0]

1.8 Q8
[8]: from math import cos, sin, sqrt, asin

La question aurait méritée un petit dessin. Le grand cercle dont on parle correspond au cercle de
rayon moyen r = RT + h1 +h 2
2
qui passe par les deux points (géodésique de la sphère). Il faut en
plus tenir compte de la distance supplémentaire parcourue à cause de la variation d’altitude :


dreelle = (d2 + ∆h2 )

[9]: def distance(c1:[float, float, float, float], c2:[float, float, float,␣


,→float])->float:

RT = 6371000
lat1, long1, h1, t1 = c1
lat2, long2, h2, t2 = c2
r = RT + (h1 + h2)/2
d = 2*h_moy*asin(sqrt( (sin((lat2-lat2)/2))**2 +␣
,→cos(lat1)*cos(lat2)*(sin((long2 - long1)/2))**2 ))

dh = h2 - h1
return sqrt( d**2 + dh**2 )

1.9 Q9
Il s’agit d’une simple somme :
[10]: def distance_totale(coords:[[float], [float], [float], [float]])-> float:
d = 0
for i in range(len(coords)-1):
d += distance(coords[i+1], coords[i])
return d

3
2 Mouvement brownien d’une petite particule
2.1 Q10
[11]: def vma(v1:[float, float], a:float, v2:[float, float])->[float, float]:
x1, y1 = v1
x2, y2 = v2
return [x1 + a*x2, y1 + a*y2]

2.2 Q11
Il s’agit de la fonction qu’on passe en paramètre à une fonction de résolution de problème différentiel
telle que odeint :
[12]: from math import pi
from random import uniform, gauss

[13]: def derive(E): # type de E non précisé, type de retour non précisé
# constantes
m = 1e-6
alpha = 1e-5
sigma = 1e-8

x, y, vx, vy = E
theta = uniform(0, 2*pi) # direction de fB
fB = abs(gauss(0, sigma)) # norme de fB

return vx, vy, -alpha*vx/m + fB*cos(theta)/m, -alpha*vy/m + fB*sin(theta)/m

2.3 Q12
[14]: def euler(E0:[float, float, float, float], dt:float, n:int)->[[float, float,␣
,→float, float]]:

E = [E0]
for i in range(n):
xi, yi, vxi, vyi = E[i]
vx, vy, ax, ay = derive(E[i])
vxi, vyi, xi, yi = vxi + ax*dt, vyi + ay*dt, xi + vx*dt, xi + vy*dt
E.append([xi, yi, vxi, vyi])
return E

[15]: import matplotlib.pyplot as plt

[16]: E0 = 0, 0, 0, 0
dt = 0.01
n = 30
E = euler(E0, dt, n)

4
X, Y = [], []
for x, y, vx, vy in E:
X.append(x)
Y.append(y)

[17]: fig, ax = plt.subplots()


ax.plot(X, Y, marker='o')

[17]: [<matplotlib.lines.Line2D at 0x7fd21ae09190>]

3 Marche auto-évitante
3.1 Q13
[18]: def positions_possibles(p:[int, int], atteints:[[int, int]]):
x, y = p
pos = []
if [x+1, y] not in atteints:
pos.append([x+1, y])
if [x-1, y] not in atteints:
pos.append([x-1, y])
if [x, y+1] not in atteints:
pos.append([x, y+1])

5
if [x, y-1] not in atteints:
pos.append([x, y-1])
if pos == []:
return None
else:
return pos

3.2 Q14
[19]: fig, ax = plt.subplots()
ax.plot([0, 1, 2, 2, 2, 1, 0, 0, 1], [0, 0, 0, 1, 2, 2, 2, 1, 1], marker='o')
ax.axis('equal')

[19]: (-0.1, 2.1, -0.1, 2.1)

Le chemin le plus court qui conduit à renvoyer None (aucune position ultérieure possible) a la forme
d’une spirale convergente formée de huits points (7 segments). par rotation succesives d’angle pi/2
on obtient 4 chemins, par symétrie dans un miroir (changement du sens de rotation de la spirale)
on multiplie ce résultat par 2 pour obtenir 8 chemins.

3.3 Q15
[20]: from random import randrange

6
[21]: def genere_chemin_naif(n:int)->[[int, int]]:
atteints = [[0, 0]]
for i in range(n):
pos = positions_possibles(atteints[i], atteints)
if pos is None:
return None
else:
atteints.append(pos[randrange(0, len(pos)-1)])
return atteints

[22]: pos = genere_chemin_naif(30)

[23]: fig, ax = plt.subplots()


ax.plot(*list(zip(*pos)), marker='o')
ax.axis('equal')

[23]: (-0.65, 13.65, -0.45, 9.45)

3.4 Q16
La fonction positions_possibles a une complexité linéaire en la taille du chemin déjà parcouru
(le mot clé in cache un parcours linéaire de la liste des positions déjà atteintes). La fonction
genere_chemin_naif appelle successivement la fonction positions_possibles pour les valeurs
1, 2, ..., n donc le coût total est proportionnel à la somme 1 + 2 + ... + n = O(n2 ).

7
3.5 Q17
On fait varier le nombre de points du chemin auto-évitant de 1 à 351. Pour chaque longueur n de
chemin auto-évitant, on fait une petite statistique sur 10000 tentatives : - on compte le nombre de
chemins qui conduisent à un échec prématuré (None) de la méthode de génération - on calcule le
rapport entre ce nombre et le nombre total d’essais.
Le grand nombre d’essais rapproche le rapport calculé de la probabilité qu’un chemin de taille n
conduise à un échec avec la méthode utilisée. On voit que cette probabilité tend vers 1 lorsque n
augmente (pour n=350, l’échec est quasi-certain).
Ce graphique démontre que la méthode naïve employée n’est pas utilisable pour générer des chemins
auto-évitants de taille supérieure à quelques centaines.

3.6 Q18
Un algorithme de tri optimal a une complexité en O(nlog(n)), c’est donc très certainement la
complexité de la fonction sorted. Le tri fusion a cette complexité, et le tri rapide en moyenne
également, si le choix du pivot est aléatoire.

3.7 Q19
C’est un problème classique de recherche de doublons (cf bases de données avec la clause distinct),
qu’on peut résoudre par un tri préalable puis un parcours linéaire : lors du parcours si deux élements
adjacents sont égaux alors le chemin n’est pas auto-évitant (présence d’au moins un doublon dans
le chemin).
La complexité de la fonction est donc celle du tri (O(nlog(n)), puisque celle du parcours lui est
inférieure (O(n)).

[24]: def est_CAE(chemin:[[int, int]])->bool:


cht = sorted(chemin)
for i in range(len(cht)-1):
if cht[i] == cht[i+1]:
return False
return True

[25]: est_CAE(pos)

[25]: True

3.8 Q20
On peut définir la matrice de rotation d’angle alpha (on calcule alors explicitement les valeurs des
cosinus et sinus) et le produit matriciel entre un vecteur de taille 2 et une matrice (2, 2) :

[26]: def M(a:int)->[[int, int], [int, int]]:


if a == 0:
return [[-1, 0], [0, -1]]
elif a == 1:

8
return [[0, -1], [1, 0]]
else:
return [[0, 1], [-1, 0]]

[27]: def prod(M:[[int, int], [int, int]], v:[int, int]):


[[a, b], [c, d]] = M
[x, y] = v
return [a*x + b*y, c*x + d*y]

La fonction demandée est alors facile à écrire :


[28]: def rot(p:[int, int], q:[int, int], a:int)->[int, int]:
[px, py] = p
[qx, qy] = q
pq = [qx - px, qy - py]
[pq2x, pq2y] = prod(M(a), pq)
return [px + pq2x, py + pq2y]

[29]: p = [2, 3]
q = [1, 4]
q2 = q2x, q2y = rot(p, q, 1)
fig, ax = plt.subplots()
ax.plot([2, 1], [3, 4], marker='o')
ax.plot([2, q2x], [3, q2y], marker='o')
ax.axis('equal');

9
3.9 Q21
[30]: def rotation(chemin:[[int, int]], i_piv:int, a:int)->[[int, int]]:
return chemin[:i_piv] + [rot(chemin[i_piv], q, a) for q in chemin[i_piv:]]

[31]: n = 10
chemin = [[i, 0] for i in range(n)]
chemin2 = rotation(chemin, 5, 1)

[32]: fig, ax = plt.subplots()


ax.plot(*list(zip(*chemin)), marker='o')
ax.plot(*list(zip(*chemin2)), marker='o', color='red')
ax.axis('equal');

3.10 Q22
[33]: from copy import deepcopy

[34]: def genere_chemin_pivot(n:int, n_rot:int)->[[int, int]]:


chemin = [[i, 0] for i in range(n)] # initialisation
for i in range(n_rot):
chemin2 = rotation(chemin, randrange(1, n-1), randrange(0, 2))
while not est_CAE(chemin2):
chemin2 = rotation(chemin, randrange(1, n-1), randrange(0, 2))
chemin = deepcopy(chemin2)
return chemin

10
[35]: chemin = genere_chemin_pivot(300, 100)

[36]: fig, ax = plt.subplots()


ax.plot(*list(zip(*chemin)), marker='o')
ax.axis('equal');

3.11 Q23
Les positions respectives du pivot, du point précédent prec et du point suivant suiv empêche une
rotation (celle qui envoie suiv sur prec). On pourrait donc éliminer des valeurs de a possibles celle
qui conduit à cette situation, afin de ne pas appeler inutilement la fonction est_CAE (qui coûte un
tri).

[ ]:

11

Vous aimerez peut-être aussi