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
| Sub Villes()
Dim TabVilleEtape
Dim TheCell As Range
Dim DicoDistance As New Dictionary
Dim DicoVille As New Dictionary
Dim LastRow As Integer
Dim x As Integer, y As Integer, iPos As Integer
Dim TabRetour
Dim tmpDist As Double, tmpParcours As String
'On place les km dans le dico (plus rapide que d'aller chercher dans le tableau excel
With ThisWorkbook.Sheets("Données")
LastRow = .Range("B28").End(xlUp).Row
TabVilleEtape = .Range("B7", .Cells(LastRow, "B").Offset(0, LastRow - 7)).Value
End With
'On boucle dans le tableau et on le classe par longueur de trajet
For x = 2 To LastRow - 6
For y = 2 To LastRow - 6
tmpDist = CDbl(TabVilleEtape(x, y))
tmpParcours = TabVilleEtape(1, x) & "¤" & TabVilleEtape(1, y)
For iPos = 0 To DicoDistance.Count - 1
If tmpDist < DicoDistance.Items(iPos) Then
'On le place devant
InsertDico DicoDistance, iPos, tmpParcours, tmpDist
GoTo suite
End If
Next
DicoDistance.Add tmpParcours, tmpDist
suite:
Next
Next
'On récupère la liste des villes étapes
With ThisWorkbook.Sheets("Données")
For Each TheCell In .Range("X10", .Range("X29").End(xlUp))
DicoVille.Add TheCell.Value, ""
Next
End With
'On appelle PermuteVilles
With ThisWorkbook.Sheets("Données")
TabRetour = PermuteVilles(DicoDistance, DicoVille, .Range("X8"), .Range("X9"))
'On place le meilleur parcours dans le tabelau excel
'On vide
.Range("Y8:Y28").ClearContents
'On inscrit les km
.Range("AA7").Value = Split(TabRetour, "$")(1)
'On ne conserve que le parcours
TabRetour = Split(TabRetour, "$")(0)
'On permute la chaine en tableau
TabRetour = Split(TabRetour, "¤")
'On place le tableau
.Range("Y8").Resize(UBound(TabRetour) + 1).Value = WorksheetFunction.Transpose(TabRetour)
End With
End Sub
Function PermuteVilles(DicoKm As Dictionary, DicoVille As Dictionary, VilleDepart As String, VilleArrive, Optional aParcours As String = "<vide>", Optional aDistance As Double)
Dim DicoVilleTmp As New Dictionary
Dim iVille As Integer, iVilleT As Integer, iPos As Integer
Dim tmpDist As Double, tmpParcours As String
Static MeilleurKm As Double
Static MeilleurParcours As String
Static NbrPermute As Long
Dim ParcoursEnCours As String
Dim Distance As Double
Dim MustExit As Boolean
Dim ParcoursTmp As String
Dim DistanceTmp As Double
Dim DistanceSuppose As Double
Dim DicoTrajetRestant As New Dictionary
'On prolonge le parcours existant
If aParcours <> "<vide>" Then
ParcoursEnCours = aParcours
Distance = aDistance
Else
'On initialise les variables "Meilleur", les variables static sont conservées, même aprés une analyse complete
MeilleurKm = 0
MeilleurParcours = ""
'Et On début le parcours
ParcoursEnCours = VilleDepart
NbrPermute = 0
End If
'On clone le dico, moins la ville de départ
For iVille = 0 To DicoVille.Count - 1
If DicoVille.Keys(iVille) <> VilleDepart Then
DicoVilleTmp.Add DicoVille.Keys(iVille), ""
End If
For iVilleT = iVille + 1 To DicoVille.Count - 1
tmpDist = DicoKm(VilleDepart & "¤" & DicoVille.Keys(iVilleT))
tmpParcours = DicoVille.Keys(iVille) & "¤" & DicoVille.Keys(iVilleT)
'On boucle sur le contenu du dico
For iPos = 0 To DicoTrajetRestant.Count - 1
If tmpDist < DicoTrajetRestant.Items(iPos) Then
'On l'ajoute devant
InsertDico DicoTrajetRestant, iPos, tmpParcours, tmpDist
GoTo suite
End If
Next
'Si l'execution du code passe par ici, la valeur doit être ajoutée à la suite du dico
DicoTrajetRestant.Add tmpParcours, tmpDist
suite:
Next
Next
'On calcule les km de trajet max en fonction du nombre de villes restant à visiter
If DicoTrajetRestant.Count > 0 Then DicoTrajetRestant.Key(DicoTrajetRestant.Keys(0)) = 1
For iPos = 1 To DicoTrajetRestant.Count - 1
DicoTrajetRestant.Key(DicoTrajetRestant.Keys(iPos)) = iPos + 1
DicoTrajetRestant(iPos + 1) = DicoTrajetRestant.Items(iPos) + DicoTrajetRestant.Items(iPos - 1)
Next
'Pour éviter les erreurs par la suite
DicoTrajetRestant.Add 0, 0
'On calcule les distances entre villeDepart et toutes les villes étapes
For iVille = 0 To DicoVilleTmp.Count - 1
NbrPermute = NbrPermute + 1
'On ajoute la ville au parcours et distance temporaires, pour conserver les bon contenu dans ParcoursEnCours et Distance
'Comme ça, après l'appel récursif, ParcoursEnCours et Distance contiennent les bonne valeur pour accépter le teste d'une nouvelle ville
If ParcoursEnCours <> "" Then ParcoursTmp = ParcoursEnCours & "¤"
ParcoursTmp = ParcoursTmp & DicoVilleTmp.Keys(iVille)
DistanceTmp = Distance + DicoKm(VilleDepart & "¤" & DicoVilleTmp.Keys(iVille))
'On ajoute à la distance, le nombre de km minimum qu'il reste à parcourir pour faire le nombre de trajet au total
DistanceSuppose = DistanceTmp + DicoTrajetRestant(DicoVilleTmp.Count - 1)
'On note dans le tableau Excel (analyse)
With ThisWorkbook.Sheets("Données")
With .Cells(.Rows.Count, "B").End(xlUp).Offset(1)
.Value = ParcoursTmp
.Offset(0, 1) = DistanceTmp
.Offset(0, 2) = MeilleurKm
.Offset(0, 3) = DistanceSuppose
End With
End With
'Si la distance dépasse la distance la plus courte déjà trouvée, en tenant compte du nombre de trajet qu'il reste à faire
If MeilleurKm = 0 Or MeilleurKm > DistanceSuppose Then
PermuteVilles DicoKm, DicoVilleTmp, CStr(DicoVilleTmp.Keys(iVille)), VilleArrive, ParcoursTmp, DistanceTmp
End If
Next
If DicoVilleTmp.Count = 0 Then
'On boucle le trajet avec la ville d'arrivé
If ParcoursEnCours <> "" Then ParcoursEnCours = ParcoursEnCours & "¤"
ParcoursEnCours = ParcoursEnCours & VilleArrive
Distance = Distance + DicoKm(VilleDepart & "¤" & VilleArrive)
'On verifie les km
If MeilleurKm = 0 Or MeilleurKm > Distance Then
MeilleurKm = Distance
MeilleurParcours = ParcoursEnCours
End If
'On note dans le tableau Excel (analyse)
With ThisWorkbook.Sheets("Données")
With .Cells(.Rows.Count, "B").End(xlUp).Offset(1)
.Value = ParcoursEnCours 'ParcoursTmp
.Offset(0, 1) = Distance 'Tmp
.Offset(0, 2) = MeilleurKm
'.Offset(0, 3) = DistanceSuppose
End With
End With
'On vide les variables pour annalyser le prochain parcours
Distance = 0
ParcoursEnCours = ""
End If
'On retourne la meilleur combinaison
PermuteVilles = MeilleurParcours & "$" & CStr(MeilleurKm) & " / " & NbrPermute
End Function
Sub InsertDico(ByRef aDico As Dictionary, AvantItem As Integer, aKey, aItem)
Dim iPos As Integer
Dim tmpKey, tmpItem
'On place les donnée en fin de dico pour créer une ligne
aDico.Add aKey, aItem
'On boucle à partie du bas
For iPos = aDico.Count - 1 To AvantItem + 1 Step -1
'On mémorise les valeurs
tmpKey = aDico.Keys(iPos - 1)
tmpItem = aDico.Items(iPos - 1)
'On modifie le contenu de key pour éviter les doublons le temps de la copie
aDico.Key(aDico.Keys(iPos - 1)) = tmpKey & "¤¤"
'On décale chaque valeur vers le bas
aDico.Key(aDico.Keys(iPos)) = tmpKey
aDico(aDico.Keys(iPos)) = tmpItem
Next
'On place ensuite les nouvelle valeur à leur place d'insertion
aDico.Key(aDico.Keys(AvantItem)) = aKey
aDico(aDico.Keys(AvantItem)) = aItem
End Sub |
Partager