巡回セールスマン問題


関数名
tsp  巡回セールスマン問題(n個の市1回ずつ通ってもとの市
     に戻るための最短パスを求める問題)
形式
int tsp(int *spath, int *xcity, int *ycity, int ncity);
引数
spath  (出力)最短パスが入っている配列
xcity  (入力)n個の市のx座標
ycity  (入力)n個の市のy座標
ncity  (入力)市の数n
関数値
最短パスに沿って通るマンハッタン距離(マンハッタン距離とは、 (x1, y1)(x2, y2) の2点の距離は |x1-x2|+|y1-y2| で表す距離)

注意事項

用例
用例プログラム(tsp-test.c

使い方ですが、まずデータファイルを用意しておく。市の数をデータ ファイルの1行目に書く。2行目からは1行ごとに各市の x、y 座標を書く。 たとえば、以下のように、
4
2 4
6 8
1 4
3 6
でデータファイルを書く。つぎに、用例プログラムは
tsp-test データファイル
で実行すれば、最短距離を出すパスが表示される。最短パスがいくつも ある場合は、その1例の表示となる。

プログラム(tsp.c
#define MAX_CITY 100

int tsp(int *spath, int *xcity, int *ycity, int ncity)
{
    int slen = -1;
    int tpath[MAX_CITY];
    static int guess();
    static void evaluate();

    while (1) {
        if (guess(tpath, slen, xcity, ycity, ncity) == 1)
            break;
        evaluate(&slen, spath, tpath, xcity, ycity, ncity);
    }
    return slen;
}

static int guess(int *tpath, int slen, int *xcity, int *ycity, int ncity)
{
    int i, j, k;
    int	tmp[MAX_CITY];

    if (slen < 0) {
        for (i = 0; i < ncity; i++) tpath[i] = i;
        return 0;
    }

    for (i = ncity - 2; i >= 1; i--) {
        for (j = 0; j < ncity; j++) tmp[j] = j;
        for (j = 0; j <= i; j++) tmp[tpath[j]] = MAX_CITY;
        for (j = 0; j < ncity; j++)
            if (tmp[j] != MAX_CITY)
                if (tmp[j] > tpath[i]) {
                    tpath[i] = tmp[j];
                    goto NEXT;
                }
    }
    return 1;

NEXT:
    for (j = i+1; j < ncity; j++) {
        for (k = 0; k < ncity; k++) tmp[k] = k;
        for (k = 0; k < j; k++) tmp[tpath[k]] = MAX_CITY;
        for (k = 0; k < ncity; k++)
            if (tmp[k] != MAX_CITY) {
                tpath[j] = tmp[k];
                break;
            }
    }
    return 0;
}

static void evaluate(int *slen, int *spath, int *tpath,int *xcity, int *ycity, int ncity)
{
    int i;
    int dx, dy;
    int tlen;

    dx = xcity[tpath[0]] - xcity[tpath[ncity-1]];
    dy = ycity[tpath[0]] - ycity[tpath[ncity-1]];
    if (dx < 0) dx = -dx;
    if (dy < 0) dy = -dy;
    tlen = dx + dy;
    for (i = 1; i < ncity; i++) {
        dx = xcity[tpath[i]] - xcity[tpath[i-1]];
        dy = ycity[tpath[i]] - ycity[tpath[i-1]];
        if (dx < 0) dx = -dx;
        if (dy < 0) dy = -dy;
        tlen += dx + dy;
    }

    if (*slen < 0 || tlen < *slen) {
        for (i = 0; i < ncity; i++) spath[i] = tpath[i];
        *slen = tlen;
    }
}
説明
本プログラムは力まかせの方法を取っていて、あらゆるパスの組合せ を1つ1つ作り出し、そのパスの距離を計算し、その中から最短距離を算出 している。したがって、市の数が10を超えると、計算時間がものすごくか かってしまう。

何百市を超えると、世界の全てのコンピュータを動員して、宇宙誕生の瞬 間からの何百億年を計算させても終わらない計算時間である。コンピュー タはなんと無力なものであろう。その創造主の人間についても同じことが いえるかも知れない。

巡回セールスマン問題はいわゆるNP完全問題の1つで、多項式時間の解法 は存在しないといわれている。本当に存在するかしまいか、証明できれば、 あなたはチューリング賞(コンピュータ科学分野でのノーベル賞)をもら えるはずである。

関連関数