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 : 98
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 : 75
Taille : 36,1 Ko
Si je met en commentaire la ligne 'goto ALGO_PASSE2' (pas de reinitialisation).
Ce resultat est bien meilleur....