IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Threads & Processus C++ Discussion :

Nombres Premiers


Sujet :

Threads & Processus C++

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2014
    Messages
    12
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : France, Gard (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2014
    Messages : 12
    Points : 9
    Points
    9
    Par défaut Nombres Premiers
    Bonjour,

    Je débute dans la programmation multithread, et je bloque sur l'implantation du Crible d’Ératosthène.

    Tout d'abord voici l'algo séquentiel :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    #include <iostream>
    #include <stdlib.h>
    using namespace std;
     
    int main(int argc, char **argv)
    {
    	const int	MAX = atoi(argv[1]);
    	bool		tab[MAX+1];
     
    	for (int i = 0; i < MAX+1; i++) //Initialisation du Tableau à true
    		tab[i] = true;
    	tab[0] = false;
    	tab[1] = false;
     
    	for (int i = 2; i < MAX+1; i++) //Crible d'érastothène
    	{
    		if (tab[i])
    		{
    			for (int j = 2*i; j < MAX+1; j = j + i)
    				tab[j] = false;
    		}
    	}
     
            cout << "Nombres premiers jusque " << MAX << " :" << endl; //Affichage du Tableau
    	for (int i = 0; i < MAX+1;  i++)
    	{
    		if (tab[i])
    			cout << i << "\t ";
    	}
    	cout << endl;
     
    	return (0);
    }
    Après réflexion, je pense qu'il est impossible de découper la première boucle en plusieurs Threads sans avoir d'erreur, car le principe du Crible d’Ératosthène est d'éliminer tous les multiples de 2 et ensuite les multiples de 3, 5 etc ... Il y a des chances que le Thread démarre sur un nombre non encore traité par un Thread précédent, donc qu'il débute par un nombre non premier.

    Alors j'ai eu l'idée de découper la deuxième boucle, par exemple pour 4 threads et 100 en paramètre, on aurait le premier Thread qui élimine tous les multiples de 2 dans l'intervalle [1,25], le 2nd [26,50], le 3ème [51,75] et enfin le dernier [76,100] et ainsi de suite on fait de même pour 3, 5, 7 etc ...

    Pour cela j'ai crée une Structure data :
    - Un pointeur pour pointer sur le tableau tab.
    - size, l'intervalle sur lequel le Thread va travailler. (ex : 25)
    - Le min, c'est à partir de là que le Thread va éliminer les multiples. (ex : 51)
    - Le max. (ex : 75)
    - i, qui correspond au nombre premier auquel on va éliminer ses multiples (ex : 2, 3, 5, 7 etc ..)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    struct data
    {
    	bool*			pTab;
    	unsigned int	size;
    	unsigned int 	i;
    	unsigned int	min;
    	unsigned int	max;
    };
    Ensuite le problème c'est que mon programme ne me renvoit pas du tout les nombres premiers, je pense qu'il doit y avoir un problème dans l'attente des threads du nombre premier précédent, du coup l'algo s'enchaine sur un nombre non premier.

    Voici mon code du main :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    int main(int argc, char **argv)
    {
    	const unsigned int	MAX = atoi(argv[1]);
    	pthread_t thtab[NBTHREADS];
    	bool * tab = new bool[MAX+1];
     
    	for (unsigned int i=0; i<MAX+1; ++i) //Initialisation du tableau à true
    		tab[i] = true;
    	tab[0] = false;
    	tab[1] = false;
     
    	unsigned int partSize = MAX / NBTHREADS;
    	struct data * myStructs = new struct data[NBTHREADS];
    	for (unsigned int i=0; i<NBTHREADS ; ++i)             //Initialisation Structure
    	{
    		myStructs[i].size 	= partSize;
    		myStructs[i].pTab	= tab;
    		myStructs[i].i 		= 0;
    		if(i > 0)
    			myStructs[i].max	= myStructs[i-1].max + partSize;
    		else
    			myStructs[i].max	= partSize;
    		myStructs[i].min	= myStructs[i].max - partSize;
    	}
     
    	for (unsigned int i = 2; i < MAX+1; i++) //Crible d'érasthotène
    	{
    		if(tab[i])
    		{
    			pthread_join(thtab[0], NULL); /*Attente des Threads du Nombre Premier précédent, j'ai mis thtab[0] car juste thtab ne compile, peut-être que le problème vient d'ici.*/
    			for (unsigned int th=0; th<NBTHREADS; ++th)
    			{
    				myStructs[th].i = i;
    				pthread_create(&thtab[th], NULL, task, (void*) &myStructs[th]);
    			}
    		}
    	}
     
            cout << "Nombres premiers jusque " << MAX << " :" << endl; //Affichage du Tableau
        	for (unsigned int i = 0; i < MAX+1;  i++)
        	{
        		if (tab[i])
        			cout << i << "\t ";
        	}
        	cout << endl;
     
    	delete[] myStructs;
    	delete[] tab;
    	return (0);
    }
    Et enfin ma fonction Thread :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void * task(void * _data) 
    {
    	struct data  * localData = (struct data *) _data; //Pointeur sur ma structure
     
    	while ((localData->min)%(localData->i) != 0) //On fait en sorte que le minimum commence à partir d'un nombre multiple.
    	{
    		localData->min = localData->min + 1;
    	}
     
     
           for (unsigned int j = localData->min; j < localData->max; j = j + localData->i) //On élimine les multiples dans l'intervalle [min,max]
    			localData->pTab[j] = false;
    	return (NULL);
    }
    Je débute donc il est probable que c'est pas du tout la bonne technique que j’essaie d'employer, peut-être qu'il y a une implémentation plus logique et facile.

    Merci d'avance pour votre aide,

    Florian.

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    C'est la saison des Crible d’Ératosthène parallèle ?

    En résumé, paralléliser une aussi petite tache que la boucle intérieur, sans utilisation d'architecture SIMD (GPU et autres trou noir à transistors), c'est contre productif.

    Regardez du coté des algorithmes plus parallélisable que ce pauvre cribble.
    Ou faite des calculs de preuve de primalité via des théorèmes machin Fermat-truc.

    Si vous voulez vraiment paralléliser ce pauvre crible, c'est quand vous commencez à tomber sur des zones à faible densité de ces nombres. Vous faite des découpages de zone par thread, et comme la probabilité d'en trouvez un est faible, vous pouvez paralléliser la non découverte de nombre premier dans toutes les zones de tous les thread en un tour de boucle parallèle. Si vous avez la mal chance de trouver un nombre premier dans l'une des zones, vous n'avez qu'à tout refaire, mais uniquement dans ce tour de boucle, sur les mêmes zones mais en sachant qu'il existe un nouveau nombre premier à tenir compte.

  3. #3
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2014
    Messages
    12
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : France, Gard (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2014
    Messages : 12
    Points : 9
    Points
    9
    Par défaut
    Oui après avoir fait des recherches j'ai bien compris que paralléliser le Crible d’Ératosthène n'est pas vraiment la meilleure solution, mais je n'ai pas le choix c'est mon prof qui me l'impose.

    J'ai du mal à comprendre votre raisonnement, que voulez-vous dire par "zones à faible densité de ces nombres" ?

  4. #4
    Nouveau membre du Club
    Homme Profil pro
    Développeur Java
    Inscrit en
    Mai 2012
    Messages
    13
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Mai 2012
    Messages : 13
    Points : 33
    Points
    33
    Par défaut
    Bonjour,

    Deux petites remarques :

    Pour ce qui est du crible :

    Citation Envoyé par Flo FR Voir le message
    je pense qu'il est impossible de découper la première boucle en plusieurs Threads sans avoir d'erreur, car le principe du Crible d’Ératosthène est d'éliminer tous les multiples de 2 et ensuite les multiples de 3, 5 etc ... Il y a des chances que le Thread démarre sur un nombre non encore traité par un Thread précédent, donc qu'il débute par un nombre non premier.
    C'est contre productif mais ça ne fausse pas le résultat.

    Supposons qu'il y ait 3 threads A, B et C. A s'occupe de 5 mais a perdu la main à 10, 15 n'est donc pas supprimé. B est sur 7. C vient de finir 3 et se cherche un nouveau nombre. Il trouve 15. Il supprime donc les multiples de 15. Or ceux-ci seront de toutes manières supprimé dans pas longtemps par A. C'est donc inutile, mais pas faux.

    Autrement dit, le critère pour être : on supprime les multiples d'entiers, premier ou non. C'est théoriquement correcte mais pas performant.

    Pour l'implémentation :

    Petite précision, cela fait longtemps que je n'ai plus fait de c/c++ donc gardez un œil criticque sur ce que je vais dire.

    J'ai un gros doute sur la boucle suivante dans ton thread :

    Citation Envoyé par Flo FR Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    void * task(void * _data) 
    {
    	struct data  * localData = (struct data *) _data; //Pointeur sur ma structure
     
    	while ((localData->min)%(localData->i) != 0) //On fait en sorte que le minimum commence à partir d'un nombre multiple.
    	{
    		localData->min = localData->min + 1;
    	}
     
    }
    Précisement, "localData->min = localData->min + 1;" modifie la valeur dans ton struct et c'est donc une modification qui perdurera de lancement de thread en lancement de thread. Et c'est problématique.

    Hypothèse :
    Un thread doit nettoyer les non premiers de 30 à 39.
    On a donc un struct suivant :
    size =
    i = ?
    min = 30
    max = 40

    On lui demande de traiter 7. Cette boucle va changer min = 30 en min = 35.

    Une fois fini, on demande au même thread de s'occuper de 11. Mais comme min et à 35, il ne redescendra jamais à 33. En fait elle va s'arrêter à 44 ! Au dessus de 40. Ce thread ne supprimera plus jamais un nombre quel qu’il soit.

    P.S. :
    Bon en fait mon exemple à 33 et 11 n'est pas très bon. De toutes façons 33 a déjà été supprimé en tant que multiple de 3. Et à partir de 7 (7*7=49) la tranche [30,40[ ne contient plus que des premiers.

    J'en ai cherché un plus propre. Voici :
    On choisit l'intervalle [160,170[
    Avec i à 2 le min ne bouge pas (=160).
    Avec i à 3 il passe à 162.
    Avec i à 5 il passe à 165 (on part de 162 et pas de 160).
    Avec i à 7 il passe à 168
    Il n'y a pas de multiple de 11 dans [168, 170[ ( 11*15 = 165 (mais 165 est déjà supprimé en tant que multiple de 5) et 11*16 = 176)
    Donc on ne supprimera plus de nombre dans cette tranche. Or 13*13 = 169.

    169 ne sera jamais supprimé alors qu'il devrait.

  5. #5
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    La densité de nombre premier décroit très rapidement avec la valeur des nombres.
    Plus un nombre est grand moins il y a de chances pour qu'il soit premier.

    Pour que la parallélisassion soit "efficace" il faut que les threads travaillent sur des plages de valeurs grandes, prenons arbitrairement 100000.

    Sous Nthread = le nombre de threads mises en jeux.

    Il faut donc commencer par calculer séquenciellement les nombres premiers jusqu'a une valeur suffisante X pour que des plages valeurs de X + (100000 valeur *Nthread) est une probabilité faible de contenir au moins un nombre premier.

    Donc de X à X+ (100000 * Nthread), vous faites un cribble "standard" entre les valeur [X+numéro de thread*100000,(X+numéro de thread +1)*100000[, avec numéro de thread dans l'intervalle [0,le nombre de threads mises en jeux].
    Vous faites ces cribbles avec ces Nthread thread en parallèle et ils auront terminés à peu près en même temps leur tache car leurs plages de valeur ont la même taille.

    A la fin de cette étape, soit un nouveau nombre premier a été découvert par l'un des thread : NouveauPremier et il faudra reprendre le calcul mais en commençant à NouveauPremier et plus à X. La plage de valeur du thread 0 va de [NouveauPremier, NouveauPremier+100000[, pour le thread 1 [NouveauPremier+1000000, NouveauPremier+200000[, ...
    Soit il n' a pas de nouveau nombre premier entre X et X+ (100000 * Nthread) et on recommence le cribble parallèle mais en commençant à X+(100000* Nthread) et plus à A.

    Vous pouvez utiliser ce principe de "round" autant de fois que nécessaire, chaque round prenant pour X la valeur X du round précédent + (100000* Nthread).
    Le round étant le calcul de la primalité fait par Nthread en parallèle sur un intervalle de 100000 valeur, chacun ayant un intervalle de valeur distinct.

    Si 100000 est trop petit pour compenser l'initialisation des threads ou le passage des paramètres à ces threads si vous utilisez un pool de thread, vous pouvez utiliser le même principe mais sur des intervalles de valeur plus grands.

  6. #6
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2014
    Messages
    12
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : France, Gard (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2014
    Messages : 12
    Points : 9
    Points
    9
    Par défaut
    Citation Envoyé par R. Daneel Olivaw Voir le message
    Pour l'implémentation :

    Petite précision, cela fait longtemps que je n'ai plus fait de c/c++ donc gardez un œil criticque sur ce que je vais dire.

    J'ai un gros doute sur la boucle suivante dans ton thread :
    Citation Envoyé par Flo FR
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    while ((localData->min)%(localData->i) != 0) //On fait en sorte que le minimum commence à partir d'un nombre multiple.
    	{
    		localData->min = localData->min + 1;
    	}
    Précisement, "localData->min = localData->min + 1;" modifie la valeur dans ton struct et c'est donc une modification qui perdurera de lancement de thread en lancement de thread. Et c'est problématique.
    ...
    169 ne sera jamais supprimé alors qu'il devrait.
    Je vois ce que vous voulez dire, mais non cela ne modifie pas la valeur dans ma struc.
    En effet j'ai réussi avec un mutex dans ma structure à paralléliser les threads et j'obtiens bien les nombres premiers, mais le programme met plus de temps que le simple programme séquentiel :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
     
    #include <pthread.h>
     
    #include <cstdlib>
    #include <iostream>
    #include <stdlib.h>
     
    #define NBTHREADS 4
     
    using namespace std;
     
    struct data
    {
    	int				MAX;
    	int				MIN;
    	bool*			pTab;
    	pthread_mutex_t mut;
    	int				i;
    };
     
    void initialiserTab(bool tab[], const int* MAX)
    {
    	for (int i = 0; i < *(MAX)+1; i++)
    		tab[i] = true;
    }
     
    void affNbrPremiers(bool tab[], const int* MAX)
    {
    	cout << "Nombres premiers jusque " << *(MAX) << " :" << endl;
    	for (int i = 0; i < *(MAX)+1;  i++)
    	{
    		if (tab[i])
    			cout << i << "\t ";
    	}
    	cout << endl;
    }
     
    void *task (void* p)
    {
    	struct data *p_data = (struct data *) p;
    	if (p != NULL)
       {
    		pthread_mutex_lock (&p_data->mut);
    		while ((p_data->MIN)%(p_data->i) != 0)
    		{
    			p_data->MIN = p_data->MIN + 1;
    		}
    		for (int j = 2*p_data->i; j < p_data->MAX+1; j = j + p_data->i)
    				p_data->pTab[j] = false;
    		pthread_mutex_unlock (&p_data->mut);
       }
       return (NULL);
    }
     
    int main(int argc, char **argv)
    {
    	const int 		MAX			= atoi(argv[1]);
    	bool			tab[MAX+1];
    	pthread_t		thtab[NBTHREADS];
    	unsigned int	partSize	= MAX / NBTHREADS;
    	struct data *	myStructs	= new struct data[NBTHREADS];
     
    	for (unsigned int i=0; i<NBTHREADS ; ++i)             //Initialisation Structure
    	{
    		myStructs[i].pTab = tab;
    		myStructs[i].mut = PTHREAD_MUTEX_INITIALIZER;
    		if(i > 0) //Interval MAX
    			myStructs[i].MAX	= myStructs[i-1].MAX + partSize;
    		else
    			myStructs[i].MAX	= partSize;
    		if (i > 0) //Interval MIN
    			myStructs[i].MIN	= myStructs[i].MAX - partSize;
    		else
    			myStructs[i].MIN	= 2;
    	}
    	if (MAX%NBTHREADS != 0) //Si le nombre MAX n'est pas divisible équitablement, alors on augmente l'interval MAX du dernier thread.
    		myStructs[NBTHREADS-1].MAX = (myStructs[NBTHREADS-1].MAX) + (MAX%NBTHREADS);
     
    	initialiserTab(tab, &MAX); //Initialisation du tableau
    	tab[0] = false;
    	tab[1] = false;
     
    	for (int i = 2; i < MAX+1; i++) //Crible d'Eratosthène
    	{
    		if (tab[i])
    		{
    			for (unsigned int y = 0; y < NBTHREADS; y++)
    			{
    				myStructs[y].i = i; //Initialisation de l'élemént i de la structure au nombre premier i.
    				pthread_create (&thtab[y], NULL, task, &myStructs[y]); //Création des threads
    			}
    			for (unsigned int z = 0; z < NBTHREADS; z++) //Attente des threads
    				pthread_join (thtab[z], NULL);
    		}
    	}
     
    	affNbrPremiers(tab, &MAX);
    	delete[] myStructs;
    	return (0);
    }
    Mais par contre pour 10 millions j'ai une "erreur de segmentation (core dumped)", le tableau a donc une taille maximale ?

  7. #7
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    bin oui, la taille de l'espace restant de la stack si il est dans celles-ci (variable locale).
    La taille de l'espace restant dans le tas, s'il est alloué dynamiquement (et encore faut pas qu'il soit fragmenté par d'autres allocations).

Discussions similaires

  1. Réponses: 24
    Dernier message: 27/09/2005, 21h16
  2. [défi n°8]: premiers nombres premiers
    Par javatwister dans le forum Général JavaScript
    Réponses: 41
    Dernier message: 14/06/2005, 10h22
  3. [LG]Calcul des 15 premiers nombres premiers
    Par yffick dans le forum Langage
    Réponses: 12
    Dernier message: 18/09/2004, 14h57
  4. Cripter avec des nombres premiers
    Par clovis dans le forum Algorithmes et structures de données
    Réponses: 3
    Dernier message: 14/04/2004, 19h10
  5. premier nombre premier superieur à m=10^100+1
    Par azman0101 dans le forum Mathématiques
    Réponses: 4
    Dernier message: 17/04/2003, 03h23

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo