1 | using System;
|
---|
2 | using System.Collections.Generic;
|
---|
3 | using System.Linq;
|
---|
4 | using System.Text;
|
---|
5 | using System.IO;
|
---|
6 |
|
---|
7 | namespace VRPProblemAnalyzer {
|
---|
8 | public static class Maths {
|
---|
9 | public static double StandardDeviation(this IEnumerable<double> data) {
|
---|
10 | double average = data.Average();
|
---|
11 | var individualDeviations = data.Select(num => Math.Pow(num - average, 2.0));
|
---|
12 | return Math.Sqrt(individualDeviations.Average());
|
---|
13 | }
|
---|
14 | }
|
---|
15 |
|
---|
16 | class Program {
|
---|
17 |
|
---|
18 | private static double GetDistance(double[,] vertices, int source, int target) {
|
---|
19 | return Math.Sqrt(
|
---|
20 | Math.Pow((vertices[source, 0] - vertices[target, 0]), 2) +
|
---|
21 | Math.Pow((vertices[source, 1] - vertices[target, 1]), 2));
|
---|
22 | }
|
---|
23 |
|
---|
24 | private static double[] GetDistances(double[,] vertices) {
|
---|
25 | List<double> result = new List<double>();
|
---|
26 |
|
---|
27 | int cities = vertices.Length / 2;
|
---|
28 | for (int i = 0; i < cities; i++) {
|
---|
29 | for (int j = 0; j < cities; j++) {
|
---|
30 | if (i != j) {
|
---|
31 | result.Add(GetDistance(vertices, i, j));
|
---|
32 | }
|
---|
33 | }
|
---|
34 | }
|
---|
35 |
|
---|
36 | return result.ToArray();
|
---|
37 | }
|
---|
38 |
|
---|
39 | private static double[] GetAverageDistances(double[,] vertices) {
|
---|
40 | int cities = vertices.Length / 2;
|
---|
41 | double[] result = new double[cities];
|
---|
42 |
|
---|
43 | for (int i = 0; i < cities; i++) {
|
---|
44 | double distance = 0;
|
---|
45 | for (int j = 0; j < cities; j++) {
|
---|
46 | if (i != j) {
|
---|
47 | distance += GetDistance(vertices, i, j);
|
---|
48 | }
|
---|
49 | }
|
---|
50 | result[i] = distance / (cities - 1);
|
---|
51 | }
|
---|
52 |
|
---|
53 | return result;
|
---|
54 | }
|
---|
55 |
|
---|
56 | private static double GetAverageDistance(TSPLIBParser instance) {
|
---|
57 | double result = 0;
|
---|
58 |
|
---|
59 | double[] distances = GetDistances(instance.Vertices);
|
---|
60 | result = distances.Average();
|
---|
61 |
|
---|
62 | return result;
|
---|
63 | }
|
---|
64 |
|
---|
65 | private static double GetDistanceHeterogenity(TSPLIBParser instance) {
|
---|
66 | double result = 0;
|
---|
67 |
|
---|
68 | result = GetAverageDistances(instance.Vertices).StandardDeviation();
|
---|
69 |
|
---|
70 | return result;
|
---|
71 | }
|
---|
72 |
|
---|
73 | private static double GetDistance(double[] cust1, double[] cust2) {
|
---|
74 | return Math.Sqrt(
|
---|
75 | Math.Pow((cust1[0] - cust2[0]), 2) +
|
---|
76 | Math.Pow((cust1[1] - cust2[1]), 2));
|
---|
77 | }
|
---|
78 |
|
---|
79 | private static double GetDistance(Cluster cluster1, Cluster cluster2) {
|
---|
80 | List<double> distances = new List<double>();
|
---|
81 | foreach (double[] cust1 in cluster1.Items) {
|
---|
82 | foreach (double[] cust2 in cluster2.Items) {
|
---|
83 | distances.Add(GetDistance(cust1, cust2));
|
---|
84 | }
|
---|
85 | }
|
---|
86 |
|
---|
87 | return distances.Min();
|
---|
88 |
|
---|
89 | //return GetDistance(cluster1.Center, cluster2.Center);
|
---|
90 | }
|
---|
91 |
|
---|
92 | private static double GetDistance(List<Cluster> clusters, Cluster cluster) {
|
---|
93 | List<double> distances = new List<double>();
|
---|
94 | foreach (Cluster other in clusters) {
|
---|
95 | if (other != cluster) {
|
---|
96 | distances.Add(GetDistance(cluster, other));
|
---|
97 | }
|
---|
98 | }
|
---|
99 |
|
---|
100 | if (distances.Count > 0)
|
---|
101 | return distances.Min();
|
---|
102 | else
|
---|
103 | return 0;
|
---|
104 | }
|
---|
105 |
|
---|
106 | private static double GetCohesion(Cluster cluster) {
|
---|
107 | double cohesion = 0;
|
---|
108 |
|
---|
109 | List<double> distances = new List<double>();
|
---|
110 | foreach (double[] cust1 in cluster.Items) {
|
---|
111 | //option 1
|
---|
112 | foreach (double[] cust2 in cluster.Items) {
|
---|
113 | if (cust1 != cust2) {
|
---|
114 | distances.Add(GetDistance(cust1, cust2));
|
---|
115 | }
|
---|
116 | }
|
---|
117 |
|
---|
118 | //option2
|
---|
119 | //distances.Add(GetDistance(cluster.Center, cust1));
|
---|
120 | }
|
---|
121 |
|
---|
122 | if (distances.Count > 0)
|
---|
123 | cohesion = distances.Average();
|
---|
124 |
|
---|
125 | return cohesion;
|
---|
126 | }
|
---|
127 |
|
---|
128 | private static double GetClustering(TSPLIBParser instance) {
|
---|
129 | double result = 0;
|
---|
130 |
|
---|
131 | int cities = instance.Vertices.Length / 2 - 1;
|
---|
132 | double[,] coordinates = new double[cities, 2];
|
---|
133 | for (int i = 1; i <= cities; i++) {
|
---|
134 | coordinates[i - 1, 0] = instance.Vertices[i, 0];
|
---|
135 | coordinates[i - 1, 1] = instance.Vertices[i, 1];
|
---|
136 | }
|
---|
137 |
|
---|
138 | double clusterFactor = 0;
|
---|
139 | double couplingFactor = 1;
|
---|
140 | double cohesionFactor = 1;
|
---|
141 |
|
---|
142 | double maxQuality = double.MinValue;
|
---|
143 |
|
---|
144 | int clusters = -1;
|
---|
145 | for (int i = 1; i <= cities; i++) {
|
---|
146 | List<Cluster> collection = KMeansClustering.Cluster(coordinates, i, 9);
|
---|
147 |
|
---|
148 | if (collection.Count > 0) {
|
---|
149 | List<double> distances = new List<double>();
|
---|
150 | List<double> cohesions = new List<double>();
|
---|
151 | foreach (Cluster cluster in collection) {
|
---|
152 | distances.Add(GetDistance(collection, cluster));
|
---|
153 | cohesions.Add(GetCohesion(cluster));
|
---|
154 | }
|
---|
155 |
|
---|
156 | double distance = distances.Average();
|
---|
157 | double cohesion = cohesions.Average();
|
---|
158 |
|
---|
159 | double quality = clusterFactor * (distances.Count / (double)cities) + couplingFactor * distance - cohesionFactor * cohesion;
|
---|
160 | if (quality > maxQuality) {
|
---|
161 | maxQuality = quality;
|
---|
162 | clusters = distances.Count;
|
---|
163 | }
|
---|
164 | }
|
---|
165 | }
|
---|
166 |
|
---|
167 | if (clusters != 0)
|
---|
168 | result = (cities / (double)clusters) / cities;
|
---|
169 |
|
---|
170 | return result;
|
---|
171 | }
|
---|
172 |
|
---|
173 | private static double GetDemandSize(TSPLIBParser instance) {
|
---|
174 | double result = 0;
|
---|
175 |
|
---|
176 | result = instance.Demands.Average();
|
---|
177 |
|
---|
178 | return result;
|
---|
179 | }
|
---|
180 |
|
---|
181 | private static double GetDemandHeterogenity(TSPLIBParser instance) {
|
---|
182 | double result = 0;
|
---|
183 |
|
---|
184 | result = instance.Demands.StandardDeviation();
|
---|
185 |
|
---|
186 | return result;
|
---|
187 | }
|
---|
188 |
|
---|
189 | private static void Normalize(TSPLIBParser instance) {
|
---|
190 | //normalize demands
|
---|
191 | for (int i = 0; i < instance.Demands.Length; i++) {
|
---|
192 | instance.Demands[i] = instance.Demands[i] / instance.Capacity;
|
---|
193 | }
|
---|
194 |
|
---|
195 | //normalize coordinates
|
---|
196 | //-find bounds
|
---|
197 | double maxX = double.MinValue;
|
---|
198 | double maxY = double.MinValue;
|
---|
199 |
|
---|
200 | int cities = instance.Vertices.Length / 2;
|
---|
201 | for (int i = 0; i < cities; i++) {
|
---|
202 | if (instance.Vertices[i, 0] > maxX)
|
---|
203 | maxX = instance.Vertices[i, 0];
|
---|
204 |
|
---|
205 | if (instance.Vertices[i, 1] > maxY)
|
---|
206 | maxY = instance.Vertices[i, 1];
|
---|
207 | }
|
---|
208 |
|
---|
209 | //-normalize
|
---|
210 | double factorX = 1;
|
---|
211 | double factorY = 1;
|
---|
212 | if (maxX > maxY) {
|
---|
213 | factorX = 1 / maxX;
|
---|
214 | factorY = (maxY / maxX) / maxY;
|
---|
215 | } else {
|
---|
216 | factorX = (maxX / maxY) / maxX;
|
---|
217 | factorY = 1 / maxY;
|
---|
218 | }
|
---|
219 |
|
---|
220 | for (int i = 0; i < cities; i++) {
|
---|
221 | instance.Vertices[i, 0] *= factorX;
|
---|
222 | instance.Vertices[i, 1] *= factorY;
|
---|
223 | }
|
---|
224 | }
|
---|
225 |
|
---|
226 | static void Main(string[] args) {
|
---|
227 | if (args.Length < 1) {
|
---|
228 | Console.Error.WriteLine("Not enough arguments, please specify directory with VRP instances.");
|
---|
229 | Environment.Exit(-1);
|
---|
230 | }
|
---|
231 | var path = args[0];
|
---|
232 | using (StreamWriter sw = new StreamWriter(Path.Combine(path, "analysis.csv"))) {
|
---|
233 | sw.WriteLine("Instance;Customers;Clustering;DistanceAvg;DistanceStdev;DemandAvg;DemandStdev");
|
---|
234 |
|
---|
235 | string[] instances = Directory.GetFiles(path, "*.vrp", SearchOption.AllDirectories);
|
---|
236 | foreach (string instance in instances) {
|
---|
237 | TSPLIBParser parser = new TSPLIBParser(instance);
|
---|
238 | parser.Parse();
|
---|
239 | Normalize(parser);
|
---|
240 |
|
---|
241 | sw.Write(Path.GetFileNameWithoutExtension(instance));
|
---|
242 | sw.Write(';');
|
---|
243 | sw.Write(parser.Vertices.Length / 2 - 1);
|
---|
244 | sw.Write(';');
|
---|
245 | sw.Write(GetClustering(parser));
|
---|
246 | sw.Write(';');
|
---|
247 | sw.Write(GetAverageDistance(parser));
|
---|
248 | sw.Write(';');
|
---|
249 | sw.Write(GetDistanceHeterogenity(parser));
|
---|
250 | sw.Write(';');
|
---|
251 | sw.Write(GetDemandSize(parser));
|
---|
252 | sw.Write(';');
|
---|
253 | sw.Write(GetDemandHeterogenity(parser));
|
---|
254 | sw.Write(';');
|
---|
255 | sw.WriteLine();
|
---|
256 | }
|
---|
257 | }
|
---|
258 | }
|
---|
259 | }
|
---|
260 | }
|
---|