Modèles PRAM et threads Java
par Eric Goubault

 Login :  Mot de passe :

Tri PRAM

On considère l'algorithme de tri, décrit informellement comme suit. Soit \[l=(l_1,\ldots,l_n)\] une liste de n entiers distincts. On construit une grille d'entiers n fois n, où l'entrée (i,j) est égale à 1 si \[l_i > l_j\] comme indiqué sur l'exemple ci-dessous pour la liste l=(7,8,3,1): \[ \begin{array}{r|rrrr} & 7 & 8 & 3 & 1 \\ \hline 7 & 0 & 0 & 1 & 1 \\ 8 & 1 & 0 & 1 & 1 \\ 3 & 0 & 0 & 0 & 1 \\ 1 & 0 & 0 & 0 & 0 \\ \end{array} \] Ensuite, on somme les entrées par ligne, pour obtenir une liste d'entiers \[r=(r_1,\ldots,r_n)\] Ainsi pour notre exemple, r=(2,3,1,0). On définit maintenant une nouvelle liste l' à partir de l, en la réordonnant par r: \[l'_{r_i+1}=l_i, \mbox{ } i=1,\ldots,n\] Ainsi, on obtient l'=(1,3,7,8), qui est l triée.

Question 1

On suppose que l'on dispose d'une machine PRAM CREW (voir le poly section 2.1 page 23) et que dans sa mémoire partagée on ait stocké l. Ecrire l'algorithme de tri correspondant. Quelle est la complexité de l'algorithme?

Question 2

Prouver que pour toute liste de taille n, ce procédé opère un tri.

Question 3

Quelle est l'efficacité de l'algorithme (voir le poly section 2.4 page 31) de la question 1? Peut-on améliorer celle-ci? Que se passe t-il si on n'a plus qu'une machine EREW?

Récurrences linéaires

Le problème qui nous préoccupe dans cette section est de calculer efficacement, en parallèle, des suites récurrentes linéaires d'ordre m > 0, de la forme (pour i > -1): $$\begin{array}{lcl} y_0 & = & a_{0,0} \\ \cdots & \cdots & \cdots \\ y_{m-1} & = & a_{m-1,0} \\ y_{m+i} & = & a_{m+i,m} y_{m+i-1} + \cdots + a_{m+i,1} y_{i} + a_{m+i,0} \\ \end{array}$$ Comme applications visées, on a par exemple:

Récurrences d'ordre 1

On se place dans le cas de récurrences linéaires d'ordre 1 (m=1): c'est à dire, \[\begin{array}{lcl} y_0 & = & a_{0,0} \\ y_{i+1} & = & a_{i+1,1} y_{i} + a_{i+1,0} \\ \end{array}\] et on veut calculer \[y_i, \mbox{ } 0 \leq i \leq n-1\] (pour un n donné, que l'on pourra supposer être une puissance de 2).

Question 1

On suppose que $$a_{i,1}=1$$ Comment calculer efficacement les n premiers éléments de la suite y sur une machine PRAM EREW, CREW, CRCW? Quelle sera l'efficacité de l'algorithme?

Question 2

On se place maintenant dans le cas d'une récurrence linéaire d'ordre 1 générale, c'est à dire que les coefficients sont maintenant quelconques. Donner la formule générale pour la nième entrée de la suite en fonction des coefficients \[a_{i,1} \mbox{ et } a_{i,0}\]

Question 3

A l'aide de la question précédente, comment modifier l'algorithme de la question 1 pour calculer efficacement \[y_n, \mbox{ pour un } n \mbox{ fixé}\] sur une machine PRAM CREW, en utilisant la technique de saut de pointeur?

Question 4

Quelle est l'efficacité de cet algorithme? Justifier brièvement.

Question 5

Programmer l'algorithme de la question 3 en Java. On écrira donc successivement: On écrira enfin une classe Recurrence.java qui prend à la ligne de commande k (le logarithme en base 2 de la taille de la récurrence à considérer) et on testera la récurrence avec les cas suivants:
import java.util.*;

public class Recurrence {
  public static void main(String[] args) {
    int k = Integer.parseInt(args[0]):
    ...

    // test somme de n entiers tirés aléatoirement
    Random r = new Random();
    // on tire un nouvel élément entre 0 et n-1 par r.nextInt(n)
    ...

    // test y(i+1)=2y(i)+a(i) où a est un tableau aléatoire
    ...

Déposez Recurrence.java.

Solution récursive

On constate que la solution précédente est lourde, lente et n'est pas générale. Une autre façon de faire est de construire un algorithme diviser pour régner parallèle, plus général que le saut de pointeurs, en une étape, comme dans la question suivante.

Question 6

Exprimer \[y_{i} \mbox{ en fonction de } y_{i-2}\]

Question 7

En déduire un algorithme récursif permettant de calculer toutes les n première entrées de la suite y (pour n une puissance de 2).

Récurrence linéaire d'ordre supérieur

On se place maintenant dans le cas général m > 0, et on suppose disposer d'un algorithme PRAM permettant de faire la multiplication de deux matrices carrées m fois m en temps O(log(m)), et utilisant O(M(m)) opérations. On pourra supposer par exemple $$M(m)=O(m^{2.376})$$ sur de l'ordre de m au cube processeurs.

Question 8

Comment améliorer l'algorithme de la question 3 afin de calculer les n premières entrées de la suite y, sur une PRAM, dans le cas d'une récurrence d'ordre m? Quelle est l'efficacité de l'algorithme sur une PRAM CREW? Justifier brièvement.

Threads récursifs: Fibonacci

Ecrire un calcul de la fonction f de Fibonacci, définie par la récurrence,

en utilisant des threads JAVA. L'idée est de paralléliser l'algorithme récursif naturel permettant de calculer f. Mais pour calculer f(n+2), au lieu de s'appeler soi-même deux fois pour calculer f(n+1) et f(n), on créera un thread pour calculer f(n+1) et un autre pour calculer f(n). Ainsi, on définira une classe Fibon dont la méthode run() sera en charge de calculer la fonction f. On construira la classe Fibon contenant un champ argument, et un champ résultat de type int comme plus bas :
public class Fibon extends Thread {
    private final int x;
    int result;

    public Fibon(int x) { ... }

    public void run() { ... }


On programmera donc,

Déposez Fibon.java.