1. 程式人生 > >模擬退火演算法

模擬退火演算法

模擬退火演算法是一種求函式最小值點的隨機演算法,最近工作中要用到優化演算法,因此研究了一下這個比較簡單的演算法。模擬退火最基本的思想來源於金屬退火過程,在退火過程中,熱運動的原子逐漸凍結在勢能最低的位置,從而保證了整塊金屬晶格結構的一致性,達到最佳效能。模擬退火演算法的基本步驟如下:

1. 確定搜尋起始點x0和E(x0),其中E(x)為要求其最小值的函式。

2. 從x0出發在相空間中隨機走一步到x點(可固定步長,也可不斷減小步長加快收斂性)。

3. 比較E(x)和E(x0)的大小,如果E(x)<E(x0),則令x0=x,重複2;否則取一個隨機數r,如果r < exp[-(E-E0)/(k*T)],則令x0=x,重複2;如果r > exp[-(E-E0)/(k*T)],則直接重複2。其中E=E(x), E0=E(x0)。

4. 2和3步迴圈n次後,令溫度T下降一點,繼續重複2-3步。如果溫度T小於某個值T0,則整個過程結束。

在以上過程中,始終記錄當前的最小值Emin和對應的xmin,在過程結束後給出Emin作為函式最小值,xmin作為函式取最小值的點作為整個問題的近似解。

 

在實際計算中,需要根據問題的特點,適當調節步長的取值、迴圈次數n和溫度下降的比例,從而得到比較好的效果。下面給出模擬退火演算法的程式碼,該程式碼改編自GSL中的Simulated Annealing程式碼,為了在windows下執行做了部分改動,並將隨機數生成部分的程式碼單獨提出來,保證整個程式碼的獨立性,不依賴於VS之外的其他庫。

siman.h

#ifndef __GSL_SIMAN_H__
#define __GSL_SIMAN_H__
#include <stdlib.h>
#include <stdio.h>

#ifdef THIS_IS_DLL
#define PREFIXAPI __declspec(dllexport)
#else
#define PREFIXAPI __declspec(dllimport)
#endif

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
{
	const char* name;
	unsigned long int max;
	unsigned long int min;
	size_t size;
	void (*set) (void *state, unsigned long int seed);
	unsigned long int(*get) (void *state);
	double(*get_double) (void *state);
}
gsl_rng_type;

typedef struct
{
	const gsl_rng_type *type;
	void *state;
}
gsl_rng;

PREFIXAPI extern const gsl_rng_type *gsl_rng_default;
unsigned long int gsl_rng_default_seed;

PREFIXAPI const gsl_rng_type *gsl_rng_env_setup(const gsl_rng_type *r, unsigned long int s);
PREFIXAPI gsl_rng *gsl_rng_alloc(const gsl_rng_type *T);
PREFIXAPI void gsl_rng_free(gsl_rng *r);

/// 獲得整型隨機數的函式
__inline unsigned long int gsl_rng_get(const gsl_rng *r)
{
	return (r->type->get) (r->state);
}

/// 獲得實型隨機數的函式
__inline double gsl_rng_uniform(const gsl_rng *r)
{
	return (r->type->get_double) (r->state);
}

/* types for the function pointers passed to gsl_siman_solve */

typedef double (*gsl_siman_Efunc_t) (void *xp);
typedef void (*gsl_siman_step_t) (const gsl_rng *r, void *xp, double step_size);
typedef double (*gsl_siman_metric_t) (void *xp, void *yp);
typedef void (*gsl_siman_print_t) (void *xp);
typedef void (*gsl_siman_copy_t) (void *source, void *dest);
typedef void * (*gsl_siman_copy_construct_t) (void *xp);
typedef void (*gsl_siman_destroy_t) (void *xp);

/* this structure contains all the information needed to structure the
   search, beyond the energy function, the step function and the
   initial guess. */

typedef struct {
  int n_tries;          /* how many points to try for each step */
  int iters_fixed_T;    /* how many iterations at each temperature? */
  double step_size;     /* max step size in the random walk */
  /* the following parameters are for the Boltzmann distribution */
  double k, t_initial, mu_t, t_min;
} gsl_siman_params_t;

/* prototype for the workhorse function */

PREFIXAPI void gsl_siman_solve(const gsl_rng * r,
                     void *x0_p, gsl_siman_Efunc_t Ef,
                     gsl_siman_step_t take_step,
                     gsl_siman_metric_t distance,
                     gsl_siman_print_t print_position,
                     gsl_siman_copy_t copyfunc,
                     gsl_siman_copy_construct_t copy_constructor,
                     gsl_siman_destroy_t destructor,
                     size_t element_size,
                     gsl_siman_params_t params);

PREFIXAPI void
gsl_siman_solve_many (const gsl_rng * r, void *x0_p, gsl_siman_Efunc_t Ef,
                      gsl_siman_step_t take_step,
                      gsl_siman_metric_t distance,
                      gsl_siman_print_t print_position,
                      size_t element_size,
                      gsl_siman_params_t params);

__END_DECLS

#endif /* __GSL_SIMAN_H__ */

siman.c

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#define THIS_IS_DLL
#include "siman.h"

#define GSL_LOG_DBL_MIN (-7.0839641853226408e+02)

const gsl_rng_type *gsl_rng_default;
/// 隨機數初始化,實際上為底層隨機數生成器包了一層
const gsl_rng_type *gsl_rng_env_setup(const gsl_rng_type *r, unsigned long int s)
{
	unsigned long int seed = s;
	gsl_rng_default = r;
	gsl_rng_default_seed = seed;
	return gsl_rng_default;
}

/// 真正的隨機數初始化
void gsl_rng_set(const gsl_rng *r, unsigned long int seed)
{
	(r->type->set) (r->state, seed);
}

gsl_rng *gsl_rng_alloc(const gsl_rng_type *T)
{
	gsl_rng *r = (gsl_rng *)malloc(sizeof(gsl_rng));
	if (r == 0) printf("failed to allocate space for rng struct");
	r->state = calloc(1, T->size);
	if (r->state == 0)
	{
		free(r);
		printf("failed to allocate space for rng state");
	}
	r->type = T;
	gsl_rng_set(r, gsl_rng_default_seed);
	return r;
}

void gsl_rng_free(gsl_rng *r)
{
	if (!r) return;
	free(r->state);
	free(r);
}

static __inline double
boltzmann(double E, double new_E, double T, gsl_siman_params_t *params)
{
  double x = -(new_E - E) / (params->k * T);
  /* avoid underflow errors for large uphill steps */
  return (x < GSL_LOG_DBL_MIN) ? 0.0 : exp(x);
}

static __inline void
copy_state(void *src, void *dst, size_t size, gsl_siman_copy_t copyfunc)
{
  if (copyfunc) {
    copyfunc(src, dst);
  } else {
    memcpy(dst, src, size);
  }
}
      
/* implementation of a basic simulated annealing algorithm */

void 
gsl_siman_solve (const gsl_rng * r, void *x0_p, gsl_siman_Efunc_t Ef,  // Ef是待求函式,x0_p是起始點,r是隨機數生成器
                 gsl_siman_step_t take_step,  // 用於隨機走一步
                 gsl_siman_metric_t distance,
                 gsl_siman_print_t print_position,  // 用於將中間結果打印出來
                 gsl_siman_copy_t copyfunc,  // 用於實現變步長的模擬退火,將一個點的值複製給另一個點
                 gsl_siman_copy_construct_t copy_constructor,  // 用於變步長的模擬退火,生成一個新座標點
                 gsl_siman_destroy_t destructor,  // 回收座標點記憶體
                 size_t element_size,
                 gsl_siman_params_t params)
{
  void *x, *new_x, *best_x;
  double E, new_E, best_E;
  int i;
  double T, T_factor;
  int n_evals = 1, n_iter = 0, n_accepts, n_rejects, n_eless;

  /* this function requires that either the dynamic functions (copy,
     copy_constructor and destrcutor) are passed, or that an element
     size is given */
  assert((copyfunc != NULL && copy_constructor != NULL && destructor != NULL)
         || (element_size != 0));

  distance = 0 ; /* This parameter is not currently used */
  E = Ef(x0_p);

  if (copyfunc) {
    x = copy_constructor(x0_p);
    new_x = copy_constructor(x0_p);
    best_x = copy_constructor(x0_p);
  } else {
    x = (void *) malloc (element_size);
    memcpy (x, x0_p, element_size);
    new_x = (void *) malloc (element_size);
    best_x =  (void *) malloc (element_size);
    memcpy (best_x, x0_p, element_size);
  }

  best_E = E;

  T = params.t_initial;
  T_factor = 1.0 / params.mu_t;

  if (print_position) {
    printf ("#-iter  #-evals   temperature     position   energy\n");
  }

  while (1) {

    n_accepts = 0;
    n_rejects = 0;
    n_eless = 0;

    for (i = 0; i < params.iters_fixed_T; ++i) {

      copy_state(x, new_x, element_size, copyfunc);

      take_step (r, new_x, params.step_size);
      new_E = Ef (new_x);

      if(new_E <= best_E){
        if (copyfunc) {
          copyfunc(new_x,best_x);
        } else {
          memcpy (best_x, new_x, element_size);
        }
        best_E=new_E;
      }

      ++n_evals;                /* keep track of Ef() evaluations */
      /* now take the crucial step: see if the new point is accepted
         or not, as determined by the boltzmann probability */
      if (new_E < E) {

	if (new_E < best_E) {
	  copy_state(new_x, best_x, element_size, copyfunc);
	  best_E = new_E;
	}

        /* yay! take a step */
	copy_state(new_x, x, element_size, copyfunc);
        E = new_E;
        ++n_eless;

      } else if (gsl_rng_uniform(r) < boltzmann(E, new_E, T, &params)) {
        /* yay! take a step */
	copy_state(new_x, x, element_size, copyfunc);
        E = new_E;
        ++n_accepts;

      } else {
        ++n_rejects;
      }
    }

    if (print_position) {
      /* see if we need to print stuff as we go */
      /*       printf("%5d %12g %5d %3d %3d %3d", n_iter, T, n_evals, */
      /*           100*n_eless/n_steps, 100*n_accepts/n_steps, */
      /*           100*n_rejects/n_steps); */
      printf ("%5d   %7d  %12g", n_iter, n_evals, T);
      print_position (x);
      printf ("  %12g  %12g\n", E, best_E);
    }

    /* apply the cooling schedule to the temperature */
    /* FIXME: I should also introduce a cooling schedule for the iters */
    T *= T_factor;
    ++n_iter;
    if (T < params.t_min) {
      break;
    }
  }

  /* at the end, copy the result onto the initial point, so we pass it
     back to the caller */
  copy_state(best_x, x0_p, element_size, copyfunc);

  if (copyfunc) {
    destructor(x);
    destructor(new_x);
    destructor(best_x);
  } else {
    free (x);
    free (new_x);
    free (best_x);
  }
}

/* implementation of a simulated annealing algorithm with many tries */
/// 模擬退火方法的一個變種
void 
gsl_siman_solve_many (const gsl_rng * r, void *x0_p, gsl_siman_Efunc_t Ef,
                      gsl_siman_step_t take_step,
                      gsl_siman_metric_t distance,
                      gsl_siman_print_t print_position,
                      size_t element_size,
                      gsl_siman_params_t params)
{
  /* the new set of trial points, and their energies and probabilities */
  void *x, *new_x;
  double *energies, *probs, *sum_probs;
  double Ex;                    /* energy of the chosen point */
  double T, T_factor;           /* the temperature and a step multiplier */
  int i;
  double u;                     /* throw the die to choose a new "x" */
  int n_iter;

  if (print_position) {
    printf ("#-iter    temperature       position");
    printf ("         delta_pos        energy\n");
  }

  x = (void *) malloc (params.n_tries * element_size);
  new_x = (void *) malloc (params.n_tries * element_size);
  energies = (double *) malloc (params.n_tries * sizeof (double));
  probs = (double *) malloc (params.n_tries * sizeof (double));
  sum_probs = (double *) malloc (params.n_tries * sizeof (double));

  T = params.t_initial;
  T_factor = 1.0 / params.mu_t;

  memcpy (x, x0_p, element_size);

  n_iter = 0;
  while (1)
    {
      Ex = Ef (x);
      for (i = 0; i < params.n_tries - 1; ++i)
        {                       /* only go to N_TRIES-2 */
          /* center the new_x[] around x, then pass it to take_step() */
          sum_probs[i] = 0;
          memcpy ((char *)new_x + i * element_size, x, element_size);
          take_step (r, (char *)new_x + i * element_size, params.step_size);
          energies[i] = Ef ((char *)new_x + i * element_size);
          probs[i] = boltzmann(Ex, energies[i], T, &params);
        }
      /* now add in the old value of "x", so it is a contendor */
      memcpy ((char *)new_x + (params.n_tries - 1) * element_size, x, element_size);
      energies[params.n_tries - 1] = Ex;
      probs[params.n_tries - 1] = boltzmann(Ex, energies[i], T, &params);

      /* now throw biased die to see which new_x[i] we choose */
      sum_probs[0] = probs[0];
      for (i = 1; i < params.n_tries; ++i)
        {
          sum_probs[i] = sum_probs[i - 1] + probs[i];
        }
      u = gsl_rng_uniform (r) * sum_probs[params.n_tries - 1];
      for (i = 0; i < params.n_tries; ++i)
        {
          if (u < sum_probs[i])
            {
              memcpy (x, (char *) new_x + i * element_size, element_size);
              break;
            }
        }
      if (print_position)
        {
          printf ("%5d\t%12g\t", n_iter, T);
          print_position (x);
          printf ("\t%12g\t%12g\n", distance (x, x0_p), Ex);
        }
      T *= T_factor;
      ++n_iter;
      if (T < params.t_min)
	{
	  break;
        }
    }

  /* now return the value via x0_p */
  memcpy (x0_p, x, element_size);

  /*  printf("the result is: %g (E=%g)\n", x, Ex); */

  free (x);
  free (new_x);
  free (energies);
  free (probs);
  free (sum_probs);
}

 

下面是一個測試程式碼,其中重寫了部分GSL中的mt19937隨機數生成器

#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "siman.h"
#include <stdio.h>

/****************************************************
以下為隨機數生成器部分
*****************************************************/
#define N 624
#define M 397

static const unsigned long UPPER_MASK = 0x80000000UL;
static const unsigned long LOWER_MASK = 0x7fffffffUL;

typedef struct
{
	unsigned long mt[N];
	int mti;
}
mt_state_t;

void mt_set(void *vstate, unsigned long int s)
{
	mt_state_t *state = (mt_state_t *)vstate;
	int i;
	if (s == 0) s = 4357;
	state->mt[0] = s & 0xffffffffUL;
	for (i = 1; i < N; i++)
	{
		state->mt[i] = (1812433253UL * (state->mt[i - 1] ^ (state->mt[i - 1] >> 30)) + i);
		state->mt[i] &= 0xffffffffUL;
	}
	state->mti = i;
}

inline unsigned long mt_get(void *vstate)
{
	mt_state_t *state = (mt_state_t *)vstate;
	unsigned long k;
	unsigned long int *const mt = state->mt;
#define MAGIC(y) (((y)&0x1) ? 0x9908b0dfUL : 0)
	if (state->mti >= N)
	{
		int kk;
		for (kk = 0; kk < N - M; kk++)
		{
			unsigned long y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
			mt[kk] = mt[kk + M] ^ (y >> 1) ^ MAGIC(y);
		}
		for (; kk < N - 1; kk++)
		{
			unsigned long y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
			mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ MAGIC(y);
		}
		unsigned long y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
		mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ MAGIC(y);
		state->mti = 0;
	}
	k = mt[state->mti];
	k ^= (k >> 11);
	k ^= (k << 7) & 0x9d2c5680UL;
	k ^= (k << 15) & 0xefc60000UL;
	k ^= (k >> 18);
	state->mti++;
	return k;
}

double mt_get_double(void *vstate)
{
	return mt_get(vstate) / 4294967296.0;
}

const gsl_rng_type mt_type =
{
	"mt19937",
	0xffffffffUL,
	0,
	sizeof(mt_state_t),
	&mt_set,
	&mt_get,
	&mt_get_double
};

/******************************************************
以下為模擬退火演算法的測試部分
*******************************************************/
#define N_TRIES 500
#define ITERS_FIXED_T 2000
#define STEP_SIZE 0.02
#define K 1.0
#define T_INITIAL 1000
#define MU_T 1.006
#define T_MIN 1.0e-3

gsl_siman_params_t params
= {N_TRIES, ITERS_FIXED_T, STEP_SIZE,
   K, T_INITIAL, MU_T, T_MIN};

double E1(void *xp)
{
    double x = *((double *)xp);

    return exp(-pow((x-1.0),2.0))*sin(8*x);
}

double E2(void *xp)
{
    double x = *((double *)xp);
    double y = *((double *)xp+1);

    double z = 3*pow(1-x,2)*exp(-pow(x,2)-pow(y+1,2))
           -10*(x/5-pow(x,3)-pow(y,5))*exp(-pow(x,2)-pow(y,2))
           -exp(-pow(x+1,2)-pow(y,2))/3;
    return -z;
}

double M1(void *xp, void *yp)
{
    double x = *((double *)xp);
    double y = *((double *)yp);

    return fabs(x-y);
}

double M2(void *xp, void *yp)
{
    double x0 = *((double *)xp);
    double y0 = *((double *)xp+1);
    double x1 = *((double *)yp);
    double y1 = *((double *)yp+1);

    return sqrt(pow(x1-x0,2)+pow(y1-y0,2));
}

void S1(const gsl_rng *r, void *xp, double step_size)
{
    double old_x = *((double *)xp);
    double new_x;

    double u = gsl_rng_uniform(r);
    new_x = u*2*step_size - step_size + old_x;

    memcpy(xp, &new_x, sizeof(new_x));
}

void S2(const gsl_rng *r, void *xp, double step_size)
{
    double *old_x = (double *)xp;
    double *new_x = new double[2];

    double u = gsl_rng_uniform(r);
    double v = gsl_rng_uniform(r);
    new_x[0] = u*2*step_size - step_size + old_x[0];
    new_x[1] = v*2*step_size - step_size + old_x[1];

    memcpy(xp, new_x, sizeof(double)*2);
}

void P1(void *xp)
{
    printf("%12g", *((double *)xp));
}

void P2(void *xp)
{
    printf("%12g,%12g", *((double *)xp), *((double *)xp+1));
}

int main(int argc, char *argv[])
{
    const gsl_rng_type *T;
    gsl_rng *r;

    double x_initial1 = 15.5;
    double x_initial2[2] = {1.5, 1.5};

    gsl_rng_env_setup(&mt_type, 10);

    T = gsl_rng_default;
    r = gsl_rng_alloc(T);

	/// 最小值應為 xmin = 1.36313, ymin = -0.872871
    gsl_siman_solve(r, &x_initial1, E1, S1, M1, P1,
                    NULL, NULL, NULL,
                    sizeof(double), params);
	getchar();
	/// 最小值應為 xmin = (-0.0093, 1.5814), ymin = -8.1062
    gsl_siman_solve_many(r, x_initial2, E2, S2, M2, P2, 
                    sizeof(double)*2, params);

    gsl_rng_free(r);
	system("pause");
    return 0;
}

 

下面是模擬退火演算法的經典問題,Travelling Salesman Problem的原始碼,此處改寫了隨機數生成器,採用c標準庫函式srand和rand來產生隨機數。

#include <math.h>
#include <string.h>
#include <stdio.h>
#include "siman.h"

#define M_PI 3.14159265358979323846264338328

void set(void *vstate, unsigned long int s)
{
	srand(s);
}

__inline unsigned long get(void *vstate)
{
	return rand();
}

double get_double(void *vstate)
{
	return get(vstate) / ((double)RAND_MAX);
}

const gsl_rng_type mt_type =
{
	"default",
	RAND_MAX,
	0,
	0,
	&set,
	&get,
	&get_double
};

/* set up parameters for this simulated annealing run */

#define N_TRIES 200             /* how many points do we try before stepping */
#define ITERS_FIXED_T 2000      /* how many iterations for each T? */
#define STEP_SIZE 1.0           /* max step size in random walk */
#define K 1.0                   /* Boltzmann constant */
#define T_INITIAL 5000.0        /* initial temperature */
#define MU_T 1.002              /* damping factor for temperature */
#define T_MIN 5.0e-1

gsl_siman_params_t params = {N_TRIES, ITERS_FIXED_T, STEP_SIZE,
                             K, T_INITIAL, MU_T, T_MIN};

struct s_tsp_city {
  const char * name;
  double lat, longitude;        /* coordinates */
};
typedef struct s_tsp_city Stsp_city;

void prepare_distance_matrix(void);
void exhaustive_search(void);
void print_distance_matrix(void);
double city_distance(Stsp_city c1, Stsp_city c2);
double Etsp(void *xp);
double Mtsp(void *xp, void *yp);
void Stsp(const gsl_rng * r, void *xp, double step_size);
void Ptsp(void *xp);

/* in this table, latitude and longitude are obtained from the US
   Census Bureau, at http://www.census.gov/cgi-bin/gazetteer */

Stsp_city cities[] = {{"Santa Fe",    35.68,   105.95},
                      {"Phoenix",     33.54,   112.07},
                      {"Albuquerque", 35.12,   106.62},
                      {"Clovis",      34.41,   103.20},
                      {"Durango",     37.29,   107.87},
                      {"Dallas",      32.79,    96.77},
                      {"Tesuque",     35.77,   105.92},
                      {"Grants",      35.15,   107.84},
                      {"Los Alamos",  35.89,   106.28},
                      {"Las Cruces",  32.34,   106.76},
                      {"Cortez",      37.35,   108.58},
                      {"Gallup",      35.52,   108.74}};

#define N_CITIES (sizeof(cities)/sizeof(Stsp_city))

double distance_matrix[N_CITIES][N_CITIES];

/* distance between two cities */
double city_distance(Stsp_city c1, Stsp_city c2)
{
  const double earth_radius = 6375.000; /* 6000KM approximately */
  /* sin and cos of lat and long; must convert to radians */
  double sla1 = sin(c1.lat*M_PI/180), cla1 = cos(c1.lat*M_PI/180),
    slo1 = sin(c1.longitude*M_PI/180), clo1 = cos(c1.longitude*M_PI/180);
  double sla2 = sin(c2.lat*M_PI/180), cla2 = cos(c2.lat*M_PI/180),
    slo2 = sin(c2.longitude*M_PI/180), clo2 = cos(c2.longitude*M_PI/180);

  double x1 = cla1*clo1;
  double x2 = cla2*clo2;

  double y1 = cla1*slo1;
  double y2 = cla2*slo2;

  double z1 = sla1;
  double z2 = sla2;

  double dot_product = x1*x2 + y1*y2 + z1*z2;

  double angle = acos(dot_product);

  /* distance is the angle (in radians) times the earth radius */
  return angle*earth_radius;
}

/* energy for the travelling salesman problem */
double Etsp(void *xp)
{
  /* an array of N_CITIES integers describing the order */
  int *route = (int *) xp;
  double E = 0;
  unsigned int i;

  for (i = 0; i < N_CITIES; ++i) {
    /* use the distance_matrix to optimize this calculation; it had
       better be allocated!! */
    E += distance_matrix[route[i]][route[(i + 1) % N_CITIES]];
  }

  return E;
}

double Mtsp(void *xp, void *yp)
{
  int *route1 = (int *) xp, *route2 = (int *) yp;
  double distance = 0;
  unsigned int i;

  for (i = 0; i < N_CITIES; ++i) {
    distance += ((route1[i] == route2[i]) ? 0 : 1);
  }

  return distance;
}

/* take a step through the TSP space */
void Stsp(const gsl_rng * r, void *xp, double step_size)
{
  int x1, x2, dummy;
  int *route = (int *) xp;

  step_size = 0 ; /* prevent warnings about unused parameter */

  /* pick the two cities to swap in the matrix; we leave the first
     city fixed */
  x1 = (gsl_rng_get (r) % (N_CITIES-1)) + 1;
  do {
    x2 = (gsl_rng_get (r) % (N_CITIES-1)) + 1;
  } while (x2 == x1);

  dummy = route[x1];
  route[x1] = route[x2];
  route[x2] = dummy;
}

void Ptsp(void *xp)
{
  unsigned int i;
  int *route = (int *) xp;
  printf("  [");
  for (i = 0; i < N_CITIES; ++i) {
    printf(" %d ", route[i]);
  }
  printf("]  ");
}

int main(void)
{
  int x_initial[N_CITIES];
  unsigned int i;

  const gsl_rng * r = gsl_rng_alloc (gsl_rng_env_setup(&mt_type, 0)) ;

//  gsl_ieee_env_setup ();

  prepare_distance_matrix();

  /* set up a trivial initial route */
  printf("# initial order of cities:\n");
  for (i = 0; i < N_CITIES; ++i) {
    printf("# \"%s\"\n", cities[i].name);
    x_initial[i] = i;
  }

  printf("# distance matrix is:\n");
  print_distance_matrix();

  printf("# initial coordinates of cities (longitude and latitude)\n");
  /* this can be plotted with */
  /* ./siman_tsp > hhh ; grep city_coord hhh | awk '{print $2 "   " $3}' | xyplot -ps -d "xy" > c.eps */
  for (i = 0; i < N_CITIES+1; ++i) {
    printf("###initial_city_coord: %g %g \"%s\"\n",
           -cities[x_initial[i % N_CITIES]].longitude,
           cities[x_initial[i % N_CITIES]].lat,
           cities[x_initial[i % N_CITIES]].name);
  }

/*   exhaustive_search(); */

  gsl_siman_solve(r, x_initial, Etsp, Stsp, Mtsp, Ptsp, NULL, NULL, NULL,
                  N_CITIES*sizeof(int), params);

  printf("# final order of cities:\n");
  for (i = 0; i < N_CITIES; ++i) {
    printf("# \"%s\"\n", cities[x_initial[i]].name);
  }

  printf("# final coordinates of cities (longitude and latitude)\n");
  /* this can be plotted with */
  /* ./siman_tsp > hhh ; grep city_coord hhh | awk '{print $2 "   " $3}' | xyplot -ps -d "xy" > c.eps */
  for (i = 0; i < N_CITIES+1; ++i) {
    printf("###final_city_coord: %g %g %s\n",
           -cities[x_initial[i % N_CITIES]].longitude,
           cities[x_initial[i % N_CITIES]].lat,
           cities[x_initial[i % N_CITIES]].name);
  }

  printf("# ");
  fflush(stdout);
#if 0
  system("date");
#endif /* 0 */
  fflush(stdout);
  system("pause");
  return 0;
}

void prepare_distance_matrix()
{
  unsigned int i, j;
  double dist;

  for (i = 0; i < N_CITIES; ++i) {
    for (j = 0; j < N_CITIES; ++j) {
      if (i == j) {
        dist = 0;
      } else {
        dist = city_distance(cities[i], cities[j]);
      }
      distance_matrix[i][j] = dist;
    }
  }
}

void print_distance_matrix()
{
  unsigned int i, j;

  for (i = 0; i < N_CITIES; ++i) {
    printf("# ");
    for (j = 0; j < N_CITIES; ++j) {
      printf("%15.8f   ", distance_matrix[i][j]);
    }
    printf("\n");
  }
}

/* [only works for 12] search the entire space for solutions */
static double best_E = 1.0e100, second_E = 1.0e100, third_E = 1.0e100;
static int best_route[N_CITIES];
static int second_route[N_CITIES];
static int third_route[N_CITIES];
static void do_all_perms(int *route, int n);

void exhaustive_search()
{
  static int initial_route[N_CITIES] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
  printf("\n# ");
  fflush(stdout);
#if 0
  system("date");
#endif
  fflush(stdout);
  do_all_perms(initial_route, 1);
  printf("\n# ");
  fflush(stdout);
#if 0
  system("date");
#endif /* 0 */
  fflush(stdout);

  printf("# exhaustive best route: ");
  Ptsp(best_route);
  printf("\n# its energy is: %g\n", best_E);

  printf("# exhaustive second_best route: ");
  Ptsp(second_route);
  printf("\n# its energy is: %g\n", second_E);

  printf("# exhaustive third_best route: ");
  Ptsp(third_route);
  printf("\n# its energy is: %g\n", third_E);
}

/* James Theiler's recursive algorithm for generating all routes */
static void do_all_perms(int *route, int n)
{
  if (n == (N_CITIES-1)) {
    /* do it! calculate the energy/cost for that route */
    double E;
    E = Etsp(route);            /* TSP energy function */
    /* now save the best 3 energies and routes */
    if (E < best_E) {
      third_E = second_E;
      memcpy(third_route, second_route, N_CITIES*sizeof(*route));
      second_E = best_E;
      memcpy(second_route, best_route, N_CITIES*sizeof(*route));
      best_E = E;
      memcpy(best_route, route, N_CITIES*sizeof(*route));
    } else if (E < second_E) {
      third_E = second_E;
      memcpy(third_route, second_route, N_CITIES*sizeof(*route));
      second_E = E;
      memcpy(second_route, route, N_CITIES*sizeof(*route));
    } else if (E < third_E) {
      third_E = E;
      memcpy(route, third_route, N_CITIES*sizeof(*route));
    }
  } else {
    int new_route[N_CITIES];
    unsigned int j;
    int swap_tmp;
    memcpy(new_route, route, N_CITIES*sizeof(*route));
    for (j = n; j < N_CITIES; ++j) {
      swap_tmp = new_route[j];
      new_route[j] = new_route[n];
      new_route[n] = swap_tmp;
      do_all_perms(new_route, n+1);
    }
  }
}

 

完整的VS2013工程可從如下網址下載:

https://download.csdn.net/download/u014559935/10828563