zoukankan      html  css  js  c++  java
  • 智能优化算法对TSP问题的求解研究

    要求:

    TSP 算法(Traveling Salesman Problem)是指给定 n 个城市和各个城市之间的距离,要

    求确定一条经过各个城市当且仅当一次的最短路径,它是一种典型的优化组合问题,其最优

    解得求解代价是指数级的。TSP 问题代表一类优化组合问题,在实际工程中有很多应用,如

    计算机联网、电子地图、交通诱导等,具有重要的研究价值。遗传算法和禁忌搜所算法都是

    是一种智能优化算法,具有全局的优化性能、通用性强。这种算法一般具有严密的理论依据,

    理论上可以在一定的时间内找到最优解或近似最优解,广泛应用于计算机科学优化调度问题、

    组合优化问题。通过阅读书籍以及科技文献,研究遗传算法或 禁忌搜索算法的基本原理,

    研究TSP 问题并提出常见解决方案。要求在此基础上,编程实现以智能优化算法来解决 TSP

    问题,并给出相应实验结果和算法分析。

     

    遗传算法:

    package org.wucl.ga;

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.Reader;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;

    import org.wucl.City;

    /**
    * 遗传算法解决TSP问题
    *
    * 1.个体编码方案 整数编码:对城市进行编号,每个城市分别用0到n-1之间不同的整数表示,n个整数的一个排列就代表了旅行商问题的一个可能解
    *
    * 2.交配方法 常规交配法,交配概率为100%
    *
    * 3.变异方法 打乱变异,使用逆序方式,变异概率为10%
    *
    * 4.新种群构成的方法 用轮盘赌求出子代种群
    *
    * 5.算法结束的条件 当代数打到200时,结束算法
    *
    * @author wucl(lailaiwcl@163.com)
    *
    */
    public class Genetic {

    List<City> vec = new ArrayList<City>();
    int n = 0;
    int num;
    double optimal; // 记录最优解
    int best[] = new int[n]; // 记录最终路径

    public void getStrategy() {

    try {
    Reader reader = new InputStreamReader(Genetic.class
    .getClassLoader().getResourceAsStream("data.txt"));
    BufferedReader br = new BufferedReader(reader);
    String line = br.readLine();
    while (line != null) {
    String[] sa = line.split(" ");
    vec.add(new City(sa[0], Double.parseDouble(sa[1]), Double
    .parseDouble(sa[2])));
    line = br.readLine();
    }
    n = vec.size();
    } catch (IOException e) {
    e.printStackTrace();
    }
    num = 4 * n * n; // 初始个体数为4*n*n;
    int a[][] = new int[num][n];

    for (int i = 0; i < num; i++) // 随机生成num个个体
    {
    a[i] = random(n, n);
    }
    optimal = getValue(a[0])[1];
    for (int i = 0; i < 200; i++) {
    a = nextGen(a);
    }
    System.out.print("最佳路径:");
    for (int i = 0; i < n - 1; i++)
    System.out.print(vec.get(best[i]).name + "->");
    System.out.println(vec.get(best[n - 1]).name);
    System.out.println("最佳长度:" + optimal);
    }

    public int[] random(int m, int t) // 生成互不相同的随机数序列的函数
    {
    Random r = new Random();
    int k = 0;
    int tmp[] = new int[m];
    for (int i = 0; i < m; i++) {
    while (true) {
    boolean flag = true;
    tmp[i] = r.nextInt(t);
    k++;
    for (int j = 0; j < k - 1; j++) {
    if (tmp[i] == tmp[j]) {
    k--;
    flag = false;
    break;
    }
    }
    if (flag == true)
    break;
    }
    }
    return tmp;
    }

    public int[][] Crossove(int a[][]) // 交配函数
    {
    int A[][] = new int[num][n];
    int k = 0;
    for (int i = 0; i < num; i = i + 2) {
    Random r = new Random();
    int loc = r.nextInt(n - 1);
    for (int j = 0; j <= loc; j++) {
    A[k][j] = a[i][j];
    A[k + 1][j] = a[i + 1][j];
    }
    int m1 = loc + 1, m2 = m1;
    for (int t = 0; t < n; t++) {
    if (!is_exist(loc, a[i + 1][t], a[i])) {
    A[k][m1] = a[i + 1][t];
    m1++;
    }
    if (!is_exist(loc, a[i][t], a[i + 1])) {
    A[k + 1][m2] = a[i][t];
    m2++;
    }

    }
    if (r.nextInt(100) < 10) {
    A[k] = variation(A[k]);
    }
    if (r.nextInt(100) < 10) {
    A[k + 1] = variation(A[k + 1]);
    }
    k += 2;
    }
    return A;
    }

    public int[][] nextGen(int a[][]) // 生成新种群的函数
    {
    int A[][] = new int[num][n];
    double p[] = new double[num];
    double value[] = new double[num];
    double sum = 0;
    for (int i = 0; i < num; i++) {
    double tmp[] = new double[2];
    tmp = getValue(a[i]);
    value[i] = tmp[0];
    if (optimal > tmp[1]) {
    optimal = tmp[1];
    best = a[i];
    }
    sum += value[i];
    }
    for (int i = 0; i < num; i++) {
    p[i] = value[i] / sum;
    }
    int k = 0;
    for (int i = 0; i < num; i++) {
    double s = 0;
    int j = 0;
    double r = Math.random();
    while (true) {
    if (s >= r)
    break;
    s += p[j];
    j++;
    }
    A[k] = a[j - 1];
    k++;
    }
    return Crossove(A);
    }

    public boolean is_exist(int loc, int x, int b[]) {
    for (int i = 0; i <= loc; i++) {
    if (x == b[i])
    return true;
    }
    return false;
    }

    public int[] variation(int a[]) // 变异函数
    {
    int b[] = new int[n];
    Random r = new Random();
    int x = r.nextInt(n);
    int y = r.nextInt(n);
    while (y == x)
    y = r.nextInt(n);
    if (x < y) {
    int k = 0;
    for (int i = 0; i < n; i++) {
    if (i < x)
    b[i] = a[i];
    else if (i > y)
    b[i] = a[i];
    else {
    b[i] = a[y - k];
    k++;
    }
    }
    } else {
    int yy = y, k = 1;
    for (int i = 0; i < n; i++) {
    if (i <= yy) {
    b[i] = a[y];
    y--;
    } else if (i >= x) {
    b[i] = a[n - k];
    k++;
    } else
    b[i] = a[i];

    }
    }
    return b;
    }

    public double[] getValue(int a[]) // 获取路径值的函数
    {
    double length = 0;
    int k = a.length - 1;
    for (int i = 0; i < k; i++) {
    double tmp1 = Math.pow(vec.get(a[i]).x - vec.get(a[i + 1]).x, 2);
    double tmp2 = Math.pow(vec.get(a[i]).y - vec.get(a[i + 1]).y, 2);
    length += Math.sqrt(tmp1 + tmp2);
    }
    double tmp1 = Math.pow(vec.get(a[0]).x - vec.get(a[k]).x, 2);
    double tmp2 = Math.pow(vec.get(a[0]).y - vec.get(a[k]).y, 2);
    length += Math.sqrt(tmp1 + tmp2);
    double f[] = new double[2];
    f[0] = 1 / Math.pow(length, 8);
    f[1] = length;
    return f;
    }

    public static void main(String argv[]) {
    new Genetic().getStrategy();
    }
    }

    禁忌搜索算法(1):单线程实现

    package org.wucl.ts;

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.Reader;
    import java.util.Random;

    import org.wucl.ga.Genetic;
    /**
    * 单线程实现禁忌搜索算法解决TSP问题
    *
    *
    * @author wucl(lailaiwcl@163.com)
    *
    */
    public class Tabu {

    private int MAX_GEN;// 迭代次数

    private int N;// 每次搜索邻居个数

    private int ll;// 禁忌长度

    private int cityNum; // 城市数量,编码长度

    private double[][] distance; // 距离矩阵

    private int bestT;// 最佳出现代数

    private int[] Ghh;// 初始路径编码

    private int[] bestGh;// 最好的路径编码

    private double bestEvaluation;

    private int[] LocalGhh;// 当代最好编码

    private double localEvaluation;

    private int[] tempGhh;// 存放临时编码

    private double tempEvaluation;

    private int[][] jinji;// 禁忌表

    private int t;// 当前代数

    private Random random;

    public Tabu() {

    }

    /**
    *
    * constructor of Tabu
    *
    * @param n
    *
    * 城市数量
    * @param g
    *
    * 运行代数
    * @param c
    *
    * 每次搜索邻居个数
    *
    * @param m
    *
    * 禁忌长度
    *
    **/

    public Tabu(int n, int g, int c, int m) {
    cityNum = n;
    MAX_GEN = g;
    N = c;
    ll = m;
    }


    /**

    * 初始化Tabu算法类

    * @param filename 数据文件名,该文件存储所有城市节点坐标数据

    * @throws IOException

    */
    private void init() throws IOException {

    // 读取数据
    int[] x;
    int[] y;
    String strbuff;
    Reader reader = new InputStreamReader(Genetic.class.getClassLoader().getResourceAsStream("data.txt"));
    BufferedReader data = new BufferedReader(reader);
    distance = new double[cityNum][cityNum];
    x = new int[cityNum];
    y = new int[cityNum];
    for (int i = 0; i < cityNum; i++) {
    strbuff = data.readLine();
    String[] strcol = strbuff.split(" ");
    x[i] = Integer.valueOf(strcol[1]);// x坐标
    y[i] = Integer.valueOf(strcol[2]);// y坐标

    }
    // 计算距离矩阵
    //System.out.println("距离矩阵为:");
    for (int i = 0; i < cityNum - 1; i++) {
    distance[i][i] = 0; // 对角线为0
    for (int j = 0; j < cityNum; j++) {
    distance[i][j] = Math
    .sqrt(((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j])
    * (y[i] - y[j])));
    //System.out.print(Math.ceil(distance[i][j]) + " ");
    }
    //System.out.println();

    }

    distance[cityNum - 1][cityNum - 1] = 0;
    Ghh = new int[cityNum];
    bestGh = new int[cityNum];
    bestEvaluation = Integer.MAX_VALUE;
    LocalGhh = new int[cityNum];
    localEvaluation = Integer.MAX_VALUE;
    tempGhh = new int[cityNum];
    tempEvaluation = Integer.MAX_VALUE;
    jinji = new int[ll][cityNum];
    bestT = 0;
    t = 0;
    random = new Random(System.currentTimeMillis());
    }

    // 初始化编码Ghh

    void initGroup() {
    int i, j;
    Ghh[0] = random.nextInt(65535) % cityNum;
    for (i = 1; i < cityNum;)// 编码长度
    {
    Ghh[i] = random.nextInt(65535) % cityNum;
    for (j = 0; j < i; j++) {
    if (Ghh[i] == Ghh[j]) {
    break;
    }
    }
    if (j == i) {
    i++;
    }
    }
    }

    // 复制编码体,复制编码Gha到Ghb

    public void copyGh(int[] Gha, int[] Ghb) {
    for (int i = 0; i < cityNum; i++) {
    Ghb[i] = Gha[i];
    }
    }

    public double evaluate(int[] chr) {
    double len = 0;
    // 编码,起始城市,城市1,城市2...城市n
    for (int i = 1; i < cityNum; i++) {
    len += distance[chr[i - 1]][chr[i]];
    }
    // // 城市n,起始城市
    len += distance[chr[cityNum - 1]][chr[0]];
    // for (int i = 1; i < cityNum; i++) {
    // System.out.print(chr[i] + ",");
    // }
    // System.out.println("-------------" + len);
    return len;
    }

    // 邻域交换,得到邻居
    public void Linju(int[] Gh, int[] tempGh) {
    int i, temp;
    int ran1, ran2;
    for (i = 0; i < cityNum; i++) {
    tempGh[i] = Gh[i];
    }
    ran1 = random.nextInt(65535) % cityNum;
    ran2 = random.nextInt(65535) % cityNum;
    while (ran1 == ran2) {
    ran2 = random.nextInt(65535) % cityNum;
    }
    temp = tempGh[ran1];
    tempGh[ran1] = tempGh[ran2];
    tempGh[ran2] = temp;

    }

    // 判断编码是否在禁忌表中

    public int panduan(int[] tempGh) {
    int i, j;
    int flag = 0;
    for (i = 0; i < ll; i++) {
    flag = 0;
    for (j = 0; j < cityNum; j++) {
    if (tempGh[j] != jinji[i][j]) {
    flag = 1;// 不相同
    break;
    }
    }
    if (flag == 0)// 相同,返回存在相同
    {
    // return 1;
    break;

    }
    }

    if (i == ll)// 不等
    {
    return 0;// 不存在

    } else {
    return 1;// 存在
    }
    }

    // 解禁忌与加入禁忌

    public void jiejinji(int[] tempGh) {
    int i, j, k;
    // 删除禁忌表第一个编码,后面编码往前挪动
    for (i = 0; i < ll - 1; i++) {
    for (j = 0; j < cityNum; j++) {
    jinji[i][j] = jinji[i + 1][j];
    }
    }

    // 新的编码加入禁忌表

    for (k = 0; k < cityNum; k++) {
    jinji[ll - 1][k] = tempGh[k];
    }
    }

    public void solve() {
    int nn;
    // 初始化编码Ghh
    initGroup();
    copyGh(Ghh, bestGh);// 复制当前编码Ghh到最好编码bestGh
    bestEvaluation = evaluate(Ghh);
    while (t < MAX_GEN) {
    nn = 0;
    localEvaluation = Integer.MAX_VALUE;
    while (nn < N) {
    Linju(Ghh, tempGhh);// 得到当前编码Ghh的邻域编码tempGhh
    if (panduan(tempGhh) == 0)// 判断编码是否在禁忌表中
    {
    // 不在
    tempEvaluation = evaluate(tempGhh);
    if (tempEvaluation < localEvaluation) {
    copyGh(tempGhh, LocalGhh);
    localEvaluation = tempEvaluation;
    }
    nn++;
    }
    }
    if (localEvaluation < bestEvaluation) {
    bestT = t;
    copyGh(LocalGhh, bestGh);
    bestEvaluation = localEvaluation;
    }
    copyGh(LocalGhh, Ghh);
    // 解禁忌表,LocalGhh加入禁忌表
    jiejinji(LocalGhh);
    t++;
    }
    System.out.print("最佳长度出现代数:");
    System.out.println(bestT);
    System.out.print("最佳长度:");
    System.out.println(bestEvaluation);
    System.out.print("最佳路径:");
    for (int i = 0; i < cityNum; i++) {
    System.out.print(bestGh[i] + "->");
    }
    }

    public static void main(String[] args) throws IOException {
    System.out.println("Start....");
    Tabu tabu = new Tabu(30,200000, 200, 20);
    tabu.init();
    long t1 = System.currentTimeMillis();
    tabu.solve();
    System.out.println();
    System.out.println("计算用时:" + (System.currentTimeMillis() - t1));
    }

    }

     

     

     

    禁忌搜索算法(2):多线程是实现

    package org.wucl.ts;

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.Reader;
    import java.util.Random;

    import org.wucl.ga.Genetic;

    /**
    * 多线程调度实现禁忌搜索算法解决TSP问题
    *
    *
    * @author wucl(lailaiwcl@163.com)
    *
    */
    public class Tabu2 {

    public static int MAX_GEN;// 迭代次数

    private int N;// 每次搜索邻居个数

    private int ll;// 禁忌长度

    private int cityNum; // 城市数量,编码长度

    private double[][] distance; // 距离矩阵

    public int bestT;// 最佳出现代数

    private int[] Ghh;// 初始路径编码

    public int[] bestGh;// 最好的路径编码

    public double bestEvaluation;

    private int[] LocalGhh;// 当代最好编码

    public double localEvaluation;

    private int[] tempGhh;// 存放临时编码

    private double tempEvaluation;

    private static int[][] jinji;// 禁忌表

    private static int t;// 当前代数

    private Random random;

    private Object obj1 = new Object();

    private Object obj2 = new Object();

    public Tabu2() {

    }

    /**
    *
    * constructor of Tabu
    *
    * @param n
    *
    * 城市数量
    * @param g
    *
    * 运行代数
    * @param c
    *
    * 每次搜索邻居个数
    *
    * @param m
    *
    * 禁忌长度
    *
    **/

    public Tabu2(int n, int c, int m) {
    cityNum = n;
    N = c;
    ll = m;
    }

    /**

    * 初始化Tabu算法类

    * @param filename 数据文件名,该文件存储所有城市节点坐标数据

    * @throws IOException

    */
    private void init() throws IOException {

    // 读取数据
    int[] x;
    int[] y;
    String strbuff;
    Reader reader = new InputStreamReader(Genetic.class.getClassLoader()
    .getResourceAsStream("data.txt"));
    BufferedReader data = new BufferedReader(reader);
    distance = new double[cityNum][cityNum];
    x = new int[cityNum];
    y = new int[cityNum];
    for (int i = 0; i < cityNum; i++) {
    strbuff = data.readLine();
    String[] strcol = strbuff.split(" ");
    x[i] = Integer.valueOf(strcol[1]);// x坐标
    y[i] = Integer.valueOf(strcol[2]);// y坐标

    }
    // 计算距离矩阵
    // System.out.println("距离矩阵为:");
    for (int i = 0; i < cityNum - 1; i++) {
    distance[i][i] = 0; // 对角线为0
    for (int j = 0; j < cityNum; j++) {
    distance[i][j] = Math
    .sqrt(((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j])
    * (y[i] - y[j])));
    }

    }

    distance[cityNum - 1][cityNum - 1] = 0;
    Ghh = new int[cityNum];
    bestGh = new int[cityNum];
    bestEvaluation = Integer.MAX_VALUE;
    LocalGhh = new int[cityNum];
    localEvaluation = Integer.MAX_VALUE;
    tempGhh = new int[cityNum];
    tempEvaluation = Integer.MAX_VALUE;
    jinji = new int[ll][cityNum];
    bestT = 0;
    t = 0;
    random = new Random(System.currentTimeMillis());
    }

    // 初始化编码Ghh

    void initGroup() {
    int i, j;
    Ghh[0] = random.nextInt(65535) % cityNum;
    for (i = 1; i < cityNum;)// 编码长度
    {
    Ghh[i] = random.nextInt(65535) % cityNum;
    for (j = 0; j < i; j++) {
    if (Ghh[i] == Ghh[j]) {
    break;
    }
    }
    if (j == i) {
    i++;
    }
    }
    }

    // 复制编码体,复制编码Gha到Ghb

    public void copyGh(int[] Gha, int[] Ghb) {
    for (int i = 0; i < cityNum; i++) {
    Ghb[i] = Gha[i];
    }
    }

    public double evaluate(int[] chr) {
    double len = 0;
    // 编码,起始城市,城市1,城市2...城市n
    for (int i = 1; i < cityNum; i++) {
    len += distance[chr[i - 1]][chr[i]];
    }
    // // 城市n,起始城市
    len += distance[chr[cityNum - 1]][chr[0]];
    return len;
    }

    // 邻域交换,得到邻居
    public void Linju(int[] Gh, int[] tempGh) {
    int i, temp;
    int ran1, ran2;
    for (i = 0; i < cityNum; i++) {
    tempGh[i] = Gh[i];
    }
    ran1 = random.nextInt(65535) % cityNum;
    ran2 = random.nextInt(65535) % cityNum;
    while (ran1 == ran2) {
    ran2 = random.nextInt(65535) % cityNum;
    }
    temp = tempGh[ran1];
    tempGh[ran1] = tempGh[ran2];
    tempGh[ran2] = temp;

    }

    // 判断编码是否在禁忌表中
    public int panduan(int[] tempGh) {
    int i, j;
    int flag = 0;
    for (i = 0; i < ll; i++) {
    flag = 0;
    for (j = 0; j < cityNum; j++) {
    if (tempGh[j] != jinji[i][j]) {
    flag = 1;// 不相同
    break;
    }
    }
    if (flag == 0)// 相同,返回存在相同
    {
    // return 1;
    break;

    }
    }

    if (i == ll)// 不等
    {
    return 0;// 不存在
    } else {
    return 1;// 存在
    }
    }

    // 解禁忌与加入禁忌
    public void jiejinji(int[] tempGh) {
    synchronized (obj2) {
    int i, j, k;
    // 删除禁忌表第一个编码,后面编码往前挪动
    for (i = 0; i < ll - 1; i++) {
    for (j = 0; j < cityNum; j++) {
    jinji[i][j] = jinji[i + 1][j];
    }
    }

    // 新的编码加入禁忌表
    for (k = 0; k < cityNum; k++) {
    jinji[ll - 1][k] = tempGh[k];
    }
    }
    }

    public void solve() {
    try {
    init();
    } catch (IOException e) {
    e.printStackTrace();
    }
    int nn;
    // 初始化编码Ghh
    initGroup();
    copyGh(Ghh, bestGh);// 复制当前编码Ghh到最好编码bestGh
    bestEvaluation = evaluate(Ghh);
    while (t < MAX_GEN) {
    nn = 0;
    localEvaluation = Integer.MAX_VALUE;
    while (nn < N) {
    Linju(Ghh, tempGhh);// 得到当前编码Ghh的邻域编码tempGhh
    if (panduan(tempGhh) == 0)// 判断编码是否在禁忌表中
    {
    // 不在
    tempEvaluation = evaluate(tempGhh);
    if (tempEvaluation < localEvaluation) {
    copyGh(tempGhh, LocalGhh);
    localEvaluation = tempEvaluation;
    }
    nn++;
    }
    }
    if (localEvaluation < bestEvaluation) {
    bestT = t;
    copyGh(LocalGhh, bestGh);
    bestEvaluation = localEvaluation;
    }
    copyGh(LocalGhh, Ghh);
    // 解禁忌表,LocalGhh加入禁忌表
    jiejinji(LocalGhh);
    synchronized (obj1) {
    t++;
    }
    }
    }

    public static void main(String[] args) {
    Tabu2.MAX_GEN = 200000;
    final Tabu2 tabu1 = new Tabu2(30, 200, 20);
    final Tabu2 tabu2 = new Tabu2(30, 200, 20);
    final Thread thread1 = new Thread("thread-1") {
    @Override
    public void run() {
    System.out.println("thread-1 start...");
    tabu1.solve();
    }
    };
    final Thread thread2 = new Thread("thread-2") {
    @Override
    public void run() {
    System.out.println("thread-2 start...");
    tabu2.solve();
    }
    };
    final long t1 = System.currentTimeMillis();
    Thread thread3 = new Thread("thread-2") {
    @Override
    public void run() {
    System.out.println("thread-3 start...");
    while (true) {
    if (!thread1.isAlive() && !thread2.isAlive()) {
    Tabu2 bestTabu = tabu1;
    if (tabu1.localEvaluation <= tabu2.localEvaluation) {
    bestTabu = tabu2;
    }
    System.out.print("最佳长度出现代数:");
    System.out.println(bestTabu.bestT);
    System.out.print("最佳长度:");
    System.out.println(bestTabu.bestEvaluation);
    System.out.print("最佳路径:");
    for (int i = 0; i < 30; i++) {
    System.out.print(bestTabu.bestGh[i] + "->");
    }
    System.out.println();
    break;
    }
    }
    System.out.println("计算用时:" + (System.currentTimeMillis() - t1));
    }
    };
    thread1.start();
    thread2.start();
    thread3.start();
    }

    }

     

  • 相关阅读:
    python将阿拉伯数字转换为中文书写的数字形式
    python生成textgrid文件
    将 Nodejs 服务部署到阿里云服务器
    盛最多水的容器
    连续子数组的最大和
    数组中出现次数超过一半的数字
    变态跳台阶
    二进制中 1 的个数
    784.字母大小写全排列
    链表中环的入口节点
  • 原文地址:https://www.cnblogs.com/luo841997665/p/4623894.html
Copyright © 2011-2022 走看看