• Aucun résultat trouvé

Disque dur à deux têtes

N/A
N/A
Protected

Academic year: 2022

Partager "Disque dur à deux têtes"

Copied!
7
0
0

Texte intégral

(1)

Ce sujet est librement adapté du sujet de l’école polytechnique 2006 (MP/PC). Il estfortement recom- mandéde traiter les questions dans l’ordre et d’éviter d’en sauter.

Disque dur à deux têtes

On attachera une grande importance à la concision, à la clarté, et à la précision de la rédaction. Les programmes doivent être rédigés en Python.

Le temps d’exécutionT(f)d’une fonctionf est le nombre d’opérations élémentaires (addition, soustrac- tion, multiplication, division, affectation, etc.) nécessaire au calcul def. Lorsque ce temps d’exécution dépend d’un paramètre n, il sera notéTn(f). On dit que la fonction f s’exécute en temps O(nα), s’il existeK >0tel que pour toutn >0,Tn(f)≤Knα.

Ce problème étudie des stratégies de déplacement des têtes d’un disque dur afin de minimiser le temps moyen d’attente entre deux requêtes au disque dur (de lecture ou d’écriture). Dans ce problème, le disque dur est représenté par une demi-droite[0,+∞)et possède deux têtes de lecture/écriture. Chacune des têtes peut aller indifféremment à n’importe quelle position sur le disque pour y lire ou écrire une donnée.

Les deux têtes peuvent être au même endroit ou encore se croiser. On ne s’intéresse qu’aux temps de déplacement des têtes et non aux temps de lecture/écriture. Les deux têtes ont la même vitesse de déplacement. Le temps de déplacement d’une tête est supposé égal à la distance qu’elle parcourt.

Une requête r est un entier positif ou nul représentant l’emplacement du disque auquel l’une des deux têtes doit se rendre. Initialement les deux têtes sont chacune à la position0.

Le disque dur est muni d’une mémoire (appelée cache) qui permet d’enregistrernrequêtes (n >0) avant de les traiter. À chaque bloc denrequêtes présentes dans le cache, le contrôleur du disque dur doit alors satisfaire ce bloc de requêtes, dans leur ordre d’arrivée, en minimisant le déplacement total des deux têtes. L’ordre importe puisqu’une opération d’écriture peut précéder une autre opération de lecture ou d’écriture. Il faut donc déterminer pour chacune desn requêtes le numéro de la tête à déplacer de manière à minimiser la somme totale des temps de tous les déplacements.

Notes de programmation : On rappelle qu’un programme python peut prendre en argument des listes ou les renvoyer. On pourra si besoin définir des sous-fonctions, à condition d’indiquer par une phrase à quoi elles servent. abs(x) renvoie la valeur absolue dex. Sauf mention du contraire, un programme ne doit pas modifier une liste donnée en argument. On peut créer une liste de taille k remplie de x avec [x]* k. La taille d’une liste s’obtient avec len(li). On peut accéder au k-ème élément (pour 0 ≤ k <len(li)) d’une liste li avecli[k], et on peut en changer la valeur avec li[k] = x. On peut ajouter un élément xà la fin d’une liste li avec li.append(x), et cette fonction ne renvoie rien. On peut supprimer un élément à la fin d’une liste avecli.pop(), et cette fonction renvoie l’élément ainsi supprimé. On peut créer une matrice de dimensionsn×p(n lignes,pcolonnes) remplie avec la valeur x avec mat = [[x for j in range(p)] for i in range(n)]. mat[i][j] correspond alors à l’élément situé sur lai-ème ligne etj-ème colonne (avec 0≤i < net0≤j < p).

Il est interditd’utiliser toute autre fonction spécifique du langage (tranches, sort, index, count, etc.) sans les avoir définies auparavant.

Enfin, on considèrera qu’on a défini une constanteinfini = −1, qui sera notée∞dans la suite du sujet.

On posera par convention1 que∀x,∞ ≥xet∞+x=∞

Partie I

A. Cout d’une séquence de déplacements

Un bloc de n requêtes est représenté par n entiers positifs ou nuls (r1, r2, . . . , rn) rangés dans une liste req de taille n. Une séquence de déplacements (d1, d2, . . . , dn) est une suite de n entiers, 1 ou 2, rangés dans une liste d indiquant à l’étape iqui de la première tête (d[i−1] == 1) ou de la deuxième tête (d[i−1] == 2) doit se déplacer à la position req[i−1] (1 ≤ i ≤ n). Le cout d’une séquence de déplacements est la somme totale des distances parcourues par chacune des têtes.

Ainsi, pour le bloc de requêtes(5,2,4), le cout de la séquence de déplacements(1,1,2)est5 + 3 + 4 = 12, alors que le cout de(1,2,1)vaut5 + 2 + 1 = 8.

1Attention cependant : python ne connait pas cette convention !

(2)

IQuestion 1 Écrire une fonctioncout_de(req,d)qui calcule le cout d’une séquence de déplacements dpour le bloc de requêtesreq.

IRéponse 1

def cout_de ( req , d ):

pos1 = 0 pos2 = 0 n = len( req ) distance = 0 for i in range( n ):

if d[i ] == 1:

distance += abs( pos1−req [i ]) pos1 = req [ i ]

else:

distance += abs( pos2−req [i ]) pos2 = req [ i ]

return distance

Le cout minimal d’une suite de requêtesreq est le plus petit cout des séquences de déplacements satis- faisant le bloc de requêtesreq.

IQuestion 2 Montrer qu’il existe toujours une séquence de déplacements de cout minimal qui com- mence par1, c’est-à-dire commençant par déplacer la première tête.

IRéponse 2 Il existe un nombre fini de séquences (on peut choisir n fois entre la première et la deuxième tête, soit2n possibilités). Il y en a donc au moins une qui a un cout minimal.

Si celle-ci commence par un 1, le problème est réglé. Sinon, on peut inverser chaque 1 en 2. De manière immédiate, cette nouvelle séquence a le même cout, qui est donc minimal, et elle commence désormais par un 1.

IQuestion 3 Décrire (par des phrases, sans le programmer) un algorithme naïf qui permettrait de déterminer le cout minimal d’une suite de requêtesreq.

Quelle serait sa complexité ?

IRéponse 3 On pourrait énumérer les 2n combinaisons. Il faudrait alors pour chaque calculer son cout, ce qui peut s’obtenir enO(n), pour extraire celle de cout optimal. Au final, on serait donc avec une complexitéO(n2n).

B. Cout minimal pour deux requêtes

Dans cette partie, le cache est de taille 2 (n = 2). Il n’y a donc que deux requêtes r1 et r2. Par convention, la première tête sera toujours celle qui bouge sur la première requête.

IQuestion 4 Donner une séquence de déplacements de cout minimal pour chacun des deux blocs de requêtes(10,3)et (3,10).

IRéponse 4 Dans cette situation, on n’a que deux possibilités : (1,1) et(1,2).

Pour(10,3),(1,1)a un cout de 17 et(1,2)de 13. C’est donc(1,2)qui est optimal.

Pour(3,10),(1,1)a un cout de 10, contre 13 pour(1,2). C’est donc(1,1)qui est optimal.

IQuestion 5 Écrire une fonctioncout_minimal_2(r1, r2) qui retourne une liste de taille 2, donnant une séquence de déplacements de cout minimal pour le bloc[r1, r2].

(3)

IRéponse 5 Explication : après le premier déplacement, la première tête est en r1, et la deuxième en 0. Pour satisfaire la deuxième requête, la première doit effectuer|r1−r2|, la deuxième doit effectuerr2. Il faut donc comparer les deux.

def cout_minimal_2 ( r1 , r2 ):

if r2 < abs(r1−r2 ):

return [1 , 2]

else:

return [1 , 1]

Partie II

A. Cout minimal pour trois requêtes

Dans cette partie, le cache est de taille 3 (n= 3). Il y a donc trois requêtesr1,r2etr3. Par convention, la première tête sera toujours celle qui bouge sur la première requête.

IQuestion 6 On suppose qu’on a utilisé un algorithme glouton pour ce cas là : l’algorithme de la question 5 a été étendu en appliquant à la troisième requête la même règle de décision que pour la deuxième. Appliquer cet algorithme en justifiant la réponse sur l’exemple(20,9,1).

IRéponse 6 Sur cet exemple, la première tête va d’abord en 20. Pour satisfaire à la deuxième, c’est la deuxième tête qui se déplace en 9 (car9<|20−9|). À ce moment, c’est bien la deuxième tête qui est la plus proche de la troisième requête. On aura donc utilisé le déplacement(1,2,2) pour un cout total de20 + 9 + 8 = 37.

IQuestion 7 Énumérer toutes les stratégies possibles sur l’exemple de la question précédente. En déduire que l’algorithme glouton de la question 6 ne fournit pas nécessairement la solution de cout minimal.

IRéponse 7 Les stratégies possibles sont au nombre de 4 :

• (1,1,1) donne un cout total de 39

• (1,1,2) donne un cout total de 32

• (1,2,1) donne un cout total de 48

• (1,2,2) donne un cout total de 37

La stratégie donnée par l’algorithme glouton n’est donc pas minimale dans ce cas précis : (1,1,2)serait meilleure.

IQuestion 8 Écrire une fonction cout_minimal_3(r1, r2, r3) qui retourne une liste donnant une séquence de déplacements de cout minimal.

IRéponse 8 Explication : On organise un « tournoi » entre les 4 stratégies possibles. s1à s4 sont ces stratégies. s5et s6sont les « finalistes ».

def cout_minimal_3 ( r1 , r2 , r3 ):

req = [ r1 , r2 , r3 ] s1 = [1 , 1, 1]

s2 = [1 , 1, 2]

s3 = [1 , 2, 1]

s4 = [1 , 2, 2]

if cout_de ( req , s1 ) < cout_de ( req , s2 ):

s5 = s1 else:

s5 = s2

if cout_de ( req , s3 ) < cout_de ( req , s4 ):

s6 = s3 else:

s6 = s4

(4)

if cout_de ( req , s5 ) < cout_de ( req , s6 ):

return s5 else:

return s6

B. Cout minimal pour n requêtes

Dans cette partie, on suppose qu’on a n requêtes (r1, . . . , rn). Pour simplifier la programmation, on rajoutera par convention quer0= 0, et les valeurs seront dans une listereq = [0,r1,...rn].

Désormais, les deux têtes peuvent effectuer indifféremment le premier mouvement.

Pour une stratégie donnée, à un instant donné, la configuration des têtes du disque dur est représentée par une paire(i, j)codant le numéro des deux dernières requêtes respectivement satisfaites par chacune des deux têtes : la première tête a satisfait en dernier la i-ème requête et la deuxième tête la j-ème requête. Par convention, la configuration initiale est (0,0) ; 0 signifie qu’une tête n’a satisfait aucune requête.

IQuestion 9 Écrire une fonctionconfig_pos(req, i, j) qui prend en entrée la liste req (avec le 0 au début), deux entiers i et j, et renvoie la liste des deux positions des deux têtes de lecture dans la configuration(i, j).

IRéponse 9

def config_pos ( req , i , j ):

return [ req [ i ], req [j ]]

À chaque instantk ∈ {1, n}, on associe une matrice de taille (n+ 1)×(n+ 1)notée coutk. L’élément coutk[i][j] est égal au cout minimal pour satisfaire lak-ème requête, en terminant sur la configuration (i, j). On pose coutk[i][j] =∞si aucune stratégie n’aboutit à cette configuration.

IQuestion 10 Expliquer comment calculer le cout minimal d’une suite de requêtes(r1, r2, . . . , rn)à l’aide de la matricecoutn.

IRéponse 10 Considérons la solution de cout minimale qui satisfait toutes les requêtes du bloc. Cette solution se termine sur un état (i, j), et son cout sera alors par définition lu dans la cellule coutn[i][j]

(le cout minimal est aussi en particulier le cout minimal terminant sur l’état (i, j)). Toutes les autres cellules contiendront alors soit ∞soit une valeur supérieure (parce que chaque cellule contient le cout d’une stratégie qui satisfait le bloc). Ainsi, il suffit de trouver la valeur minimale dans la matrice.

On peut également préciser qu’une des deux têtes devra satisfaire la requête finale, donc cette valeur minimale se lira sur la dernière ligne ou sur la dernière colonne.

IQuestion 11

a) Justifier que∀(i, j)∈J0;nK

2,cout1[i][j] =





r1 si(i, j) = (1,0) r1 si(i, j) = (0,1)

∞sinon b) Justifier que∀k∈J1;nK,∀(i, j)∈J0;nK

2, i6=ket j6=k⇒coutk[i][j] =∞. c) Justifier que∀k∈J1;nK,∀i∈J1;nK,∀j > k,coutk[i][j] =∞.

d) Montrer que∀k∈J1;nK,∀j ∈J0;nK,coutk[k][j] =coutk[j][k]

e) Pourk∈J1;nK, que peut-on dire decoutk[k][k] ?

f) Soitk >1, et soiti < k−1. Expliquer comment calculer la valeur decoutk[i][k]à partir des valeurs decoutk−1 et des valeurs des(ri)0≤i≤n.

g) Soitk >1. Expliquer comment on peut calculer la valeur decoutk[k−1][k]. Indication : cette formule est assez différente de la formule de la question précédente, et implique un calcul d’un minimum..

h) Justifier que les points précédents permettent de calculer la matrice coutk à partir de la matrice coutk−1, de la valeur deket des valeurs des(ri)0≤i≤n.

(5)

IRéponse 11

a) Après avoir satisfait r1, soit la première tête s’est déplacée, alors la configuration est (1,0), et le cout est r1, soit c’est la deuxième, la configuration est (0,1) et le cout est identique. Les autres configurations sont inaccessibles.

b) et c) Quand on vient d’avoir satisfait la requêterk, on sait qu’une des deux têtes vient de bouger en rk, et on a donc une configuration de type (i, k) ou (k, i). De plus, il est impossible d’arriver dans une configuration où une requête ultérieure aurait été satisfaite.

d) Comme déjà vu, on peut transformer une stratégie en inversant les rôles des têtes 1 et 2. Le cout total est alors inchangé. Ainsi, si on dispose d’une stratégie qui satisfaitkrequêtes et termine dans la configuration(i, j), on terminerait avec le même cout sur la configuration(j, i)en inversant les têtes.

On a donc toujourscoutk[i][j]≤coutk[j][i], et donc égalité par symétrie.

e) Une configuration ne peut être satisfaite par les deux têtes à la fois. On a donccoutk[k][k] =∞. f) Dans cette situation, la deuxième tête a satisfait la k-ème requête. Avant cela, c’est nécessairement

aussi elle qui avait satisfait la(k−1)-ème requête, car la dernière satisfaite par la première tête est la i-ème, et i < k−1. Ainsi, la deuxième tête a parcouru |rk−1−rk|, et le cout total est donc coutk[i][k] =coutk−1[i][k−1] +|rk−1−rk|.

L’optimalité n’est pas évidente, mais est garantie par « induction » : notre stratégie découle forcément d’une stratégie sur lesk−1premières requêtes, et toute amélioration sur lesk−1premières requêtes se traduirait par une amélioration de notre stratégie.

g) Sans doute la question la plus difficile du sujet, si on veut la faire correctement.

Dans cette situation, la deuxième tête a satisfait lak-ème requête, et la première la(k−1)-ème. On note j la dernière requête effectuée par la deuxième tête avant la k-ème. Cela signifie qu’après la (k−1)-ème requête, la configuration était(k−1, j). Le cout de notre stratégie est donc :

coutk−1[k−1][j] +|rk−rj| (ici encore, toute amélioration de la stratégie sur les (k−1)premières requêtes se traduirait par une amélioration du cout de notre stratégie, ce qui garantit que la stratégie utilisée pour les(k−1)premières requêtes était optimale.

Inversement, toute stratégie sur les(k−1) premières requêtes dont le cout serait coutk−1[k−1][j]

peut se prolonger en une stratégie sur les kpremières requêtes en déplaçant alors la deuxième tête, et son cout serait alorscoutk−1[k−1][j] +|rk−rj|.

On en déduit donc quecoutk[k−1][k] = min

0≤j≤k−2(coutk−1[k−1][j] +|rk−rj|).

h) On peut constater facilement qu’on peut traiter tous les cas : la plupart des cases de la matrices sont

∞par b). Il reste alors la k-ème ligne et lak-ème colonne, mais la matrice est symétrique par d), donc remplir lak-ème ligne suffit. Sur cette ligne, les éléments à droite de lak-ème colonne sont∞ par c). C’est le cas aussi dans la k-ème colonne par e). L’élément dans la (k−1)-ème colonne est rempli par g) et les précédents par f).

IQuestion 12 En déduire une fonctionmatrice_suivante(req, k, cout)qui prend en entrée un bloc de nrequêtesreq, un entierk >0 et la matrice de taille (n+ 1)×(n+ 1)correspondant à coutk−1 et renvoie la matrice correspondant àcoutk.

Attention : la requête à satisfaire dans la nouvelle matrice est bienrk=req[k], grâce à l’ajout du0au début dereq.

IRéponse 12

def matrice_suivante ( req , k , cout ):

n = len( req)−1

res = [[−1 for j in range(n +1)] for i in range( n +1)]

for i in range(k−1):

# Utilisation du f )

res [ i ][ k ] = cout [ i ][ k−1] + abs( req [k−1]−req [k ]) res [ k ][ i ] = cout [ i ][ k−1] + abs( req [k−1]−req [k ])

# Utilisation du g ) pour calculer res [k−1][k ] mini = cout [k−1][0] + req [k ]

(6)

for j in range(1 , k−1):

if cout [k−1][j] + abs( req [k ] − req [j] ) < mini : mini = cout [k−1][j] + abs( req [k] − req [j] ) res [k−1][k] = mini

res [ k ][ k−1] = mini return res

IQuestion 13 En déduire une fonction cout_minimal(req) qui renvoie le cout minimal du bloc de requêtesreq. Donner la complexité decout_minimalen fonction den.

IRéponse 13 On réalise une première fonction qui calculecout1 : def cout_1 ( req ):

n = len( req)−1

res = [[−1 for j in range(n +1)] for i in range( n +1)]

res [0][1] = req [1]

res [1][0] = req [1]

return res

Puis :

def cout_minimal ( req ):

n = len( req)−1

matrice = cout_1 ( req ) for k in range(2 , n +1):

matrice = matrice_suivante ( req , k , matrice )

# matrice est alors cout_n : il suffit de calculer le minimum de la derni è re ligne . mini = matrice [−1][0]

for j in range(1 , n ):

if matrice [−1][j ] < mini : mini = matrice [−1][j]

return mini

On se rend compte que les matricescoutk sont assezcreuses : seule lak-ème ligne et lak-ème colonnes contiennent autre chose que des∞. Comme de plus la matrice est symétrique, seule une ligne est utile.

IQuestion 14 Ré-écrire une nouvelle fonctioncout_minimal_opt(req)qui calcule le cout minimal du blocreqen n’utilisant qu’une liste coutde taille n+ 1. Évaluer sa nouvelle complexité.

IRéponse 14 Du coup, on refait tout mais en mode ligne : def liste_suivante_opt ( req , k , liste ):

n = len( req)−1 res = [−1] * ( n +1) for i in range(k−1):

# Utilisation du f )

res [ i ] = liste [ i ] + abs( req [k−1]−req [k ])

# Utilisation du g ) pour calculer res [k−1][k ] mini = liste [0] + req [ k]

for j in range(1 , k−1):

if liste [ j ] + abs( req [k] − req [j] ) < mini : mini = liste [ j] + abs( req [k ] − req [ j] ) res [k−1] = mini

return res

def cout_opt_1 ( req ):

n = len( req ) − 1 res = [−1] * ( n +1) res [0] = req [1]

return res

def cout_minimal_opt ( req ):

(7)

n = len( req)−1

liste = cout_opt_1 ( req ) for k in range(2 , n +1):

liste = liste_suivante_opt ( req , k , liste ) mini = liste [0]

for j in range(1 , n ):

if liste [ j ] < mini : mini = liste [ j]

return mini

Références

Documents relatifs

Ce câble SATA vers USB est un adaptateur de lecteur externe offrant un accès simple et rapide à un disque SATA via le port USB-A d'un ordinateur portable.. Cet adaptateur de SSD

Appuyez sur la flèche Haut ou Bas pour sélectionner le nouveau volume (disque dur multimédia) ou le périphérique de stockage USB attaché (l'emplacement à partir duquel vous

Remettez le capot du système en place et fixez-le, rebranchez le câble d'alimentation et allumez le système. La procédure d'installation du disque dur est terminée. Au démarrage,

• Vérifier que le disque dur a bien été installé dans le boîtier avant de connecter celui-ci à l’ordinateur.. • Un nouveau disque dur doit être initialisé et

Si vous avez créé un nouveau groupe et souhaitez attribuer un ou plusieurs utilisateurs à ce groupe, cliquez d’abord sur Enregistrer comme susmentionné, puis cliquez sur le

Objectif : Déterminer le temps d’accès à un fichier en caractérisant les mouvements de rotation autour d’un axe fixe des plateaux et des têtes de

En revanche, j'aimerais démontrer que cette présomption déploie essentiellement ses effets dans les rapports entre associés (internes) et que la protection de la

Si vous voulez supprimer la partition et donc revenir à votre disque dur d'origine avec son espace d'origine, il faut retourner sur : Panneau de configuration &gt;