当前位置:   article > 正文

旅行商问题_旅行商问题,问题重述

旅行商问题,问题重述

旅行商问题

1 问题描述
何为旅行商问题?按照非专业的说法,这个问题要求找出一条n个给定的城市间的最短路径,使我们在回到触发的城市之前,对每个城市都只访问一次。这样该问题就可以表述为求一个图的最短哈密顿回路的问题。(哈密顿回路:定义为一个对图的每个顶点都只穿越一次的回路)

很容易看出来,哈密顿回路也可以定义为n+1个相邻顶点v1,v2,v3,…,vn,v1的一个序列。其中,序列的第一个顶点和最后一个顶点是相同的,而其它n-1个顶点都是互不相同的。并且,在不失一般性的前提下,可以假设,所有的回路都开始和结束于相同的特定顶点。因此,可以通过生成n-1个中间城市的组合来得到所有的旅行线路,计算这些线路的长度,然后求取最短的线路。下图是该问题的一个小规模实例,并用该方法得到了它的解,具体如下:
这里写图片描述

2.蛮力法
此处使用蛮力法(即使用全排列再求最小值)解决旅行商问题,取的是4个城市规模,并已经定义好各个城市之间的距离(PS:该距离使用二维数组初始化定义,此处的距离是根据图1中所示距离定义)。

public class TravelingSalesman {

    public int count = 0;     //定义全局变量,用于计算当前已行走方案次数,初始化为0
    public int MinDistance = 100;    //定义完成一个行走方案的最短距离,初始化为100(PS:100此处表示比实际要大很多的距离)
    public int[][] distance = {{0,2,5,7},{2,0,8,3},{5,8,0,1},{7,3,1,0}};   //使用二维数组的那个音图的路径相关距离长度
    /*
     * start为开始进行排序的位置
     * step为当前正在行走的位置
     * n为需要排序的总位置数
     * Max为n!值
     */
    public void Arrange(int[] A,int start,int step,int n,int Max){
        if(step == n){   // 当正在行走的位置等于城市总个数时
            ++count;           //每完成一次行走方案,count自增1
            printArray(A);     //输出行走路线方案及其总距离
        }
        if(count == Max)
            System.out.println("已完成全部行走方案!!!,最短路径距离为:"+MinDistance);
        else{
            for(int i = start;i < n;i++){
                /*第i个数分别与它后面的数字交换就能得到新的排列,从而能够得到n!次不同排序方案
                swapArray(A,start,i);
                Arrange(A,start+1,step+1,n,Max);
                swapArray(A,i,start);
            }
        }
    }

    //交换数组中两个位置上的数值
    public  void swapArray(int[] A,int p,int q){
        int temp = A[p];
        A[p] = A[q];
        A[q] = temp;
    }

    //输出数组A的序列,并输出当前行走序列所花距离,并得到已完成的行走方案中最短距离
    public void printArray(int[] A){
        for(int i = 0;i < A.length;i++)   //输出当前行走方案的序列
            System.out.print(A[i]+"  ");

        int tempDistance = distance[A[0]][A[3]];     //此处是因为,最终要返回出发地城市,所以总距离要加上最后到达的城市到出发点城市的距离
        for(int i = 0;i < (A.length-1);i++)   //输出当前行走方案所花距离
            tempDistance += distance[A[i]][A[i+1]];

        if(MinDistance > tempDistance)   //返回当前已完成方案的最短行走距离
            MinDistance = tempDistance;

        System.out.println("  行走路程总和:"+tempDistance);
    }

    public static void main(String[] args){
        int[] A = {0,1,2,3};
        TravelingSalesman test = new TravelingSalesman();
        test.Arrange(A,0,0,4,24);    //此处Max = 4!=24
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

运行结果

0 1 2 3 行走路程总和:18
0 1 3 2 行走路程总和:11
0 2 1 3 行走路程总和:23
0 2 3 1 行走路程总和:11
0 3 2 1 行走路程总和:18
0 3 1 2 行走路程总和:23
1 0 2 3 行走路程总和:11
1 0 3 2 行走路程总和:18
1 2 0 3 行走路程总和:23
1 2 3 0 行走路程总和:18
1 3 2 0 行走路程总和:11
1 3 0 2 行走路程总和:23
2 1 0 3 行走路程总和:18
2 1 3 0 行走路程总和:23
2 0 1 3 行走路程总和:11
2 0 3 1 行走路程总和:23
2 3 0 1 行走路程总和:18
2 3 1 0 行走路程总和:11
3 1 2 0 行走路程总和:23
3 1 0 2 行走路程总和:11
3 2 1 0 行走路程总和:18
3 2 0 1 行走路程总和:11
3 0 2 1 行走路程总和:23
3 0 1 2 行走路程总和:18
已完成全部行走方案!!!,最短路径距离为:11

3.减治法
旅行商问题的核心,就是求n个不同城市的全排列,通俗一点的话,就是求1~n的全排列。下面两种方法都是基于减治思想进行的,此处只实现求取1~n的全排列。对于每一种排列,在旅行商问题中还得求取其相应路径长度,最后,在进行比较从而得到最短路径,对于求取最短路径的思想在蛮力法中已经体现,此处不在重复,感兴趣的同学可以自己再动手实现一下~

3.2.1 Johson-Trotter算法

此处算法思想借用《算法设计与分析基础》第三版上讲解,具体如下:
这里写图片描述
代码如下

public class Arrange {
    //使用JohnsonTrotter算法获取1~n的全排列
    public HashMap<Integer , String> getJohnsonTrotter(int n){
        HashMap<Integer , String> hashMap = new HashMap<Integer , String>();
        int count = 0;                //用于计算生成排列的总个数,初始化为0
        int[] arrayN = new int[n];
        int[] directionN = new int[n+1];      //directionN[i]用于标记1~n中数字i上的箭头方向,初始化值为0,表示箭头方向向左;值为1 表示箭头方向向右
        for(int i = 0;i < n;i++)
            arrayN[i] = i+1;
        String result = getArrayString(arrayN);
        hashMap.put(count, result);        //将原始排列添加到哈希表中
        while(judgeMove(arrayN,directionN)){      //存在一个移动元素
            int maxI = getMaxMove(arrayN,directionN);
            if(directionN[arrayN[maxI]] == 0)      //箭头指向左方
                swap(arrayN,maxI,--maxI);
            if(directionN[arrayN[maxI]] == 1)       //箭头指向右方
                swap(arrayN,maxI,++maxI);
            for(int i = 0;i < n;i++){               //调转所有大于arrayN[maxI]的数的箭头方向
                if(arrayN[i] > arrayN[maxI]){
                    if(directionN[arrayN[i]] == 0)
                         directionN[arrayN[i]] = 1;
                    else
                        directionN[arrayN[i]] = 0;
                }
            }
            count++;
            result = getArrayString(arrayN);
            hashMap.put(count, result);        //将得到的新排列添加到哈希表中
        }
        return hashMap;
    }
    //判断数组arrayN中是否存在可移动元素
    public boolean judgeMove(int[] arrayN,int[] directionN){
        boolean judge = false;
        for(int i = arrayN.length - 1;i >= 0;i--){
            if(directionN[arrayN[i]] == 0 && i != 0){     //当arrayN[i]数字上的箭头方向指向左边时
                if(arrayN[i] > arrayN[i-1])
                    return true;
            }
            if(directionN[arrayN[i]] == 1 && i != (arrayN.length-1)){    //当arrayN[i]数字上的箭头方向指向右边时
                if(arrayN[i] > arrayN[i+1])
                    return true;
            }
        }
        return judge;
    }
    //获取数组arrayN中最大的可移动元素的数组下标
    public int getMaxMove(int[] arrayN,int[] directionN){
        int result = 0;
        int temp = 0;
        for(int i = 0;i < arrayN.length;i++){
            if(directionN[arrayN[i]] == 0 && i != 0){     //当arrayN[i]数字上的箭头方向指向左边时
                if(arrayN[i] > arrayN[i-1]){
                    int max = arrayN[i];
                    if(max > temp)
                        temp = max;
                }
            }
            if(directionN[arrayN[i]] == 1 && i != (arrayN.length-1)){    //当arrayN[i]数字上的箭头方向指向右边时
                if(arrayN[i] > arrayN[i+1]){
                    int max = arrayN[i];
                    if(max > temp)
                        temp = max;
                }    
            }
        }
        for(int i = 0;i < arrayN.length;i++){
            if(arrayN[i] == temp)
                return i;
        }
        return result;
    }
    //交换数组中两个位置上的数值
    public void swap(int[] array,int m,int n){
        int temp = array[m];
        array[m] = array[n];
        array[n] = temp;
    }
    //把数组array中所有元素按照顺序以字符串结果返回
    public String getArrayString(int[] array){
        String result = "";
        for(int i = 0;i < array.length;i++)
            result = result + array[i];
        return result;
    }

    public static void main(String[] args){
        Arrange test = new Arrange();
        HashMap<Integer , String> hashMap = test.getJohnsonTrotter(3);
        Collection<String> c1 = hashMap.values();
        Iterator<String> ite = c1.iterator();
        while(ite.hasNext())
            System.out.println(ite.next());
        System.out.println(hashMap);

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97

运行结果

123
132
312
321
231
213
{0=123, 1=132, 2=312, 3=321, 4=231, 5=213}

3.基于字典序的算法

此处算法思想也借用《算法设计与分析基础》第三版上讲解,具体如下:
这里写图片描述
代码

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;

public class Arrange1 {

    public HashMap<Integer,String> getLexicographicPermute(int n){
        HashMap<Integer,String> hashMap = new HashMap<Integer,String>();
        int count = 0;         //用于计算生成排列的总个数,初始化为0
        int[] arrayN = new int[n];
        for(int i = 0;i < n;i++)
            arrayN[i] = i+1;
        String result = getArrayString(arrayN);
        hashMap.put(count, result);        //将原始排列添加到哈希表中
        while(riseTogetherArray(arrayN)){     //数组中存在两个连续的升序元素
            int i = getMaxI(arrayN);     //找出使得ai<ai+1的最大i: ai+1>ai+2>...>an
            int j = getMaxJ(arrayN);     //找到使得ai<aj的最大索引j: j>=i,因为ai<ai+1
            swap(arrayN,i,j);
            reverseArray(arrayN,i+1,arrayN.length-1);
            result = getArrayString(arrayN);
            count++;
            hashMap.put(count, result);        //将新得到的排列添加到哈希表中
        }
        System.out.println("排列总个数count = "+(count+1));
        return hashMap;
    }
    //判断数组中是否 包含两个连续的升序元素
    public boolean riseTogetherArray(int[] arrayN){
        boolean result = false;
        for(int i = 1;i < arrayN.length;i++){
            if(arrayN[i-1] < arrayN[i])
                return true;
        }
        return result;
    }
    //返回i:满足ai<ai+1,ai+1>ai+2>...>an(PS:an为数组中最后一个元素)
    public int getMaxI(int[] arrayN){
        int result = 0;
        for(int i = arrayN.length-1;i > 0;){
            if(arrayN[i-1] > arrayN[i])
                i--;
            else
                return i-1;
        }
        return result;
    }
    //返回j:ai<aj的最大索引,j>=i+1,因为ai<ai+1(此处i值为上面函数getMaxI得到值)
    public int getMaxJ(int[] arrayN){
        int result = 0;
        int tempI = getMaxI(arrayN);
        for(int j = tempI+1;j < arrayN.length;){
            if(arrayN[tempI] < arrayN[j]){
                if(j == arrayN.length-1)
                    return j;
                j++;
            }
            else
                return j-1;
        }
        return result;
    }
    //交换数组中两个位置上的数值
    public void swap(int[] array,int m,int n){
        int temp = array[m];
        array[m] = array[n];
        array[n] = temp;
    }
    //将数组中a[m]到a[n]一段元素反序排列
    public void reverseArray(int[] arrayN,int m,int n){
        while(m < n){
            int temp = arrayN[m];
            arrayN[m++] = arrayN[n];
            arrayN[n--] = temp;
        }
    }
    //把数组array中所有元素按照顺序以字符串结果返回
    public String getArrayString(int[] array){
        String result = "";
        for(int i = 0;i < array.length;i++)
            result = result + array[i];
        return result;
    }

    public static void main(String[] args){
         Arrange1 test = new  Arrange1();
         HashMap<Integer,String> hashMap = test.getLexicographicPermute(3);
         Collection<String> c1 = hashMap.values();
         Iterator<String> ite = c1.iterator();
         while(ite.hasNext())
            System.out.println(ite.next());
         System.out.println(hashMap);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93

运行结果

排列总个数count = 6
123
132
213
231
312
321
{0=123, 1=132, 2=213, 3=231, 4=312, 5=321}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/696091
推荐阅读
相关标签
  

闽ICP备14008679号