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

Langage Perl Discussion :

Bug dans mon algo..


Sujet :

Langage Perl

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Août 2005
    Messages
    346
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2005
    Messages : 346
    Points : 119
    Points
    119
    Par défaut Bug dans mon algo..
    Bonjour,

    J'essaie de terminer un algo, mais j'ai un bug que je ne comprend vraiment pas.. si qqun pouvait regarder mon code et me dire où serait l'erreur, ce serait fort sympathique de sa part!

    Pour le détail:
    J'ai un fichier avec une liste de points représentant le centroide(x;y) des emprises de cartes. Chaque carte pèse une certaine taille. Mon but est de répartir en zones géographiques homogènes les emprises sur des CD de 700Mo.

    Pour cela, un premier algo fait un travail plutot pas mal. Mais dans certains cas de figure, il paraitrait plus judicieux de deplacer une emprise d'un CD vers un autre, dont le barrycentre est plus proche que celui du CD sur lequel a été affectée l'emprise.

    Voici le graphique généré, qui donne une idée précise du résultat du premier algo:


    On voit que des reaffectations pourraient etre faites sur les CD jaune et violet vers le N°10.

    J'ai donc fait un deuxieme algo qui fait ceci:
    - 1 - Initialisation:
    1.1 Calcule du barrycentre de chaque groupe actuel (moyenne des x/y).
    1.2 Tri de la liste des emprises par groupe, de l'emprise la plus eloignée du barrycentre à l'emprise la plus proche.

    - 2 - Boucle générale sur chaque groupe
    2.2 Boucle sur chaque emprise
    2.2.1 Calcule de la distance de l'emprise au barrycentre du groupe.
    2.2.2 Boucle sur le barrycentre des autres groupe. Si la distance du dataset est plus courte, on selectionne le groupe dans une liste de candidats.
    2.2.3 Une fois la liste créée, je la trie par ordre croissant (le groupe candidat le plus proche en premier).
    2.2.4 Je parcours chaque candidat, le premier qui dispose de suffisamment de place libre se voit affecter le dataset qu'on supprime de son groupe originaire et on reinitilise l'algo (Etape 1).

    Terminé quand plus aucun déplacement.

    Le resultat est pas mal, mais j'ai 2 bugs (liés):
    - J'obtiens certains CD dépassant la taille-contrainte des 700 Mo.
    - J'ai des doublons (que je supprime actuellement, mais.... c'est pas propre!)

    Le probleme semble venir de l'étape où l'algo se réinitialise car si je ne le réinitialise pas apres chaque déplacement d'emprise vers un autre CD, le resultat est meilleur (mais tjs un leger prob de taille, sans les doublons).

    voici le code du second algorithme (qui bug):
    Le fichier compilations.tmp qu'il recupere est celui du premier algo de répartition. Il se presente sous la forme:
    INDEX=1
    Identifiant_Emprise;Centroide_X Centroide_Y;Taille_Ko
    Identifiant_Emprise;Centroide_X Centroide_Y;Taille_Ko

    INDEX=2
    Identifiant_Emprise;Centroide_X Centroide_Y;Taille_Ko
    etc...

    Note: j'appelle "dataset" une emprise.

    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
     
    sub optimiser_compilations {
    # Cette routine constitue un algo de "seconde passe" de la répartition des emprises (la premiere passe etant &creer_compilations).
    # Son but est d'améliorer la répartition par rapport au barrycentre de chaque groupe.
     
    # Il faut recuperer compilations.tmp en placant les groupes créés dans 
    #	%groupe{index}[index_dataset]{ligne_listing} = "index/str/ds;x y;taille"
     
    # Calculer: barrycentres de chaque groupe dans
    # %barrycentre{index_groupe} = 'x y'
     
    my %groupe;
    my $t = open(COMPILATIONS,"$config{dir_tmp}compilations.tmp");
    my @compilations;
    	if ($t) {
    	@compilations=<COMPILATIONS>;
    	} # Fin if
    	else {
    	&afficher_erreur("\a\aERREUR dans le script: Impossible d'optimiser la repartition. Compilations.tmp illisible!\n");
    	} # Fin else
    close COMPILATIONS;
     
    # Récupération des groupes
    my $nom_groupe='';
    my $nb_elem=0;
    	foreach my $ligne (@compilations){ # Parcourt le listing généré par le premier algorithme
    	chomp $ligne;
    		if ($ligne =~ m/^INDEX=(.*)/) { # Nouveau groupe
    		$nom_groupe = $1;
    		} # Fin if
    		elsif ($ligne =~ m/^(\d+)\/(.+?)\/(\w+);(.+) (.+);(\d+)$/) {
    		push @{$groupe{$nom_groupe}}, $ligne;
    		$nb_elem++;
    		} # Fin elsif
    	} # Fin foreach
     
    print "$nb_elem element a re-analyser!\n";
     
    my %barrycentre;
     
    #######################################################
    #	DEBUT DE L'ALGORITHME
    #######################################################
    print "Passe 2\n" if ($debug);
    my $deplacements=0;
     
    ALGO_PASSE2:
    #### < Initialisation > #### Ici serait mon erreur ??
    # Calcule du barrycentre de chaque groupe
    %barrycentre=();
    	foreach my $groupe (keys %groupe) { # Parcourt chaque groupe récupéré
    	my ($somme_x, $somme_y, $nb_ds)=(0,0,0);
    		foreach my $ligne (@{$groupe{$groupe}}) {
    			if ($ligne =~ m/^(\d+)\/(.+?)\/(\w+);(.+) (.+);(\d+)$/) {
    			my $x = $4;
    			my $y = $5;
    			$somme_x+=$x;
    			$somme_y+=$y;
    			$nb_ds++;
    			} # Fin if
    		} # Fin foreach
    		if ($nb_ds>0) {
    		my $b_x = $somme_x/$nb_ds;
    		my $b_y = $somme_y/$nb_ds;
    		$barrycentre{$groupe} = "$b_x $b_y";
    		} # Fin if
    	} # Fin foreach
     
    # Tri de @{$groupe{groupe}} par éloignement du centroide des datasets par rapport au barrycentre du groupe
    # Afin d'avoir une liste du dataset le plus eloigné au dataset le plus rapproché du barrycentre de son groupe (le plus eloigné etant peut-etre un dataset à déplacer de CD).
    	foreach my $groupe (keys %groupe) {
    	my @liste = @{$groupe{$groupe}}; # On recupere la liste actuelle (non triée)
    	my $barrycentre = $barrycentre{$groupe};
    	my @liste_triee = &trier_datasets_par_eloignement($barrycentre, @liste);
    	my $i=-1;
    		foreach my $dataset (@liste_triee){ # Reaffectation ordonnancée des datasets dans %groupe
    		$i++; # Nom du groupe
    		$groupe{$groupe}[$i] = $dataset;
    		} # Fin foreach
    	} # Fin de foreach
     
    #### < FIN Initialisation > ####
     
    # Boucle generale sur chaque groupe (ordre quelconque)
    	foreach my $groupe (keys %groupe) {
    		foreach my $dataset (@{$groupe{$groupe}}) { # Parcourt les datasets, du plus eloigné du barrycentre au plus proche
    		next if ($dataset eq '');
    			if ($dataset =~ m/(.+);(.+) (.+);(\d+)/) { # On recup le centroide et la taille du dataset
    			my $id = $1;
    			my $c_x = $2; # X Centroide
    			my $c_y = $3; # Y Centroide
    			my $taille = $4;
    			my $point_centroide =  [[$c_x],[$c_y]];
    			my ($b_x, $b_y) = split(/\s/,$barrycentre{$groupe}); # Barrycentre du groupe proprietaire
    			my $barrycentre_proprietaire =  [[$b_x],[$b_y]];
    			my ($distance_proprietaire) = &distance_2_points($barrycentre_proprietaire, $point_centroide);
     
    			# On va parcourir tous les barrycentres des autres groupes et comparer leur distance au centroide par rapport à la distance du groupe proprietaire
    			my @groupes_candidats;  # Contiendra les groupes plus proches
    				foreach my $groupe_barrycentre (keys %barrycentre){
    				next if ($groupe_barrycentre == $groupe); # Ignore le meme groupe!
    				my ($b2_x, $b2_y) = split(/\s/,$barrycentre{$groupe_barrycentre}); # Barrycentre du groupe testé
    				my $barrycentre_test=  [[$b2_x],[$b2_y]];
    				my ($distance_test) = &distance_2_points($barrycentre_test, $point_centroide);
    					if ($distance_test < $distance_proprietaire){ # Groupe plus proche.... on l'ajoute à la liste candidate
    					push @groupes_candidats, "$distance_test, $groupe_barrycentre"; # Dist, ID du candidat
    					} # Fin if groupe plus proche
    				} # Fin foreach
     
    			# Il faut trier @groupes_candidats par distance croissante, puis prendre le premier groupe où il reste assez de place.
    				foreach my $candidat (sort {$a <=> $b} @groupes_candidats) { # On parcourt les candidats du plus proche au plus éloigné du dataset analysé
    				my($distance, $groupe_candidat)=split(/, /,$candidat);
    				# Reste-t-il de la place?
    				my $occupation_groupe = 0;
    					foreach my $element (@{$groupe{$groupe_candidat}}) { # On calcule le poids du groupe
    						if ($element =~ m/.+;.+ .+;(\d+)/) {
    						$occupation_groupe+=$1;
    						} # Fin if
    						else {
    						&afficher_erreur("\aERREUR DANS CE SCRIPT: Calcule du poids d'un groupe candidat impossible.\n") if (defined $element); # Condition necessaire car on supprime des elements qu'on deplace, et si on n'a pas reinitialisé l'algo! (normalement, on reinitialise...)
    						} # Fin else
    					} # Fin de foreach
    				$occupation_groupe/=1024;
    				$taille/=1024;
     
    					if ($config{capacite_media} - $occupation_groupe >= $taille) {
    					$deplacements++;
    					my $nouvelle_taille = $occupation_groupe+$taille;
    					print "DEPLACEMENT DE $id DANS LE GROUPE $groupe_candidat ($occupation_groupe Mo)!\n"; #Nouvelle taille: $nouvelle_taille Mo\n
     
    					# On effectue le deplacement:
    					push @{$groupe{$groupe_candidat}}, $dataset; # Copie
    					undef $dataset; # Supprime le dataset de son groupe originaire!
     
    					goto ALGO_PASSE2; # Reinitialisation de l'algorithme
    					last; # Sort de la boucle (on a le groupe le plus proche pouvant recevoir le dataset) -> si on ne reinitialise pas l'algo!
    					} # Fin if
    				} # Fin foreach candidat pesé
     
    			} # Fin if
    			else {
    			&afficher_erreur("\aERREUR: Un dataset n'a pu etre correctement traite:\n$dataset\n");
    			} # Fin else
    		} # Fin foreach
    	} # Fin de foreach
     
     
    # TErminé
    print "$deplacements elements deplaces par la seconde passe!\n";
     
    # Autre bug: Si j'utilise ce bloc de code (auquel cas, je ne reinitialise pas l'algo  apres chaque deplacement), j'obtiens une sortie tres similaire à 'lentree (se corrige puis se 'decourrige'....). Je ne comprend pas non plus. Mais c tjs lié à ma réinitialisation.
    #	if ($deplacements>0) {
    #	$deplacements=0;
    #	goto ALGO_PASSE2; # Reinitialisation de l'algorithme
    #	} # Fin if
     
     
    	if ($deplacements>0) {
    	# On ecrit le resultat:
    	my %liste_id; # Pour corriger un bug dans l'algorithme!
    	my $nb_elem=0;
    	my $t = open(COMPILATIONS, ">$config{dir_tmp}compilations.tmp");
    		if ($t) {
    			foreach my $groupe (sort {$a <=> $b} keys %groupe) {
    			print COMPILATIONS "\nINDEX=$groupe\n";
    			# Bug:
    			#$"="\n";
    			#my @liste = sort @{$groupe{$groupe}};
    			#print COMPILATIONS "@liste\n\n";
    				foreach my $element (@{$groupe{$groupe}}) {
    					if ($element=~m/^(.*)?;/) { # Id
    						if (! exists $liste_id{$1}){ # Pas deja ecrit!
    						$liste_id{$1}=1;
    						print COMPILATIONS "$element\n";
    						$nb_elem++;
    						} # Fin if
    						else {
    						print "Manifestation du bug des doublons de la passe 2\n";
    						} # Fin else
    					} # Fin if
    				} # Fin foreach
     
     
    			} # Fin foreach
    		} # Fin if
    		else {
    		&afficher_erreur("\aERREUR: Impossible de reecrire compilations.tmp a l'issue de la seconde passe!\n");
    		} # Fin else
    	close COMPILATIONS;
    	} # Fin if deplacements
     
    print "$nb_elem datasets recompiles!\n";
    } # Fin de sub
    Voici les resultats possibles:
    Nom : compilations_reinit.gif
Affichages : 90
Taille : 38,2 Ko
    Si je reinitialise apres chaque déplacement d'emprise.
    C'est là qu'on voit le prob (tailles > 700 Mo et bugs de repartition).

    Nom : compilations_nr.gif
Affichages : 64
Taille : 36,1 Ko
    Si je met en commentaire la ligne 'goto ALGO_PASSE2' (pas de reinitialisation).
    Ce resultat est bien meilleur....

  2. #2
    Membre chevronné
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2003
    Messages
    1 582
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Février 2003
    Messages : 1 582
    Points : 2 030
    Points
    2 030
    Par défaut
    Hum eh bien...

    Qui s'y colle ?

    Pas moi, j'ai rien capté au résultat final, alors pour retrouver le bug !

    Ledjai ou Pospos, z'allez faire ça très bien, hein les pros ?

    Bon sinon, + sérieusement, quand je travaille sur un script qui bug, j'essaie dans la mesure du possible de mettre des print de mes variables aux endroits où j'estime que peut se nicher le bug. Je te recommenderai donc de mettre des print espion un peu partout dans ton code pour savoir exactement où ça coince.

    Pour info, je l'ai fait un jour sur un script d'environ 7000 lignes qui buggait et j'ignorais pourquoi. Mettre en place un mode DEBUG m'a pris qq jours mais grâce à de simples print, j'ai trouvé où ça coinçait.

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Août 2005
    Messages
    346
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2005
    Messages : 346
    Points : 119
    Points
    119
    Par défaut print debug
    Hello!

    Merci pour ta reponse... j'avais peur en effet que ce soit rebutant.
    C'est pour ça que j'ai deja filtré au minimum.. j'ai déjà mis des print "" if ($debug), mais .... soit ca ne m'a pas aidé pour ça, justement... ainsi que Dumper(%groupe) (Data:umper). Ca met en evidence le probleme... mais ca ne m'a pas aidé à bien le localiser.
    Tout ce que j'ai pu déduire, c'est que ca venait du fait de réinitialiser (goto ALGO_PASSE2).... il semblerait que le fait d'enlever un element de mon hachage (avec undef $groupe{$groupe}[i]), reinitialiser l'algo qui traite ensuite ce hachage fasse qqchose que je ne m'explique pas...

    Si qqun veut s'y coller serieusemene,t je peux tjs lui envoyer par mail l'ensemble pour voir le script tourner.
    Ca doit etre très con (ça l'est toujours...). Je pense qu'à force de bosser dessus des heures, on finit par ne plus voir l'evidence!

  4. #4
    Membre régulier
    Profil pro
    Inscrit en
    Août 2005
    Messages
    346
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2005
    Messages : 346
    Points : 119
    Points
    119
    Par défaut à moitié corrigé...
    Bonjour!

    Bon le probleme de la taille est résolu, la contrainte de taille est de nouveau respectée. C'était tres con evidemment:
    Avant de tester si la $taille de la carte entrait dans un CD, je la convertissais des Ko en Mo.... sauf que dans une boucle... hum... ca se divisait plusieurs fois par 1024 pour une meme carte. Cette ligne, une fois placée apres la condition, corrige le bug des tailles.

    J'ai toujours le pb des doublons, moins gênant car je le corrige par la suite, mais ça m'ennuie car ce n'est pas propre et je ne comprend toujours pas pourquoi je les obtiens... probleme de boucle(s) à l'intérieur du foreach qui parcours les %groupe, certainement, mais pourquoi?

    Si cet algo intéresse qqun, cependant, il me donne des résultats très bien. Il reste encore à ajouter une étape que je ferais bientot: actuellement, les groupes sont traités dans un ordre aléatoire (foreach (keys %groupe)). Ce qui fait qu'on a plus de chances de traiter en premier des groupes plutôt corrects, et de perdre la place potentielle dans d'autres groupes en réaffectant des emprises relativement correctes, en tout cas, plus que d'autres.
    Il faut donc traiter les groupes: du plus mal réparti au mieux réparti.

    Pour cela, je vais calculer, en plus du barrycentre (centre de gravité) de chaque groupe, leur centroïde (centre du polygone minimum englobant). Puis trier les groupes par distance entre le centroïde et le barrycentre. Les groupes les plus homogènes auront un centroïde et un barrycentre très proche, les plus inégalement répartis auront une plus grande distance.
    Ce sont ces derniers qui seront prioritairement traités.

    Bon week-end.

Discussions similaires

  1. bug dans mon programme (message d'erreur)
    Par maxmarie dans le forum Windows Mobile
    Réponses: 10
    Dernier message: 13/09/2007, 10h34
  2. [OpenGL] Bug dans mon projet d'interface OpenGL
    Par Ceylo dans le forum Développement OS X
    Réponses: 12
    Dernier message: 01/07/2007, 22h22
  3. [Vba-E]Bug dans mon code
    Par antoinelavigne dans le forum Macros et VBA Excel
    Réponses: 9
    Dernier message: 19/06/2006, 14h55
  4. Bug dans mon stylessheet
    Par wikipierre dans le forum Balisage (X)HTML et validation W3C
    Réponses: 6
    Dernier message: 19/06/2006, 10h16
  5. Bug dans mon timer
    Par FredKéKé dans le forum Général JavaScript
    Réponses: 13
    Dernier message: 25/01/2006, 15h27

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