CS 315 TP4 Algorithme de justification o un fichier o Size o o o just.c contenant une int NbWords, const int* Lengths ) fonction int* Justify( int Size, est la largeur du paragraphe à justifier, NbWords le nombre de mots dans le paragraphe, et Lengths la longueur de chacun de ces mots la fonction retourne un tableau d'entiers, de longueur variable, contenant les indices des mots terminant chaque ligne du paragraphe. Il est donc au minimum de taille 1, et son dernier élément vaut forcément NbWords-1 la fonction ne doit PAS modifier les entrées vous pouvez vous servir du fichier main.c afin d'entrer le texte et afficher le résultat. Ce TP d’algorithmique va nous permettre d’implémenter un algorithme de mise en page, notamment ce qui concerne la justification d’un paragraphe. Le principe est le suivant : on a un texte sous forme de mots et on veut mettre le mot sous forme « justifié ». Pour cela : on essaie de serrer le texte pour que tout rentre sur une ligne et sinon soit on envoie le mot sur la ligne suivante soit on le coupe en deux. Nous commencerons par envisager seulement la possibilité d’envoyer le mot sur la ligne suivante en espaçant la ligne. On considère chaque cas (chaque ligne) au fur et a mesure du paragraphe, on traite le problème localement, nous allons donc implémenter un algorithme de type : glouton. L’idée de l’algorithme est : Imaginons un texte de mots m1 à mN, avec des longueurs l1 à lN. On veut organiser en colonne de taille C et en ligne (de longueur maxi c). Tout d’abord on détermine la taille d’une ligne de mots mi à mj : Taille ( i, j) = somme de longueur des mots + nb espaces = somme des longueurs lk( pour k de i à j) +( j – i) Algorithme de base glouton On considère que S est l’indice du mot qui termine la ligne Y est la ligne actuelle N est l’indice du mot à la fin du paragraphe S[0]=1 y=1 Pour x de S[y-1] à n, Si taille (de S[y-1] à x) > à c //On enlève dernier mot et on fait retour à la ligne c’est à dire S[y ] <- x-1 y<- y+1 Fin si Fin pour S[y]=n Cet algorithme est cependant un algorithme de base et il faudrait faire un algorithme plus réfléchi. En effet, cet algorithme se contente de faire un retour à la ligne lorsque le mot ne rentre pas sur la ligne au coup par coup. Nous voudrions, pouvoir optimiser notre paragraphe et faire en sorte de placer les mots de façon a ce qu’on ait jamais de gros « trous ». Exemple : alors qu’avec l’algorithme glouton, on aurait : Bla bla bla bla bla bla bla bla bla bla blabla bla bla bla bla bla bala blablablablablablablablablab bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb bb bbb bb bbbbbbbb Avec notre nouvel algorithme on aurait : Bla bla bla bla bla bla bla bla bla bla bla blabla bla bla bla bla bala blablablablablablablablablab bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb bb bbb bbb bb On optimise les espaces. Pour cela on essaie d’envisager le problème sous un autre angle. On crée une variable appelée coût, celle-ci calcule la place vide que nous coûte chaque ligne. En effet, si on ne place pas le mot en fin de ligne, il se peut que l’on ait de la place vide qui ne sera pas remplie et qui coûte donc des ressources pour rien. Ainsi, le paragraphe le mieux sera celui qui a le coût minimum. Coût (li)= (C-li) ^3 Avec li est Coût (total)= somme des coûts des li pour i de 1 à n Algorithme naïf Pour i=1 à… Pour J=I à … L1= taille de i à j Pour k = j+i à … L2= (l(j+k)….lk) On se rend compte que l’on va calculer plusieurs fois les mêmes coûts, ce qui va rendre l’algorithme plus complexe inutilement. On pourrait utiliser le coût trouvé précédemment et ainsi de suite de façon récursive. On va alors implémenter l’algorithme en programmation dynamique. On recalcule alors une expression du coût : Coût (1, i) = Coût (1, x) + Coût_ligne ( x+1, i) Coût idéal ( 1, i) = min (entre x=1 a i) du Coût (1, x) + Cout_ligne ( x+1, i) Algorithme de programmation dynamique : On cherche le coût du paragraphe qui va de 1 à n Pour i= 1 à n // n mots dans le paragraphe, on parcourt donc le paragraphe //cout idéal (1,i) ? C[i]= infini //initialisation de C afin qu’il soit maximum Pour x=1 à i //On parcourt chaque mot en partant du premier à chaque fois (c'est-à-dire : première boucle, on ne parcourt que m1, puis deuxième boucle, on parcourt m1 et m2 puis troisième boucle on parcourt m1, m2 et m3…) C=C[x] + (C- taille(x+1, i))^3 // le coût est égal au coût de la nouvelle ligne plus le cout du nouveau mot Si C< C[i] // si ce coût est un minimum C[i]<- C // alors on enregistre ce minimum X[i]<-x // et on note l’indice du dernier mot de la ligne comme étant l’indice du mot parcouru Fin si Fin pour Fin pour Le paragraphe se termine en X[n]. L’avant dernière ligne, se terminera en X[X[n]] et ainsi l’avant avant dernière ligne se terminera en X[X[X[n]]]…. Cela revient à dire que l’on regarde le mot qui termine le paragraphe du mot qui termine le paragraphe…. Pour la compréhension, on peut considérer que S[n] = X[n] et S[1] = X[X[X[X[X…[X[n]]]]]…] et ceci autant de fois qu’il y a de lignes. La complexité de l’algorithme précédent est : n boucle « pour », n boucle « pour » somme ( c-taille ( x…)) : teta de n On a donc du n* n*n c’est à dire du teta n^3 Or, le coût est 0 si c’est la dernière ligne Infini si taille ( i,j) >c (c – taille (i,j))^3 D’où C/2 mots -> taille > c/2 + C/2 >C ??? On peut donc changer l’algorithme pour baisser sa complexité Pour i= 1 à n //on a n mots dans le paragraphe //cout idéal (1,i) ? C[i]= infini //initialisation pour avoir C maximum car le bu t de l’algo est que C soit minimum Pour x= i – (C /2) à i C=D[x] + (C- taille(x+&, i))^3 Si C< C[i] C[i]<- C X[i]<-x Fin si Fin pour Fin pour La complexité devient du c*n² On doit donc adapter cet algorithme en langage C et l’optimiser si possible !! Pour la première ligne, les valeurs seront toutes minimales jusqu’à ce qu’on arrive à la fin de la ligne où là, les valeurs seront vraiment minimales, on notera donc que l’on aura des valeurs qui ne correspondent pas vraiment à un mini au début du tableau.