CORRIG´ E DU DS INFO n°1 - MINES 2019 - I. Pr´ eliminaires
Q1 -
from math import floor, ceil, log, sqrt print(log(0.5))
Q2 -
def sont_proches(x,y):
atol, rtol = 1e-5, 1e-8
return abs(x-y) <= atol + abs(y)*rtol
Q3 - On notera que la fonction de l’´enonc´e ne se termine pas lorsque b= 1 ! def mystere_recursif(x, b):
if x<b : return 0 else:
return 1+ mystere_recursif(x/b, b) def mystere_iteratif(x, b):
k = 0
while x >= b:
x = x/b k += 1 return k
print(mystere_recursif(1001, 10), mystere_iteratif(1001, 10)) 3 3
Q4 - On constate quemystere(x,b)renvoie le plus grand entier k tel que bk 6n.
Il s’agit donc de l’entier k tel que bk 6x < bk+1, c’est-`a-dire k6logb(x)< k+ 1 soit k=⌊logbx⌋ (on rappelle que logbx=lnlnxb).
Q5 - A la fin de la boucle` x1contient 105×10−5 etx2contient
105−1
P
k=0
10−5.
Si les calculs ´etaient exacts les deux contiendraient 1 . Mais ce n’est pas le cas car les nombres manipul´es sont des flottants, et les erreurs d’arrondi s’accumulent lors du calcul de la somme.
II. G´ en´ eration de nombres premiers
II.1. Approche syst´ ematique
Q6 - Une m´emoire vive de 4 Go, soit 4×109×8 bits permet d’enregistrer un tableau de 4×109×8 32 = 109
´el´ements.
Q7 - Comme il n’y a que deux valeurs pour un bool´een, on peut les coder par un seul bit, ce qui permettrait un gain m´emoire d’un facteur de 32.
Q8 - Les listes en Python commen¸cant `a l’indice 0 , et pour ´eviter un d´ecalage avec l’´enonc´e, j’ai utilis´e une liste de N+1 ´el´ements, l’´el´ement d’indice 0 ne servant pas. Ainsi, l’´el´ement d’indice i correspond exactement au nombre entier i.
from math import sqrt def erato_iter(N):
liste_bool = [True] * (N+1)
liste_bool[1] = False # 1 n'est pas premier
racine_entiere = int(sqrt(N) + 0.1) # +0.1 `a cause d'´eventuelles erreurs d'arrondi for i in range(2, racine_entiere+1): # +1 `a cause du range en Python
if liste_bool[i]:
for k in range(2*i, N+1, i):
liste_bool[k] = False return liste_bool
N = 50
liste = erato_iter(N)
print([i for i in range(2, N+1) if liste[i]])
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
Q9 - Si p est un nombre premier, la boucle for k ...qui consiste `a marquer comme faux les multiples de p diff´erents de p est effectu´ee j
N p
k fois.
La complexit´e totale est donc de l’ordre de P
p6N ppremier
N
p, soit, compte tenu de l’indication de l’´enonc´e, de l’ordre de Nln(ln(N).
Q10 - Soit b la base de num´eration et n le nombre de chiffres de N, c’est-`a-dire bn6N < bn+1.
Alors bnln(nlnb)6Nln(lnN))< bn+1ln((n+ 1) lnb) donc on peut dire que le r´esultat pr´ec´edent est un
O
(bnlnn).II.2. G´ en´ eration rapide de nombres premiers
Q11 - D’apr`es l’´enonc´e, i d´ecrit J1 ;NK donc on aura A=
N−1
P
i=1
2i= 2N −2 .
Cependant, dans l’algorithme propos´e `a la questionQ12, i d´ecritJ0 ;NK; dans ce cas, A pourra prendre la valeur maximum 2N−1 .
Q12 - L’algorithme revient `a tirer au (pseudo-)hasard chaque bit du nombre `a construire.
from time import time def bbs(N):
p1 = 24375763 p2 = 28972763 M = p1 * p2 t = time()
xi = floor( (t-floor(t))*1e7 )
# La r´esolution de l'horloge est 10**-7 d'apr`es l'´enonc´e.
A = 0
for i in range(N):
if xi % 2 == 1:
A = A + 2**i
# Remarque : il serait plus efficace de calculer les puissances
# de 2 au fur et `a mesure.
xi = xi**2 % M return A
print(bbs(10)) 879
Q13 - Si l’on veut un nombre strictement inf´erieur `a nmax, il faut 2N −1 <nmax, ou encore 2N 6nmax. On devrait donc choisir N=mystere(nmax,2). Mais en faisant ainsi, les nombres entre 2N + 1 et nmaxs’il y en a (c’est-`a-dire sinmaxn’est pas une puissance de 2 ) ne seront jamais obtenus ; pour cette raison, j’ai choisi d’augmenter N de 1 , mais de tester ensuite sibbs(N)est bien inf´erieur `anmax.
Remarques
– La fonctionbbspeut renvoyer 0 ou 1 comme valeur de p; ces valeurs sont ´evidemment `a exclure.
– Le test de Fermat ne ne fonctionne pas pour a=p (cf. la condition a∈J2 ;p−1K dans l’´enonc´e). En effet, pp−1 ≡0 6≡1[p]. Ainsi par exemple le nombre 7 sera jug´e non premier par ce test et sera donc
´ecart´e. C’est la raison pour laquelle l’´enonc´e demande que nb max>12 : ainsi il y aura au moins un nombre premier qui soit >7 et <nb max (c’est 11).
from time import time
from math import sqrt, floor, ceil, log def erato_iter(N):
liste_bool = [True for i in range(N+1)]
liste_bool[1] = False # 1 n'est pas premier
racine_entiere = int(sqrt(N) + 0.1) # +0.1 `a cause d'´eventuels erreurs d'arrondi for i in range(2, racine_entiere+1): #+1 `a cause du range en Python
if liste_bool[i]:
for k in range(2*i, N+1, i):
liste_bool[k] = False return liste_bool
def mystere(x, b):
if x<b : return 0 else:
return 1+ mystere(x/b, b) def bbs(N):
# version un peu am´elior´ee p1 = 24375763
p2 = 28972763 M = p1 * p2 t = time()
xi = floor( (t-floor(t))*1e7 ) A = 0
puiss = 1 # les puissances de 2 for i in range(N):
if xi % 2 == 1:
A += puiss puiss *= 2 xi = xi**2 % M return A
def test_Fermat(p):
for a in [2, 3, 5, 7]:
# if a**(p-1) % p != 1: # horriblement lent!
if pow(a, p-1, p) != 1: # bien plus rapide car tous les calculs
# avec pow sont faits modulo p au fur et `a mesure return False
return True
def premier_rapide(n_max):
# N = mystere(n_max, 2) + 1
N = ceil( log(n_max, 2) ) # plus rapide!
p = bbs(N)
while p < 2 or p >= n_max or not test_Fermat(p):
p = bbs(N) return p
def stats_bbs_fermat(N, nb):
premier = erato_iter(N) erreurs = []
for k in range(nb):
p = premier_rapide(N) if not premier[p]:
erreurs.append(p) return erreurs, len(erreurs)/nb deb = time()
print(stats_bbs_fermat(100000, 50000)) print('Temps:', time() - deb)
([], 0.0)
Temps: 10.945600986480713
Q14 - Voir ci-dessus.
III. Compter les nombres premiers
III.1. Calcul de π ( n ) via un crible
Q15 -
from math import sqrt, log def erato_iter(N):
liste_bool = [True for i in range(N+1)]
liste_bool[1] = False # 1 n'est pas premier
racine_entiere = int(sqrt(N) + 0.1) # +0.1 `a cause d'´eventuels erreurs d'arrondi for i in range(2, racine_entiere+1): #+1 `a cause du range en Python
if liste_bool[i]:
for k in range(2*i, N+1, i):
liste_bool[k] = False return liste_bool
def Pi(N):
premier = erato_iter(N) res = []
pi_n = 0
for n in range(1,N+1):
if premier[n]:
pi_n += 1
res.append( [n, pi_n] ) return res
def verif_Pi(N):
valeurs = Pi(N)[5392:]
for (n, pi_n) in valeurs:
if n/(log(n)-1) >= pi_n:
return False return True
print(Pi(8))
print(verif_Pi(100000))
[[1, 0], [2, 1], [3, 2], [4, 2], [5, 3], [6, 3], [7, 4], [8, 4]]
True Q16 - Voir ci-dessus.
III.2. Calcul approch´ e via une int´ egrale g´ en´ eralis´ ee
III.2.1. Estimation de li par quadrature num´erique
Q17 - Pour obtenir un r´esultat approch´e convenable par la m´ethode des rectangles, il faut quepassoit tr`es petit.
Ainsi, il est pertinent d’exprimer la complexit´e en fonction depaslorsque celui-ci tend vers 0.
L’op´eration ´el´ementaire la plus couteuse est sans doute l’appel `a la fonction `a int´egrer, qui est en
O
(1)d’apr`es l’´enonc´e. Il y a un tel appel pour chaque rectangle de la subdivision, et il y a pasx rectangles. D’o`u une complexit´e en
O
xpas
.
Q18 - Les m´ethodes des rectangles centr´es et des trap`ezes ont la mˆeme complexit´e que celle des rectangles `a droite (mˆeme nombre de points calcul´es, `a 1 pr`es).
Q19 -
from math import log
from scipy.special import expi #exponentielle int´egrale def inv_ln_rect_d(a, b, pas):
# on ´evite les multiplications:
# ce serait tr`es maladroit d'´ecrire une boucle for avec des a+k*pas S = 0
a0 = a + pas while a0 <= b:
S += 1/log(a0) a0 += pas return pas * S
def inv_ln_rect_g(a, b, pas):
# m´ethode des rectangles `a gauche S = 0
a0 = a
while a0 < b:
S += 1/log(a0) a0 += pas return pas * S def li_d(x, pas):
if x>1:
return inv_ln_rect_d(0, 1-pas, pas) + inv_ln_rect_d(1+pas, x, pas) elif x==1:
return float('inf') else:
return inv_ln_rect_d(0, x, pas) def li_d_modif(x, pas):
if x>1:
return inv_ln_rect_d(0, 1-pas, pas) + inv_ln_rect_g(1+pas, x, pas) elif x==1:
return float('inf') else:
return inv_ln_rect_d(0, x, pas) def li_d_modif2(x, pas):
if x>1:
return inv_ln_rect_d(0, 1-2*pas, pas) + inv_ln_rect_d(1+pas, x, pas) elif x==1:
return float('inf') else:
return inv_ln_rect_d(0, x, pas) for x in [0.5, 1.1, 1.45, 10, 100]:
print("Pour x = ",x,":")
print("li_d:{:.6f}, li_d_modif:{:.6f}, li_d_modif2:{:.6f}, Exact:{:.6f}".format(li_d(x, 1e-5 Pour x = 0.5 :
li_d:-0.378663, li_d_modif:-0.378663, li_d_modif2:-0.378663, Exact:-0.378671 Pour x = 1.1 :
li_d:-2.675835, li_d_modif:-1.675830, li_d_modif2:-1.675840, Exact:-1.675773 Pour x = 1.45 :
li_d:-1.003703, li_d_modif:-0.003698, li_d_modif2:-0.003708, Exact:-0.003680 Pour x = 10 :
li_d:5.165592, li_d_modif:6.165597, li_d_modif2:6.165587, Exact:6.165600 Pour x = 100 :
li_d:29.126131, li_d_modif:30.126136, li_d_modif2:30.126126, Exact:30.126142 Q20 - Voir ci-dessus.
III.2.2. Analyse des r´esultats deli d
Q21 - L’´ecart relatif pr´esente une asymptote en une valeur proche de 1,4 tout simplement car son calcul fait intervenir une division parli refqui s’annule (visible sur la figure 1).
Plus pr´ecis´ement, la fonctionlis’annule en µ≈1.4513692348... qui est la constante de Soldner.
Q22 - L’int´egrale ´etant consid´er´ee au sens de Cauchy, on a Z 1+ε
1−ε
dt ln(t) =
Z ε
−ε
du
ln(1 +u) =
O
(ε) lorsque ε→0 ; en effet, en ´ecrivant un d´eveloppement limit´e de ln au voisinage de 1 `a l’ordre 2 on aZ −h
−ε
du ln(1 +u) =
Z −h
−ε
1 u+1
2+
O
(u)du et de mˆeme
Z ε
h
du ln(1 +u) =
Z ε
h
1 u+1
2+
O
(u)du donc lim
h→0+
Z −h
−ε
du ln(1 +u)+
Z ε
h
du ln(1 +u)
!
=ε+
O
(ε2). Ainsi,Z 1+ε
1−ε
dt ln(t)
∼
ε→0ε.
Or on voit sur la figure 4 que, avec la m´ethode des rectangles `a droite, la valeur de x7→ ln1x en 1−ε n’est pas ”compens´ee” par la valeur en 1 +ε, ce qui explique l’´ecart.
Q23 - Une solution (li modif) consiste donc `a utiliser la m´ethode des rectangles `a gauche pour la partie droite de la courbe. Une autre solution (li modif2) consiste `a retirer un rectangle dans la m´ethode des rectangles
`a droite pour la partie gauche de la courbe.
Tout cela fait un peu bidouillage... Pourquoi utiliser la m´ethode des rectangles, qui est la pire ?
III.3. Estimation de li via Ei
Q24 - Pour obtenir une complexit´e en O(MAXIT), il faut penser `a calculer les factorielles et les puissances de x en les gardant en m´emoire au fur et `a mesure (c’est de toutes fa¸cons un principe de base !).
from math import log
from scipy.special import expi #exponentielle int´egrale def sont_proches(x,y):
atol, rtol = 1e-5, 1e-8
return abs(x-y) <= atol + abs(y)*rtol MAXIT = 100
def Ei(x):
if x<=0:
return False else :
gamma = 0.577215664901 n = 1
fact = 1 # n!
puiss = x # x**n
Ei0 = gamma + log(x) # ordre n-1
Ei1 = Ei0 + x # ordre n
while n <= MAXIT and not sont_proches(Ei0, Ei1):
n += 1 fact *= n puiss *= x Ei0 = Ei1
Ei1 += puiss/n/fact if n == MAXIT+1:
return False else:
return Ei1 def li_dev(x):
return Ei(log(x))
x = 10; print(li_dev(x), expi(log(x))) 6.165598760535916 6.1655995047872985
IV. ´ Evaluation des performances (BDD)
Q25 - Plusieurs enregistrements ont la mˆeme valeur pour le champnom, ce ne peut donc pas ˆetre une cl´e primaire.
Q26 - 1. Nombre d’ordinateurs disponibles et quantit´e moyenne de m´emoire vive.
SELECT COUNT(∗) AS n b o r d i , AVG(RAM) AS RAM moyenne FROM o r d i n a t e u r s
2. Noms des PC sur lesquels l’algorithmerectanglesn’a pas ´et´e test´e pour la fonctionli.
SELECT t e s t e s u r FROM f o n c t i o n s
WHERE a l g o r i t h m e <> ” r e c t a n g l e s ” OR nom <> ” l i ” GROUP BY t e s t e s u r
ou bien :
SELECT nom FROM o r d i n a t e u r s WHERE nom NOT IN (
SELECT t e s t e s u r FROM f o n c t i o n s
WHERE a l g o r i t h m e=” r e c t a n g l e s ” AND nom = ” l i ” )
3. Pour chaque test de Ei garder le nom de l’algo, du pc, et sa puissance. Trier du plus lent au plus rapide.
SELECT a l g o r i t h m e , t e s t e s u r , ram , g f l o p s
FROM f o n c t i o n s JOIN o r d i n a t e u r s ON f o n c t i o n s . t e s t e s u r = o r d i n a t e u r s . nom WHERE f o n c t i o n s . nom = ” Ei ”
ORDER BY t e m p s e x e c DESC ou, plus simplement `a mon avis :
SELECT a l g o r i t h m e , t e s t e s u r , ram , g f l o p s FROM f o n c t i o n s , o r d i n a t e u r s
WHERE f o n c t i o n s . t e s t e s u r = o r d i n a t e u r s . nom AND f o n c t i o n s . nom = ” Ei ” ORDER BY t e m p s e x e c DESC
⋆ ⋆ ⋆ ⋆
⋆ ⋆ ⋆
⋆ ⋆
⋆