#include #include #include #include #include #include #include #include #include "benchmark-data.h" /* Types */ typedef int *solution; /* Functions */ double distance(const vector u, const vector v); bool trouver_solution_initiale(void); bool solution_valide(const solution s); void print_solution(const solution s, const char *name); vector *centres_gravite(const solution s); int solution_voisine(solution *dest, const solution src); double fonction_objective(const solution s); static double calculer_inter(const vector *cgs); static double calculer_intra(const solution s, const vector *cgs); /* Variables */ solution solution_opt; solution new_solution; int n_clusters = 5; int n_iterations = 1000; int main(int argc, char *argv[]) { int c; int i; double f0; while ((c = getopt(argc, argv, ":c:i:h")) != -1) { int k; switch (c) { case 'c': k = atoi(optarg); if (k > 1) n_clusters = k; break; case 'i': k = atoi(optarg); if (k > 0) n_iterations = k; break; case 'h': printf("Usage: %s [-c N_CLUSTERS] [-i N_ITERATIONS]", argv[0]); exit(EXIT_SUCCESS); break; default: exit(EXIT_FAILURE); } } printf("Using %d clusters\n", n_clusters); /* Init benchmark_data */ init_benchmark_data(); srand(time(NULL)); if (!(solution_opt = calloc(N_VECTORS, sizeof(solution_opt[0])))) abort(); /* Solution initiale */ while (!trouver_solution_initiale()) continue; /* do it again */ f0 = fonction_objective(solution_opt); print_solution(solution_opt, "initiale"); printf("F(solution) = %f\n", f0); for (i = 0; i < n_iterations; i++) { double f1; while (!solution_voisine(&new_solution, solution_opt)) continue; /* do it again */ if ((f1 = fonction_objective(new_solution)) > f0) { print_solution(new_solution, "optimale trouvée"); printf("F(solution) = %f (%f%% improvement)\n", f1, fabs((f1 - f0) / f0 * 100.0)); free(solution_opt); solution_opt = new_solution; new_solution = NULL; f0 = f1; } } /* Free solution data */ free(solution_opt); free(new_solution); /* Free benchmark data */ for (i = 0; i < (int) N_VECTORS; i++) free(benchmark_data[i]); free(benchmark_data); exit(EXIT_SUCCESS); } double distance(const vector u, const vector v) { size_t i = 0; double sum = 0.0; if (!u || !v) { fputs("distance(): u or v is null.\n", stderr); abort(); } /* d(u, v) = sqrt( (u1 - v1)^2 + (u2 - v2)^2 + ... + (un - vn)^2 ) */ for (; i < VECTOR_SIZE; i++) sum += pow(u[i] - v[i], 2); return sqrt(sum); } bool trouver_solution_initiale(void) { size_t i; for (i = 0; i < N_VECTORS; i++) solution_opt[i] = rand() % n_clusters; return solution_valide(solution_opt); } bool solution_valide(const solution s) { /* This function returns true if the solution that it found is "valid", * i.e., it has at least one element in each cluster. */ bool *cluster_empty = calloc(n_clusters, sizeof *cluster_empty); bool valid = true; int i; for (i = 0; i < n_clusters; i++) cluster_empty[i] = true; for (i = 0; (size_t) i < N_VECTORS; i++) cluster_empty[s[i]] = false; for (i = 0; valid && i < n_clusters; i++) valid = (valid && !cluster_empty[i]); free(cluster_empty); return valid; } void print_solution(const solution s, const char *name) { size_t i = 0; printf("Solution%s%s: [\n", name ? " " : "", name ? name : ""); while (i < N_VECTORS) { const char *end; if (i + 1 == N_VECTORS) end = "]\n"; else if ((i + 1) % 20 == 0) end = ",\n"; else end = ", "; printf("%d%s", s[i++], end); } } vector *centres_gravite(const solution s) { /* Calculer le centre de gravité pour les vecteurs du cluster cluster selon la solution s */ int cluster; size_t i = 0; /* cgs[n_clusters] est le centre de gravité global */ vector *cgs = calloc(n_clusters + 1, sizeof(*cgs)); int *vectors_in_cluster = calloc(n_clusters + 1, sizeof(*vectors_in_cluster)); if (!cgs || !vectors_in_cluster) abort(); for (cluster = 0; cluster < n_clusters + 1; cluster++) { if (!(cgs[cluster] = calloc(VECTOR_SIZE, sizeof(*cgs[cluster])))) abort(); /* Initialiser à 0 */ while (i < VECTOR_SIZE) cgs[cluster][i++] = 0; if (cluster < n_clusters) vectors_in_cluster[cluster] = 0; } for (i = 0; i < N_VECTORS; i++) { size_t j; cluster = s[i]; ++vectors_in_cluster[cluster]; for (j = 0; j < VECTOR_SIZE; j++) { cgs[cluster][j] += benchmark_data[i][j]; cgs[n_clusters][j] += benchmark_data[i][j]; } } vectors_in_cluster[n_clusters] = N_VECTORS; for (cluster = 0; cluster < n_clusters + 1; cluster++) for (i = 0; i < VECTOR_SIZE; i++) cgs[cluster][i] /= (double) vectors_in_cluster[cluster]; free(vectors_in_cluster); return cgs; } int solution_voisine(solution *dest, const solution src) { /* This function changes about 10% of the solution randomly */ size_t i; const int a = 10000; /* b = 10% of a */ const int b = 1000; bool valid; if (!(*dest = calloc(N_VECTORS, sizeof(**dest)))) abort(); memcpy(*dest, src, N_VECTORS * sizeof(*src)); assert(n_clusters > 1); for (i = 0; i < N_VECTORS; i++) { if (rand() % a < b) { int new_cluster = rand() % (n_clusters - 1); /* To avoid the case where the cluster isn't actually changed. */ new_cluster += (new_cluster >= src[i]); (*dest)[i] = new_cluster; } } if (!(valid = solution_valide(*dest))) { free(*dest); *dest = NULL; } return valid; } double fonction_objective(const solution s) { int i; double **cgs = centres_gravite(s); double inter; double intra; if (!cgs) abort(); inter = calculer_inter(cgs); intra = calculer_intra(s, cgs); for (i = 0; i < n_clusters + 1; i++) free(cgs[i]); free(cgs); return inter - intra; } static double calculer_inter(const vector *cgs) { double inter = 0.0; int cluster; for (cluster = 0; cluster < n_clusters; cluster++) inter += distance(cgs[n_clusters], cgs[cluster]); return inter / (double) n_clusters; } static double calculer_intra(const solution s, const vector *cgs) { double intra = 0.0; int cluster; for (cluster = 0; cluster < n_clusters; cluster++) { double intra_for_this_cluster = 0.0; int vectors_in_this_cluster = 0; size_t i; for (i = 0; i < N_VECTORS; i++) { if (s[i] != cluster) continue; ++vectors_in_this_cluster; intra_for_this_cluster += distance(cgs[cluster], benchmark_data[i]); } intra += intra_for_this_cluster / (double) vectors_in_this_cluster; } return intra / (double) n_clusters; }