Corrigé

Transcription

Corrigé
L1 - Introduction à l’informatique (Année 2011/2012)
Correction du partiel (2e partie)
Codage (4 points)
Exercice 3.
Dans tout cet exercice, vous pouvez vous contenter de ne garder qu’un chiffre significatif pour tous les
calculs que vous effectuez. Vous pouvez également utiliser l’approximation
103 ≃ 210 = 1024.
En août 2011, Blizzard Entertainment, développeur du jeu vidéo World of Warcraft, a annoncé qu’ils allaient mettre en place un système permettant à chaque personnage du jeu de stocker 80 objets supplémentaires dans un emplacement spécial (une sorte de coffre en banque appelé void storage).
1. Sachant qu’il existe environ 80 000 objets différents dans le jeu, combien faut il de bits pour coder
un objet à l’aide d’un code unique ?
R. Puisqu’avec k bits on ne peut faire que 2k codes différents, si l’on veut coder 80000 objets il faut
au moins log2 (80000) bits.
On a
80000 = 80 ∗ 1000
26 = 64 ≤ 80 ≤ 27 = 128
log2 (80000) = log2 (80) + log2 (1000)
16 ≤ log2 (80000) ≤ 17
Il faut donc 17 bits pour représenter 80000 objets distincts.
On pouvait aussi utiliser la calculatrice pour trouver directement log2 (80000) ≃ 16, 29 mais il fallait
alors faire attention à bien calculer le logarithme en base 2.
2. Il y a en ce moment environ 10 millions d’abonnés à World of Warcraft, chaque abonné peut avoir
jusqu’à 50 personnages différents. Estimez la quantité totale de mémoire nécessaire (en gigaoctets)
pour stocker les identifiants des 80 objets supplémentaires sur tous ces personnages.
R. Puisqu’il faut 17 bits pour chaque objet, qu’il y a 80 objets par personnage, 50 personnages par
joueur et 10 millions de joueurs, il faut au total
17 × 80 × 50 × 107 bits = 68 × 1010 bits ≃ 7 × 1011 bits
Il y a 8 bits dans un octet et 109 octets dans un gigaoctet. Il faut donc
7 × 1011 /8 octets ≃ 1010 octets ≃ 100 Go
Après avoir présenté ce nouveau stockage, Blizzard a précisé que seuls les identifiants des objets seront stockés. Les modifications éventuelles que les joueurs peuvent apporter aux objets ne seront pas
conservées. Les différentes modifications que l’on peut apporter à un objet sont les suivantes :
– le nom du créateur de l’objet (le nom d’un personnage, d’au plus 12 caractères, on considérera qu’il
faut un octet par caractère) ;
– un enchantement parmi environ 1000 possibilités ;
– au plus trois gemmes serties sur l’objet parmi environ 1000 possibilités pour chacune des gemmes.
La description complète d’un objet est donc la donnée du code désignant l’objet de base parmi les 80 000
possibles, le nom du créateur, un code désignant l’enchantement associé parmi les 1000 possibles et 3
codes désignant chaque gemme parmi les 1000 possibles.
3. Combien d’octets faut-il pour représenter entièrement la description d’un objet avec tous ses paramètres personnalisés ?
1
R. La taille nécessaire pour stocker un objet est la somme des tailles nécessaires pour stocker ses
différentes parties :
– le code de l’objet : 17 bits (ou 3 octets si l’on veut un nombre entier d’octets) ;
– le nom du créateur : 12 octets (ou 12 × 8 = 96 bits si l’on veut faire le calcul en bits) ;
– l’enchantement : 1000 possibilités, soit log2 (1000) ≃ 10 bits ou 2 octets ;
– les gemmes : 1000 possibilités pour chacune, soit 10 bits ou 2 octets chacune et donc un total de 30
bits ou 6 octets pour les trois.
Au final, il faut
17 + 96 + 10 + 30 bits = 153 bits ≃ 20 octets
ou bien (si l’on fait le calcul directement en octets avec des arrondis fait dans les évaluations de la
taille de chaque partie)
3 + 12 + 2 + 6 octets = 23 octets
La valeur obtenue n’est pas exactement la même dans les deux cas car l’estimation en octets fait
des arrondis vers le haut plus tôt dans le calcul donc on surestime un peu plus la taille minimale
à utiliser. En pratique il est beaucoup plus probable que les différents éléments des objets soient
enregistrés sur des octets séparés et donc l’estimation en arrondissant aux octets à chaque étape est
probablement plus réaliste.
4. Combien de mémoire (en gigaoctets) serait nécessaire pour pouvoir mémoriser la description
complète de 80 objets sur chacun des personnages du jeu (50 personnages par joueur, 10 millions de
joueurs) ?
R. Chaque objet nécessite 23 octets, il y a toujours 80 × 50 × 107 objets au total soit une mémoire
nécessaire de
23 × 80 × 50 × 107 octets = 92 × 1010 octets ≃ 920 Go
La raison officielle pour laquelle les modifications des objets ne seront pas mémorisées dans le void
storage est que cela prendrait beaucoup trop de place 1 .
5. En comparant la mémoire nécessaire pour stocker entièrement les objets supplémentaires (question précédente) à l’ordre de grandeur de la capacité d’un disque dur couramment trouvé dans un
ordinateur actuel, que pensez-vous de ce commentaire venant d’une entreprise ayant des dizaines
de serveurs dédiés à un jeu qui leur rapporte environ un milliard d’euros par an ?
R. Aujourd’hui, le disque dur d’un ordinateur personnel standard contient entre 200 Go et 2 To (2000
Go). L’espace total nécessaire pour stocker tous les nouveaux objets est donc de l’ordre de la taille
d’un disque dur trouvé dans n’importe quel ordinateur. Sachant que cet espace nécessaire est réparti
sur des dizaines de serveurs, la contrainte de place est assez négligeable.
Remarque : Il faut tout de même signaler que les estimations que l’on a faites ici considèrent la
place minimale pour stocker des objets, en les représentant de manière minimaliste, les uns à la suite
des autres dans la mémoire. En réalité, dans une base de données, il faut ajouter beaucoup d’autres
informations (délimiteurs entre les descriptions d’objets, descriptions de certains champs, etc.) et la
taille de l’ensemble peut donc augmenter significativement.
Cependant, même en considérant une place nécessaire 100 fois plus importante on obtient une taille
d’environ 100 To ce qui reste tout à fait acceptable pour une grande entreprise.
Algorithmique (6 points)
Exercice 4.
Les programmes demandés dans cet exercice peuvent être écrits en pseudo-code ou en Python (éventuellement en pseudo-Python...).
1. Citation originale en anglais (pas indispensable à comprendre pour l’exercice) : “Part of this is due to technical
limitations. One of the reasons we’re able to offer so many more slots of storage to players now is because it’s fairly
simple data storage when we’re talking item IDs. Throw in enchants, gems, and reforging though, and you have a
lot more data that has to be stored.”
2
Une permutation de l’intervalle J0, nK est une bijection de J0, nK dans lui-même 2 . Par exemple la fonction


 J0, 6K → J0, 6K


0 7→ 4




1 7→ 3



2 7→ 6
σ:
3 7→ 2




4 7→ 5




5 7→ 0



6 7→ 1
est une permutation de J0, 6K. Puisque la fonction est bijective, si on part d’une valeur et qu’on applique
de manière répétée la fonction, on finit par retomber sur la valeur initiale. La suite des valeurs rencontrées en appliquant la fonction à partir de la valeur x est applelée cycle de x. Dans l’exemple, partant de
2 on a
2→6→1→3→2
et donc le cycle de 2 est 2, 6, 1, 3 et il est de longueur 4.
On suppose que l’on a une fonction sigma(n) qui calcule une permutation de l’intervalle J0, 999K (vous
pouvez donc directement utiliser la fonction sigma dans vos programmes).
1. Écrivez un programme qui affiche la suite des valeurs prises par sigma (il faut afficher sigma(0),
sigma(1), sigma(2), et ainsi de suite jusqu’à sigma(999)).
R. Il faut écrire une boucle for qui parcourt tous les entiers de 0 à 999 et pour chacun d’eux affiche la
valeur de sigma(i) :
f o r i in range ( 1 0 0 0 ) : # i p r e n d l e s v a l e u r s d e 0 à 999
p r i n t sigma ( i )
2. Écrivez une fonction antecedent(n) qui calcule l’antécédent de n par sigma (le seul entier m
tel que sigma(m) = n).
R. Lorsqu’on recherche l’antécédent de n par sigma, on écrit une boucle qui calcule pour chaque
valeur de i entre 0 et 999 la valeur sigma(i) et si cette valeur est n on renvoie i :
def a n t e c e d e n t ( n ) :
# on d é f i n i t une n o u v e l l e f o n c t i o n
f o r i in range ( 1 0 0 0 ) :
i f sigma ( i ) == n :
# s i l a v a l e u r de sigma ( i ) e s t n , c ’ e s t
return i
# que i e s t l ’ a n t é c é d e n t d e n
3. Écrivez une fonction cycle(n) qui prend en argument un entier n et qui calcule la taille du cycle
de n dans la permutation sigma.
R. Ici, on va définir une variable c qui servira de compteur pour compter le nombre d’éléments dans
le cycle. Initialement, c vaut 1 parce qu’on ne connaît qu’une seule valeur dans le cycle (n). On fait
ensuite une boucle while qui calcule les images successives de n par sigma jusqu’à ce qu’on retombe
sur n (on a alors parcouru tout le cycle).
À chaque nouvelle valeur rencontrée différente de n, on incrémente la variable i (pour compter le
nombre total de valeurs distinctes dans le cycle). Lorsque l’on retombe sur la valeur n (après avoir
parcouru tout le cycle, et en sortant de la boucle while), il suffit de renvoyer la valeur de la variable
c:
2. La notation J0, nK désigne l’intervalle d’entiers compris entre 0 et n, donc J0, nK = {0, 1, 2, . . . , n}
3
def c y c l e ( n ) :
c = 1
x = sigma ( n )
while x ! = n :
x = sigma ( x )
c = c + 1
return c
#
#
#
#
#
#
on i n i t i a l i s e l e c o m p t e u r
on c a l c u l e l ’ i m a g e d e n p a r s i g m a
t a n t que x e s t d i f f é r e n t d e n
on a v a n c e d a n s l e c y c l e
on i n c r é m e n t e c
quand on s o r t d e l a b o u c l e , on r e n v o i e c
4. Écrivez un programme qui vérifie que la fonction calculée par sigma est bien une permutation de
J0, 999K : il faut vérifier que l’image de tout nombre de J0, 999K est bien dans J0, 999K et que chacun de
ces nombres a bien un antécédent (la fonction doit répondre True si sigma est bien une permutation
et False sinon).
R. Ici, il faut regarder chaque valeur de n entre 0 et 999. Pour chacune de ces valeurs, il faut vérifier
que l’image de n par sigma est comprise entre 0 et 999 et que n a bien un antécédent :
def v e r i f i e ( ) :
f o r n in range ( 1 0 0 0 ) :
i f not ( 0 <= sigma ( n ) <= 9 9 9 ) : # s i l ’ i m a g e s i g m a ( n ) n ’ e s t p a s e n t r e
return False
# 0 e t 9 9 9 , on r é p o n d F a l s e
e l i f a n t e c e d e n t ( n ) == None : # s i l a f o n c t i o n a n t e c e d e n t ne t r o u v e
return False
# p a s d ’ a n t e c e d e n t , on r e n v o i e F a l s e
r e t u r n True
# s i aucune e r r e u r n ’ e s t t r o u v é e −> True
4