25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

324 satır
6.4 KiB

#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#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;
}