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

C++ Discussion :

Bellman-Ford algorithm problème


Sujet :

C++

  1. #1
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2014
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2014
    Messages : 126
    Points : 48
    Points
    48
    Par défaut Bellman-Ford algorithm problème
    Bonjour à tous,

    J'essaie d'implémenter l'algorithme de Bellman-Ford qui permet de calculer les plus courts chemins à partir de sommets sources dans un graphe orienté pondéré. Je crois que mon implémentation donne le bon résultat (ce que je ne peux vérifier) mais mon programme tourne beaucoup trop lentement. J'aimerais donc l'accélerer de manière significative. Voici le code :

    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
    101
    102
    #include <iostream>
    #include <array>
    #include <map>
    #include <fstream>
    #include <climits>
    #include <set>
     
    struct Edge
    {
    	unsigned int tail;
    	unsigned int head;
    	int length;
    } ;
     
    struct Key
    {
    	long unsigned int q;
    	long unsigned int t;
    };
     
    struct KeyCompare {
    	bool operator()(const Key& a, const Key& b){
    		return (a.q < b.q) or (a.q == b.q and a.t < b.t);
    	}
    };
     
    typedef std::map<Key, int, KeyCompare> Graph;
     
    Graph get_graph();
    int** initialize(unsigned int source);
    void compute(int** & tab);
    Edge compute_min_edge(unsigned int source, Graph g);
     
    Edge compute_min_edge(unsigned int head_, Graph g){
    	Edge e({INT_MAX, head_, INT_MAX});
    	for(unsigned int x(0); x < 1000; ++x){
    		if(g.find({x, head_}) != g.end()){
    			if(g.find({x, head_})->second < e.length){
    				e.length = g.find({x, head_})->second;
    				e.tail = x;
    			}
    		}
    	}
     
    	return e;
    }
     
     
    Graph get_graph(){
    	Graph graph;
    	std::string ligne("");
    	std::ifstream fichier("g1.txt", std::ios::in);
     
    	if(fichier){
    		getline(fichier, ligne);
    		while(getline(fichier, ligne)){
    			Key k = {stoul(ligne.substr(0, ligne.find(' ', 0)))-1, stoul(ligne.substr(ligne.find(' ', 0), ligne.find(' ', ligne.find(' ', 0) + 1)))-1};
    			graph[k] = stoi(ligne.substr(ligne.find(' ', ligne.find(' ', 0) + 1)));
    		}
    	}
     
    	return graph;
    }
     
    int** initialize(unsigned int source){
    	unsigned int dimX = 1000, dimY = 47978;
    	int** tableau;
     
    	tableau = new int*[dimX];
    	for(size_t x(0); x < dimX; ++x){
    		tableau[x] = new int[dimY];
    		for(size_t y(0); y < dimY; ++y){
    			tableau[x][y] = INT_MAX;
    			if(y == 0 and x == source)
    				tableau[x][y] = 0;
    		}
    	}
     
    	return tableau;
    }
     
    void compute(int** & tab, Graph g){
    	for(size_t i(1); i < 47978; ++i){
    		for(size_t j(0); j < 1000; ++j){
    			Edge e(compute_min_edge(j, g));
    			tab[i][j] = std::min(tab[i-1][j], tab[i-1][e.tail] + e.length);
    			std::cout << " i " << i << std::endl;
    		}
    	}
    }
     
     
    int main(int argc, char* argv[]){
     
    	Graph graph(get_graph());
     
    	int** tab(initialize(0));
     
    	compute(tab, graph);
     
    	return 0;
    }
    Comme vous pouvez le voir 3 fonctions sont appelées dans le main, les deux premières cumulées tournent en moins de 1 sec donc le problème vient de la fonction "compute". Or dans cette fonction il y a deux choses principales, une double boucle que je ne peux pas éviter à ma connaissance, des "look-up" répétés dans ma map qui contient mes arêtes. J'imagine que c'est donc ici que je perds du temps. Si quelqu'un a la moindre idée sur la manière d'accélerer mon programme que ce soit avec mes structures ou avec d'autres je vous serais reconnaissant d'en faire part , merci à tous.

  2. #2
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Premier réflexe, 47 millions de int? tu es sur de ton coup, là?
    Et en plus, il n'y a aucun delete...

    Après, ton code est un peu amusant, il est à mi chemin entre le C et le C++.
    Je commence à le restructurer un peu de mon coté, pour me creuser un peu la tête.

    Pourrais tu nous donner un petit fichier de graphe? une douzaine d’arêtes, par exemple, ça permettrait d'exécuter ton programme.

    Autre remarque, le nom du fichier devrait être donné en argument de get_graph().

    map.find() est plutot lent, en log(n), donc dans compute_min_edge, il faut le sauvegarder localement, plutot que de le calculer trois fois à chaque passage.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  3. #3
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2014
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2014
    Messages : 126
    Points : 48
    Points
    48
    Par défaut
    Bonjour et merci de ta réponse ! Oui tu as raison j'ai oublié de delete (où dois-je les mettre ? ), mon code est à mi-chemin car j'ai appris à coder en C (j'en ai fais un peu) et maintenant j'apprends à coder en C++, mais pour les exercices que je fais ici j'aimerai dans la mesure du possible éviter l'orienté objet si c'est ce à quoi tu pensais à moins que ce ne soit la seule solution. Euh oui malheureusement je suis sûr de mon coup, je programme cet algorithme pour un cours en ligne et c'est le graphe fourni par le cours en question. Et tu as aussi raison je devrai mettre le nom du fichier en argument, j'en suis conscient mais dans la mesure où le challenge et le fun réside pour moi dans l'implémentation de l'algorithme je ne fais pas forcément tout parfaitement en dehors désolé :p.

    J'essaie de te construire un graphe qui puisse te servir à run le programme.

  4. #4
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2014
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2014
    Messages : 126
    Points : 48
    Points
    48
    Par défaut
    Voilà, le fichier est sous la même forme que celui sur lequel je travaille, sur la premiere ligne tu as le nombre de sommets puis le nombre d'arêtes.
    Sur toutes les autres lignes tu as sommet1 puis sommet2 puis poids de l'arête sommet1-sommet2. Il est en pièce jointe
    Fichiers attachés Fichiers attachés
    • Type de fichier : txt ex.txt (84 octets, 101 affichages)

  5. #5
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Si tu apprends le C++, je t'invite à lire notre faq, elle t'apprendra plein de choses sur le pourquoi coder de telle ou telle manière.

    L'avantage du C++, c'est qu'il propose des classes ayant des attributs privés, et des fonctions.
    Ca permet d'augmenter la sécurité de ce qu'on fait.

    Par exemple, plutot qu'un tableau de 1000 bidules, on utilise un vector<bidule> de taille 1000. Un vector est de taille variable, et possède les memes performances qu'un tableau (c'en est un en interne).

    de meme, plutot que tes stoul, on utiliserai istringstream, et son opérateur >>, permettant justement de lire directement un entier.

    Je vais recoder en C++ classique ton code, pour que tu vois ce que ca donne.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  6. #6
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2014
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2014
    Messages : 126
    Points : 48
    Points
    48
    Par défaut
    Très bien merci du conseil je lirai la faq autant que possible. Je connais le concept d'orienté objet, j'ai fais du java . Je connais aussi les vector mais on m'a appris que lorsque je connais à l'avance
    la taille de mon tableau un array est plus facile à utiliser, si tu entendais par là qu'un vector me permettrait de m'adapter plus rapidement à des changements de taille de l'input je suis parfaitement d'accord avec toi et je suis désolé de me répéter mais pour l'instant j'essaie de faire fonctionner mes algorithmes correctement et en des temps raisonnables (ce qui n'est déjà pas une mince affaire) donc je n'ai pas beaucoup de temps à passer sur ce genre d'optimisation. En revanche je ne connais pas du tout les istreamstring dont tu parles, je te remercie beaucoup de tes efforts et attends impatiemment de voir ton code.

  7. #7
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 630
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 630
    Points : 10 556
    Points
    10 556
    Par défaut
    Ce que je remarque dans ta méthode compute_min_edge tu appelles 2 à 3 fois g.find({x, head_}) ou

  8. #8
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2014
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2014
    Messages : 126
    Points : 48
    Points
    48
    Par défaut
    Tu as parfaitement raison, avant ce cours d'algo je n'avais jamais eu à me soucier du temps que prends mon programme pour tourner et par conséquent à faire attention à ce genre de choses, c'est stupide je devrai stocker la valeur. Je modifie et regarde si ça va mieux.

  9. #9
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2014
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2014
    Messages : 126
    Points : 48
    Points
    48
    Par défaut
    Voici le code modifié, je ne sais pas si c'est ce que tu attendais mais en tout cas il ne tourne pas plus vite ou en tout cas pas assez sensiblement malheureusement pour que ça fasse une différence pour mon problème.


    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
    Edge compute_min_edge(unsigned int head_, Graph g){
    	Edge e({INT_MAX, head_, INT_MAX});
    	for(unsigned int x(0); x < 1000; ++x){
    		std::map<Key, int, KeyCompare>::iterator it = g.find({x, head_});
    		if(it != g.end()){
    			int l(it->second);
    			if(l < e.length){
    				e.length = l;
    				e.tail = x;
    			}
    		}
    	}
     
    	return e;
    }

  10. #10
    Membre éclairé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2010
    Messages
    517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Santé

    Informations forums :
    Inscription : Avril 2010
    Messages : 517
    Points : 718
    Points
    718
    Par défaut
    Salut,
    Si tu peux utiliser C++11 et en particulier <chrono>, tu pourrais mesurer facilement la fonction bloquante. Encore, mieux, fait un petit profiling de code (gprof sous Linux avec le flag -pg). Comme ça, tu pourras clairement identifier le nombre d'appel de tes différentes fonctions ainsi que le pourcentage de temps passé dans chacune d'elles.

  11. #11
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2014
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2014
    Messages : 126
    Points : 48
    Points
    48
    Par défaut
    Salut darkman, ta solution est intéressante néanmoins dans la mesure où comme je l'ai dis je n'execute que trois fonctions dans mon main, et dans la mesure où les 2 premieres prennent moins d'une seconde j'aurai tendance à penser que c'est la troisième fonction qui prend du temps. Par ailleurs cette fonction est composée de deux choses, une double-boucle assez grosse que je ne peux pas réduire malheureusement et une fonction, donc à moins que je ne dise des bêtises la seule fonction qui ralentisse mon programme et sur laquelle j'ai potentiellement des choses à faire c'est celle-ci nan ?

    Cette fonction en question "compute_min_edge" j'y ai réfléchis, elle consiste à regarder pour un sommet i toutes les arêtes qui vont vers i (le graphe est orienté) et à choisir celle dont la longueur est la plus faible. Et par conséquent pour regarder toutes les edges potentielles en question j'ai eu l'idée de les mettres dans une map car j'ai pensé que ce serait plus facile de les trouver que de regarder dans un tableau par exemple ouù je serai forcément de lire la totalité de l'input.

    En conclusion soit je me suis trompé dans mon raisonnement jusqu'ici, soit ma map n'est pas implémentée de manière optimale et cela ralentit mon programme soit il y a une structure plus performante que je ne connais pas.

    Etant donné cette analyse, as-tu une idée de solution à m'apporter ? Encore merci à vous deux.

    Par ailleurs j'ai vérifié que la complexité de l'algorithme est O(nm) ici mon graphe est relativement creux donc en gros O(n^2) ici j'ai n = 1000 donc 10^6 ça me parait raisonnable.

    Au fait j'ai regardé et le temps d'accès à un élément grâce est à une map est constant donc je suis vraiment blocké !

  12. #12
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2014
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2014
    Messages : 126
    Points : 48
    Points
    48
    Par défaut
    Je viens de m'apercevoir que j'ai fait une erreur dans mon programme...Je reposte quand je l'ai réglé

  13. #13
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2014
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2014
    Messages : 126
    Points : 48
    Points
    48
    Par défaut
    Voilà je remets le code qui fait ce que je veux ce coup-ci, il tourne sensiblement plus vite puisque j'ai passé certains paramètres par référence, mais il est toujours trop lent.

    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
    101
    102
    103
    #include <iostream>
    #include <array>
    #include <map>
    #include <fstream>
    #include <climits>
    #include <set>
     
    struct Edge
    {
    	unsigned int tail;
    	unsigned int head;
    	int length;
    } ;
     
    struct Key
    {
    	long unsigned int q;
    	long unsigned int t;
    };
     
    struct KeyCompare {
    	bool operator()(const Key& a, const Key& b){
    		return (a.q < b.q) || (a.q == b.q && a.t < b.t);
    	}
    };
     
    typedef std::map<Key, int, KeyCompare> Graph;
     
    Graph get_graph(std::string fichier_);
    int** initialize(unsigned int source, unsigned int dimX, unsigned int dimY);
    void compute(int** & tab);
    int compute_min_path(unsigned int source, unsigned int i, Graph& g, int** & tab);
     
    int compute_min_path(unsigned int v, unsigned int i, Graph& g, int** & tab){
    	int path(INT_MAX);
    	for(unsigned int x(0); x < 1000; ++x){
    		std::map<Key, int, KeyCompare>::iterator it = g.find({x, v});
    		if(it != g.end()){
    			int l(it->second);  
    			if(tab[i-1][x] + l < path){
    				path = tab[i-1][x] + l;
    			}
    		}
    	}
     
    	return path;
    }
     
     
    Graph get_graph(std::string fichier_){
    	Graph graph;
    	std::string ligne("");
    	std::ifstream fichier(fichier_.c_str(), std::ios::in);
     
    	if(fichier){
    		getline(fichier, ligne);
    		while(getline(fichier, ligne)){
    			Key k = {stoul(ligne.substr(0, ligne.find(' ', 0)))-1, stoul(ligne.substr(ligne.find(' ', 0), ligne.find(' ', ligne.find(' ', 0) + 1)))-1};
    			graph[k] = stoi(ligne.substr(ligne.find(' ', ligne.find(' ', 0) + 1)));
    		}
    	}
     
    	else 
    		std::cerr << "Impossible d'ouvrir le fichier" << std::endl;
     
    	return graph;
    }
     
    int** initialize(unsigned int source, unsigned int dimX, unsigned int dimY){
    	int** tableau;
     
    	tableau = new int*[dimX];
    	for(size_t x(0); x < dimX; ++x){
    		tableau[x] = new int[dimY];
    		for(size_t y(0); y < dimY; ++y){
    			tableau[x][y] = INT_MAX;
    			if(y == 0 and x == source)
    				tableau[x][y] = 0;
    		}
    	}
     
    	return tableau;
    }
     
    void compute(int** & tab, Graph& g){
    	for(size_t i(1); i < 47978; ++i){
    		for(size_t v(0); v < 1000; ++v){
    			tab[i][v] = std::min(tab[i-1][v], compute_min_path(v, i, g, tab));
    		}
    	}
    }
     
     
    int main(int argc, char* argv[]){
     
    	Graph graph(get_graph("g1.txt"));
     
    	int** tab(initialize(0, 1000, 47978));
     
    	compute(tab, graph);
     
    	return 0;
    }

  14. #14
    Membre éclairé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2010
    Messages
    517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Santé

    Informations forums :
    Inscription : Avril 2010
    Messages : 517
    Points : 718
    Points
    718
    Par défaut
    Au lieu d'utiliser un tableau à 2 dimensions (int ** tab), utilises un tableau 1 dimension de plus grande taille (int* tab = new int[1000*47978] Tu feras les accès à l'élément (i,j) de la façon suivante: tab[i+j*47978] (ou tu peux faire une macro qui te fais ça automatiquement).

    Après, je ne sais pas si tu n'as pas un petit bug au niveau de compute_min_path: if(tab[i-1][x] + l < path). Aurais-tu inversé les indices?

    Ensuite, micro optimisation: sort la déclaration de tes variables en dehors de tes boucles, il n'y aura qu'une seule allocation mémoire comme ça. Mais bon peut-être que le compilo peut faire ces optimisations pour toi, qu'en pensez-vous les experts?

    Tu peux également optimiser ta fonction d'initialisation en remplissant tout ton tableau avec INT_MAX en utilisant std::fill_n(tab, 1000*47978, INT_MAX); par exemple, puis initialiser tab[0] à 0: ça t'évite de faire tout un tas de tests inutiles pour modifier seulement une valeur.

    Mais sinon, je te conseille quand même vraiment de faire du profiling: tu trouveras plus facilement des solutions.

  15. #15
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Déjà, il y a une amélioration substantielle en quantité de mémoire:
    tu n'a pas besoin de te souvenir de l'historique des calculs, donc d'allouer un tableau de tableaux.

    Il te suffirait de deux tableaux. que tu intervertirais à chaque itération.
    Ces tableaux correspondrait aux temps pairs et impairs.

    Par ailleurs, tes données ne sont pas structurées convenablement vis-a-vis du problème.
    Il te faut écrire du code un peu complexe pour obtenir le poids d'une arête particulière.
    ton graph devrait être une classe proposant deux fonctions:
    • un test d'existence d'une arête
    • le poids de la dite arête


    Par ailleurs, l'affichage est une opération lente, j'enleverai le cout de la double boucle. au minimum pour le mettre dans la boucle extérieure.

    Ce ne sont que des guides.
    Comme je suis au travail, j'avance doucement sur la réécriture.
    Surtout que je n'ai pas de compilateur sous la main...
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  16. #16
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Salut !

    Tu bosses sur quelle plateforme ? Ma réponse ne fonctionne que sur linux ou mac. L'équivalent sur Windows existe, c'est juste que je ne connais pas.

    J'ai pas le temps de le faire sur ton code mais pour améliorer les perfs, une chose simple à essayer est de profiler une exécution avec valgrind. A faire sur un exécutable compilé en debug.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    valgrind --tool=callgrind ./ton_executable
    kcachegrind callgrind.xxxxx.out
    Ensuite kcachegrind te permet d'afficher différentes informations sur les quantités d'appels et la quantité de temps que ces appels ont consommé. Il y a une option "shorten templates" qui rend les appels aux templates plus lisible. Cela te permettra d'identifier rapidement qui bouffe le plus de temps dans ton code. En général, il s'agit d'une opération sur une collection, et le choix d'une autre collection ou d'un autre algo permet d'améliorer les choses.
    Find me on github

  17. #17
    Membre éclairé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2010
    Messages
    517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Santé

    Informations forums :
    Inscription : Avril 2010
    Messages : 517
    Points : 718
    Points
    718
    Par défaut
    Citation Envoyé par leternel Voir le message
    Déjà, il y a une amélioration substantielle en quantité de mémoire:
    tu n'a pas besoin de te souvenir de l'historique des calculs, donc d'allouer un tableau de tableaux.
    Pour paralléliser après (avec OpenMP par exemple), ça serait plus simple de le garder, tu ne penses pas?

  18. #18
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Non, parce que justement, les valeurs à calculer dans chaque tableau dépend de la valeur calculée dans le précédent.

    Et le problème est que le calcul effectif n'est pas en o(N*E), mais o(N*E*N), à cause de compute_min_edge.

    Voici le code réécrit "à la C++". Je l'ai templaté, parce que j'avais envie.
    Si cela te pose problème, ImmoTPA, je peux le réécrire sans les templates.


    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
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    #include <set>
    #include <map>
    #include <iostream>
    #include <sstream>
    #include <fstream>
    #include <limits>
    #include <utility>
     
    /* class et struct sont quasiment équivalent, la seule grosse différence, c'est la privauté par défaut*/
     
    //un type utilisé comme exception, par get_graph(...)
    struct file_not_found{};
    struct parse_error{};
     
    namespace graph {
    template <typename Index, typename Value>
    class graph {
    	//les typedef améliore l'écriture du code utilisateur
    	public:
    		typedef Index node_id_type;   
    		typedef Value weight_type;
     
    		class edge {
    		private:
    			node_id_type from, to;
    			weight_type w;
     
    		public:
    			edge(node_id_type source, node_id_type target) : from(source), to(target), w(0) {}
    			edge(node_id_type source, node_id_type target, weight_type weight) : from(source), to(target), w(weight) {}
     
    			weight_type& weight() {return w;}
    			weight_type weight() const {return w;}
     
    			node_id_type source() const {return from;}
    			node_id_type target() const {return to;}
    			friend struct graph<Index, Value>::edge_comparator;
    		};
     
    	private:
    		struct edge_comparator {
    			bool operator()(graph::edge const & a, graph::edge const & b) const {
    				return (a.from < b.from) or (a.from == b.from and a.to < b.to);
    			}
    		};
    		typedef std::set<edge, edge_comparator> edges_type;
    		edges_type my_edges;
     
    		typedef std::set<node_id_type> nodes_type;
    		nodes_type my_nodes;
     
    	public:
    		typedef typename edges_type::size_type edge_count_type;
    		typedef typename nodes_type::size_type node_count_type;
     
    		typedef typename edges_type::const_iterator edge_const_iterator;
    		typedef typename edges_type::iterator edge_iterator;
     
    		typedef typename nodes_type::const_iterator node_const_iterator;
    		typedef typename nodes_type::iterator node_iterator;
     
    	public:
     
    		node_count_type node_count() const {return my_nodes.size();}
    		edge_count_type edge_count() const {return my_edges.size();}
     
    		edge_const_iterator edges() const {return my_edges.begin();}
    		edge_const_iterator notAnEdge() const {return my_edges.end();}
     
    		edge_iterator edges() {return my_edges.begin();}
    		edge_iterator notAnEdge() {return my_edges.end();}
     
     
    		node_const_iterator nodes() const {return my_nodes.begin();}
    		node_const_iterator notANode() const {return my_nodes.end();}
     
    		node_iterator nodes() {return my_nodes.begin();}
    		node_iterator notANode() {return my_nodes.end();}
     
    		//je choisis le return *this pour te montrer la syntaxe permettant le chainage.
    		//on pourra écrire g.createEdge(1,2).remove(1,3);
    		//même si en pratique, pour ces fonctions, ce n'est pas utile.
     
    		graph& createEdge(node_id_type from, node_id_type to, weight_type w) {
    			my_nodes.insert(from);
    			my_nodes.insert(to);
    			my_edges.insert(edge(from, to, w));
    			return *this;
    		}
    };//fin de la classe graph
     
    template <typename NodeId, typename Weight>
    graph<NodeId, Weight> parse(const char* filename) {
    	graph<NodeId, Weight> graph;
     
    	std::string ligne;
    	std::ifstream fichier(filename);
    	if (!fichier) throw file_not_found();
     
    	//le fichier contient une ligne de dimensionnement: nombre de sommets et d'aretes
    	if (!std::getline(fichier, ligne)) throw parse_error();
    	/*
    	en théorie, on aurait les trois lignes suivantes.
    	sauf qu'on va ignorer le nombre de noeud et d'arete dans cet exemple, car on ne vérifie pas le fichier.
     
    	int node_count, edge_count;
    	std::istringstream iss(ligne);
    	//iss >> node_count >> edge_count;
    	*/
     
    	//par contre, il faut du coup déclarer iss.
    	std::istringstream iss;
     
    	//puis des lignes d'arêtes: source, cible, poids
    	while (std::getline(fichier, ligne)) {
    		iss.str(ligne);
     
    		NodeId source, target;
    		Weight weight;
     
     		iss >> source >> target >> weight;
    		graph.createEdge(source, target, weight);
    	}
     
    	return graph;
    }
     
     
    }//fin du namespace graph
     
     
     
    namespace algo {
     
    //une classe d'exception pour le cas où le graphe contiendrait un cycle de poids négatif.
    template <typename NodeId>
    class NegativeCycleDetected {
    private:
    	NodeId from, to;
     
    public:
    	NegativeCycleDetected(NodeId source, NodeId target) :from(source), to(target) {}
    	NodeId source() const {return from;}
    	NodeId target() const {return to;}
    };
     
    template <typename NodeId>
    std::ostream& operator<<(std::ostream& os, NegativeCycleDetected<NodeId> const& ncd) {
    	return os << "cycle de poids négatif détecté autour de l'arete ("<<ncd.source() <<','<<ncd.target()<<')';
    }
     
    /*
    Bellman-Ford établi pour chaque noeud quel est son prédecesseur depuis la source, et le prix total.
    Il faut avoir une structure simple pour accéder à ces informations.
    */
    template <typename NodeId, typename Weight>
    class BFdata{
    public:
    	typedef NodeId node_id_type;
    	typedef Weight weight_type;
    private:
    	node_id_type predecessor;
    	weight_type weight_sum;
    public:
    	BFdata(node_id_type source, weight_type cost) : predecessor(source), weight_sum(cost) {}
     
    	//grace à ce constructeur par défaut non explicite, l'initialisation de la map sera triviale.
    	BFdata() :
    		predecessor(-1),
    		weight_sum(std::numeric_limits<weight_type>::max())
    		{}
     
    	/* fonction à négliger car inutilisée, je la laisse pour les explications */
    	//opérateur de convénience, pour simplifier un peu l'écriture de BellmanFord(...)
    	BFdata& operator=(weight_type path_cost) {
    		weight_sum = path_cost;
    		return *this;
    	}
     
    	//à cause du précédent, je dois redéfinir l'opérateur de copie complet.
    	BFdata& operator=(BFdata const& other) {
    		predecessor = other.predecessor;
    		weight_sum = other.weight_sum;
    		return *this;
    	}
     
    	node_id_type source() const {return predecessor;}
    	node_id_type& source() {return predecessor;}
     
    	weight_type path_cost() const {return weight_sum;}
    	weight_type& path_cost() {return weight_sum;}
    };
     
    //using est un outil puissant, parce qu'il permet de créer un alias sur un type, même si c'est une template.
    //ici, je déclare une template de type
    template <typename NodeId, typename Weight>
    using weight_matrix = std::map<NodeId, BFdata<NodeId, Weight>>;
     
    /*
    Je me suis référé à http://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm
    Parce que je ne comprenais pas pourquoi tu utilise compute_min_edge, alors que ce n'est pas dans l'algo    // dans la derniere version de mon code je l'ai corrigé
    */
     
    template <typename NodeId, typename Weight>
    weight_matrix<NodeId, Weight>
    BellmanFord(graph::graph<NodeId, Weight> const& g, NodeId source) {
    	weight_matrix<NodeId, Weight> paths;
     
    	paths.insert(std::make_pair(source, BFdata<NodeId, Weight>(source, 0)));
     
    	//je fais la boucle à l'envers pour ne pas calculer tout le temps size().
    	//ici, auto est pratique, car les noms complets sont effrayants:
    	//graph::graph<NodeId, Weight>::node_count_type
    	//graph::graph<NodeId, Weight>::edge_const_iterator
    	for (auto step = g.node_count(); step>0; --step) {
    		for (auto e = g.edges(); e!= g.notAnEdge(); ++e) {
    			Weight weight = paths[e->source()].path_cost() + e->weight();
     			if (weight < paths[e->target()].path_cost()) {						
    				paths[e->target()] = BFdata<NodeId, Weight>(e->source(), weight);
    			}
    		}
    	}
     
    	for (auto e = g.edges(); e!= g.notAnEdge(); ++e) {
    		Weight weight = paths[e->source()].path_cost() + e->weight();
     
    		if (weight < paths[e->target()].path_cost()) {                
    			throw NegativeCycleDetected<NodeId>(e->source(), e->target());
    		}
    	}
    	return paths;
    }
    }//fin du namespace algo
     
     
    typedef unsigned int node_id_type;
    typedef int weight_type;
     
    using Graph = graph::graph<node_id_type, weight_type>;
    using Paths = algo::weight_matrix<node_id_type, weight_type>;
     
    using namespace std;
     
    int main() {
    	try {
    		Graph g = graph::parse<node_id_type, weight_type>("g1.txt");
    		Paths paths = algo::BellmanFord(g, 0u);
     
    		cout << "chemins depuis chaque noeuds: (noeud précédent, prix cumulé)" << endl;
    		for (auto node = g.nodes(); node != g.notANode(); ++node) {
    			cout << paths[*node].source() << "\t" << paths[*node].path_cost() << endl;
    		}
    		return 0;
    	} catch (algo::NegativeCycleDetected<node_id_type> const& e) {
    		cerr << e << endl;
    		return 1;
    	} catch (parse_error const& e) {
    		cerr << "graph file is invalid." << endl;
    		return 2;
    	} catch (file_not_found const& e) {
    		cerr << "could not open file." << endl;
    		return 3;
    	}
    }
    Normalement, ca devrait compiler, mais je n'en suis pas certain, car je n'ai pas de compilateur sous la main.
    avec g++, tu compileras avec g++ -std=c++11 -Wall -Wextra -O3 bf_by_leternel.cpp -o bf_by_leternel.

    N'hésite pas à poser des questions sur ce code, je reste à ton écoute.

    EDIT: J'ai modifié le code pour le rendre compilable. C'est fou ce que je fais comme petites erreurs quand je n'ai pas un compilo pour raller.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  19. #19
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2014
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2014
    Messages : 126
    Points : 48
    Points
    48
    Par défaut
    Bonjour tout le monde et merci pour vos réponses, je vais essayer de mettre en œuvre ce que vous m'avez conseillé tous. Je travaille sous linux pour la personne qui m'a demandé . Merci beaucoup pour ton code leternel mais il y a tellement de choses que je connais pas que ça va me prendre du temps de décrypter ! Néanmoins si tu pouvais m'expliquer à quoi sert un template en général ? Je pensais que c'était pour faire de la programmation générique comme en java (donc en gros on écrit des méthodes avec un type qu'on ne précise pas et puis ensuite le comportement de la méthode s'adapte en fonction du type utilisé). Oui je m'étais trompé avec le compute_min_edge, en fait il faut calculer le min de chemin de s -> w (un sommet intermédiaire) + wt (arête qui va de w jusquà la target). Par ailleurs à un moment tu dis que je dois avoir une structure efficace qui me permet de savoir quels sont les chemins empruntés, désolé j'aurai du te le dire mais dans mon exercice ce n'est pas demandé, je dois programmer l'algorithme de Johnson pour calculer les plus courts chemins de tous les sommets d'un graphe, sans pour autant savoir par où on passe. Je dois donc programmer Bellman-Ford puis redéfinir les poids des arêtes et enfin faire tourner dijkstra n fois.

    Tu as plusieurs lignes qui ressemblent à ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef typename edges_type::size_type edge_count_type;
    ça veut dire quoi ?

    ligne ~ 40
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef std::map<node_id_type> nodes_type;

    Là je t'avouerai n'avoir rien compris, ta map ne prend que un paramètre ? Par ailleurs désolé mais ça ne compile pas et il y a tellement d'erreurs ça ne m'étonnerait pas qu'il y ait des problèmes de compatibilité avec le compilateur ou autre.

    J'essaie de décrypter ton code au fur et à mesure merci de ton aide en tout cas et merci les autres aussi

    Par ailleurs tes deux premiers struct m'interpellent :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    struct file_not_found{};
    struct parse_error{};
    Tu me dis que les struct sont quasiment des classes donc là tu crées des classes d'exceptions dans lesquelles tu ne mets rien et ensuite tu lèves les exceptions comme suit :
    mais concrètement il fait quoi quand il arrive là parce que tu n'as rien dans ton struct Oo ?

  20. #20
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2014
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2014
    Messages : 126
    Points : 48
    Points
    48
    Par défaut
    Re coucou, Voici ton code leternel avec mes comms sur les subtilités de ton code que je ne comprends pas, en revanche j'ai compris le fonctionnement général du code en partie grâce à la FAQ et à tes comms donc merci


    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
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    #include <set>
    #include <map>
    #include <iostream>
    #include <fstream>
    #include <limits>
     
    // Je te mets mes coms toutes les questions ne sont pas primordiales tt suite si tu n a pas trop le temps ne répond que aux trucs vitaux :p
    // merci du coup de main !
     
     
    /* class et struct sont quasiment équivalent, la seule grosse différence, c'est la privauté par défaut*/
     
    //un type utilisé comme exception, par get_graph(...)
    struct file_not_found{};      // je sais ce qu'est une exception mais en Java quand j'en utilisais la classe n'était pas vide, je ne comprends pas ce qui se passe                     
    struct parse_error{};			// quand tu soulèves une de ces deux exceptions
     
    namespace graph {
    template <typename Index, typename Value> // Si je comprends bien, tu ne définis pas à l'avance le type de mes sommets et de mes poids de sorte que je pourrai 
    class graph { 								// utiliser des float comme des ints ou autre ?
    	//les typedef améliore l'écriture du code utilisateur
    	public:
    		typedef Index node_id_type;   
    		typedef Value weight_type;
     
    		class edge {
    		private:
    			node_id_type from, to;
    			weight_type w;
     
    		public:
    			edge(node_id_type source, node_id_type target) : from(source), to(target), w(0) {}
    			edge(node_id_type source, node_id_type target, weight_type w) : from(source), to(target), w(weight) {}
     
    			weight_type weight() const {return w;}
    			weight_type& weight() {return w;}       // Je ne vois pas pourquoi il y a deux méthodes ici, tu fais un get_poids avc une référence constante et une pas 
    													// constante si j'ai bien compris, mais ça change quoi ici
     
    			node_id_type source() const {return from;}
    			node_id_type target() const {return to;}
    		};
     
    	private:
    		struct edge_comparator {                           // j'ai déjà utilisé ce objet_comparator mais je ne sais pas pk tu ne peux pas le déclarer comme une méthode 
    															// normale et que tu dois mettre un struct
    			bool operator()(graph::edge const & a, graph::edge const & b) const {
    				return (a.from < b.from) or (a.from == b.from and a.to < b.to);
    			}
    		};
    		typedef std::set<edge, edge_comparator> edges_type;
    		edges_type my_edges;
     
    		typedef std::map<node_id_type> nodes_type; // là c'est carrément nébuleux, d'habitude je déclare une map comme ça std::map<Key, Value> éventuellement avec 
    													// une fonction de comparaison pour les clés, std::map<Key, Value, KeyCompare>
    		nodes_type my_nodes;
     
    	public:
    		typedef typename edges_type::size_type edge_count_type;             // les 6 typedef typename ici ne veulent malheureusement pas dire grand chose pour moi à l'heure
    		typedef typename nodes_type::size_type node_count_type;				// où je t'écris ça
     
    		typedef typename edges_types::const_iterator edge_const_iterator;
    		typedef typename edges_types::iterator edge_iterator;
     
    		typedef typename nodes_type::const_iterator node_const_iterator;
    		typedef typename nodes_type::iterator node_iterator;
     
    	public:
     
    		node_count_type node_count() const {return my_nodes.size();}
    		edge_count_type edge_count() const {return my_edges.size();}
     
    		edge_const_iterator edges() const {return my_edges.begin();}         // si j'ai bien compris compris ça renvoie un itérateur sur le set de edges
    		edge_const_iterator notAnEdge() const {return my_edges.end();}       // Pourquoi deux fois les mêmes méthodes, quel est l'intérêt d'un iterator constant et d'un 
    																			// pas constant
     
    		edge_iterator edges() {return my_edges.begin();}
    		edge_iterator notAnEdge() {return my_edges.end();}
     
     
    		node_const_iterator nodes() const {return my_nodes.begin();}
    		node_const_iterator notANode() const {return my_nodes.end();}
     
    		node_iterator nodes() {return my_nodes.begin();}
    		node_iterator notANode() {return my_nodes.end();}
     
    		//je choisis le return *this pour te montrer la syntaxe permettant le chainage.
    		//on pourra écrire g.createEdge(1,2).remove(1,3);
    		//même si en pratique, pour ces fonctions, ce n'est pas utile.                          //merci c'est super classe j'avais déjà aperçu une fois ou deux
     
    		graph& createEdge()(node_id_type from, node_id_type to, weight_type w) {
    			my_nodes.insert(from);
    			my_nodes.insert(to);
    			my_edges.insert(edge(from, to, w));
    			return *this;
    		}
     
    		//L'opérateur suivant permet aussi le remplissage, mais c'est plus "fun" mais moins élégant.
    		//Je ne le mets que pour présenter un problème à connaitre
    		//Plus de détails dans parse
     
    		//accesseur par référence non constante
    		weight_type& operator()(node_id_type from, node_id_type to) {
    			my_nodes.insert(from);
    			my_nodes.insert(to);
    			return my_edges[edge(from, to)];
    		}
     
    };//fin de la classe graph
     
    template <typename NodeId, typename Weight>
    graph<NodeId, Weight> parse(const char* filename) {         // une raison pour laquelle tu n'utilises pas une string à la place de char* ? 
    	graph<NodeId, Weight> graph;
     
    	std::string ligne;
    	std::ifstream fichier(filename, std::ios::in);
    	if (!fichier) throw file_not_found();
     
    	//le fichier contient une ligne de dimensionnement: nombre de sommets et d'aretes
    	if (!getline(fichier, ligne)) throw parse_error;
    	/*
    	en théorie, on aurait les trois lignes suivantes.
    	sauf qu'on va ignorer le nombre de noeud et d'arete dans cet exemple, car on ne vérifie pas le fichier.
     
    	int node_count, edge_count;
    	std::istringstream iss(ligne);              //si je comprends bien ça met ligne dans iss qui est une sorte de liste où chaque élément est séparé par un espace dans ligne
    	iss >> node_count >> edge_count;           // et ensuite ça mets les éléments de iss dans node_count et edge_count l'un après l'autre
    	*/
     
     
    	//puis des lignes d'arêtes: source, cible, poids
    	while (getline(fichier, ligne)) {
    		iss.str(ligne);
     
    		NodeId source, target;
    		iss >> source >> target;
    		//pas besoin d'une variable temporaire pour le poids, vu qu'on a l'opérateur () retournant une référence modifiable.
    		//mais il faut utiliser une seconde expression, à cause de l'ordre d'évaluation des sous-expressions qui n'est pas défini.
    		iss	>> graph(source, target);   // ici on dirait que tu fais appel au constructeur de graph mais je ne le trouve pas ? 
     
    		/*
    		En dehors de cet exemple, j'aurai utilisé une temporaire, avec un code tel que:
     
    		NodeId source, target;
    		Weight weight;
     
    		iss >> source >> target >> weight;
    		graph.createEdge(source, target, weight);
    		*/
    	}
     
    	return graph;
    }
     
     
    }//fin du namespace graph
     
     
     
    namespace algo {
     
    //une classe d'exception pour le cas où le graphe contiendrait un cycle de poids négatif.
    template <typename NodeId>
    class NegativeCycleDetected {
    private:
    	NodeId from, to;
     
    public:
    	NegativeCycleDetected(NodeId source, NodeId target) :from(source), to(target) {}
    	NodeId source() const {return from;}
    	NodeId target() const {return to;}
    };
     
    template <typename NodeId>
    std::ostream& operator<<(std::ostream& os, NegativeCycleDetected<NodeId> const& ncd) {
    	return os << "cycle de poids négatif détecté autour de l'arete ("<<ncd.source() <<','<<ncd.target<<')';
    }
     
    /*
    Bellman-Ford établi pour chaque noeud quel est son prédecesseur depuis la source, et le prix total.
    Il faut avoir une structure simple pour accéder à ces informations.
    */
    template <typename NodeId, typename Weight>
    class BFdata{
    public:
    	typedef NodeId node_id_type;
    	typedef Weight weight_type;
    private:
    	node_id_type predecessor;
    	weight_type weight_sum;
    public:
    	BFdata(node_id_type source, weight_type cost) : predecessor(source), weight_sum(cost) {}
     
    	//grace à ce constructeur par défaut non explicite, l'initialisation de la map sera triviale.
    	BFdata() :
    		predecessor(-1),
    		weight_sum(numeric_limits<weight_type>::max())           // tu initilises en mettant que personne n'a de prédecesseurs au début et que le poids vaut le max
    		{}														// du type que tu utilises pour le poids des arêtes donc ça pourrait être INT_MAX ou DOUBLE_MAX, balèze
    																// par contre c'est quoi {}
     
    	//opérateur de convénience, pour simplifier un peu l'écriture de BellmanFord(...)
    	BFdata& operator=(weight_type path_cost) {              //désolé mais je ne vois pas en quoi ça simplifie, en fait je ne vois pas à quoi sert la méthode, enfin tu
    															// redéfinies l'opérateur = mais je ne comprends pas ce que le code signifie en terme d'égalité pour deux objets
    															//BFdata
    		weight_sum = path_cost;
    		return *this;
    	}
     
    	//à cause du précédent, je dois redéfinir l'opérateur de copie complet.
    	BFdata& operator=(BFdata const& other) {
    		predecessor = other.predecessor;
    		weight_sum = other.weight_sum;
    		return *this;
    	}
     
    	node_id_type source() const {return node_id_type;}
    	node_id_type& source() {return node_id_type;}
     
    	weight_type path_cost() const {return weight_sum;}
    	weight_type& path_cost() {return weight_sum;}
    };
     
    //using est un outil puissant, parce qu'il permet de créer un alias sur un type, même si c'est une template.
    //ici, je déclare une template de type
    template <typename NodeId, typename Weight>
    using weight_matrix = std::map<NodeId, BFdata<NodeId, Weight>>;  // en gros comme tu peux pas faire typedef sur un template tu fais using ? 
     
    /*
    Je me suis référé à http://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm
    Parce que je ne comprenais pas pourquoi tu utilise compute_min_edge, alors que ce n'est pas dans l'algo    // dans la derniere version de mon code je l'ai corrigé
    */
     
    template <typename NodeId, typename Weight>
    weight_matrix<NodeId, Weight>                                        //extrêmement bizarre, à quoi sert cette ligne ? 
    BellmanFord(graph::graph<NodeId, Weight> const& g, NodeId source) {
    	weight_matrix<NodeId, Weight> paths;                             //à la ligne 225 weight_marix c'est std::map<NodeId, BFdata<NodeId, Weight>> et maintenant
    																	// je comprends plus ce que c'est
     
    	paths.emplace(source, 0);
     
    	//je fais la boucle à l'envers pour ne pas calculer tout le temps size().     // je ne vois pas pourquoi il te faudrait calculer size() ? 
    	//ici, auto est pratique, car les noms complets sont effrayants:
    	//graph::graph<NodeId, Weight>::node_count_type
    	//graph::graph<NodeId, Weight>::edge_const_iterator
    	for (auto step = g.node_count(); step>0; --step) {          // tant qu'il y a des noeuds ? 
    		for (auto e = g.edges(); e!= g.notAnEdge(); ++e) {            // tant qu'il y a des edges ? 
    			Weight weight = paths[e->source()].path_cost() + e->weight();     // on crée un poids qui vaut la longueur du chemin pour aller de la source jusqu'à l'arête + le poids de l'arête 
     
    			if (weight < paths[e->target()].path_cost()) {						
    				paths[e->target()]=BFData<NodeId, Weight>(e.source(), weight);       // cette ligne elle met à jour le plus cours chemin pour la target mais pourquoi 
    																					// tu dois écrire BFData<NodeId, Weight>(e.source(), weight) 
    			}
    		}
    	}
     
    	for (auto e = g.edges(); e!= g.notAnEdge(); ++e) {
    		Weight weight = paths[e->source()].path_cost() + e->weight();
     
    		if (weight < paths[e->target()].path_cost()) {                
    			throw NegativeCycleDetected(e.source, e.target);
    		}
    	}
    	return paths;
    }
    }//fin du namespace algo
     
     
    typedef unsigned int node_id_type; // et donc là on finit par définir un type que le template va utiliser c'est ça ? 
    typedef int weight_type;
     
    using Graph = graph::graph<node_id_type, weight_type>;
    using Paths = algo::weight_matrix<node_id_type, weight_type>;
     
    using namespace std;
     
    int main(int argc, char* argv[]){
    	try {
    		Graph = graph::parse<node_id_type, weight_type>("g1.txt");
    		Paths paths = algo::compute(graph, 0);                           // je ne trouve pas compute dans le namespace algo
     
    		cout << "chemins depuis chaque noeuds: (noeud précédent, prix cumulé)" << endl;
    		for (auto node = g.nodes(); node != g.notANode(); ++node) {
    			cout << paths[*node].source() << "\t" << paths[*node].path_cost() << endl;
    		}
    		return 0;
    	} catch (NegativeCycleDetected<node_id_type> const& e) {
    		cerr << e << endl;
    		return 1;
    	} catch (parse_error const& e) {
    		cerr << "graph file is invalid." << endl;
    		return 2;
    	} catch (file_not_found const& e) {
    		cerr << "could not open file." << endl;
    		return 3;
    	}
    }

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Comparaison d'algorithmes : A* et Bellman-Ford
    Par geforce dans le forum Algorithmes et structures de données
    Réponses: 10
    Dernier message: 23/05/2015, 22h35
  2. Trouver l'arborescence des plus courts chemins (algorithme de Bellman-Ford)
    Par geforce dans le forum Algorithmes et structures de données
    Réponses: 12
    Dernier message: 11/03/2015, 05h39
  3. Trouver les chemins les plus courts avec l'algorithme de Bellman-Ford
    Par geforce dans le forum Algorithmes et structures de données
    Réponses: 3
    Dernier message: 06/02/2015, 16h28

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