蛮力法解决旅行商问题(穷举查找求最短路径)含解析与代码实现

简介: 蛮力法解决旅行商问题(穷举查找求最短路径)含解析与代码实现

目录


蛮力算法

蛮力算法的初步运用

旅行商问题

问题解析

代码实现

普通程序运行时间计时

高精度程序运行时间计时

随机数生成(一秒内)

解决旅行商问题


正文


蛮力算法


       在算法运用之前,我们先来了解一下蛮力算法


什么是蛮力法?

是一种简单直接地解决问题的方法,常常基于问题的描述和所涉及的概念定义,没有特别巧妙的设计策略。

例如:计算 a n 值的蛮力法就是乘上 n 次的 a 。

蛮力法的缺点:通常效率很低

⚫ 为什么要学习蛮力法(或蛮力法的优点)?

       1、相比于其它算法设计策略,它是更一般性的方法,能解决广阔领域的各种问题;

       2、如果输入规模不大,而蛮力法又能以接受的速度求解,设计更高效算法所花费的代价很可能不值得;

       3、即使效率通常很低,仍然可以解决一些小规模的问题实例。


蛮力算法的初步运用


选择排序和冒泡排序是解决排序问题最直截了当的方法。而选择排序和冒泡排序也正是使用蛮力算法的思想。


       选择排序每次扫描数组,找到最小(或最大)元素,并与扫描开始处的元素进行交换。每次都会从当前位置将整个数组扫描一遍,这就是蛮力算法的思想。


       冒泡排序比较相邻元素,如果逆序就交换它们的位置,每次将最大的元素沉到最后位置(相对地,较小/轻的元素往上冒)。如此扫描数组n-1次即可排序完成。同样的,每一次扫描也会从当前位置将整个数组扫描一遍。


       我们以选择排序来举个例子:对数组89, 45, 68, 90, 29, 34, 17进行选择排序(标黄为已排序好了的)

未扫描 89 45 68 90 29 34 17
第一次扫描后 17 45 68 90 29 34 89
第二次扫描后 17 29 68 90 45 34 89
第三次扫描后 17 29 34 90 45 68 89
第四次扫描后 17 29 34 45 90 68 89
第五次扫描后 17 29 34 45 68 90 89
第六次扫描后 17 29 34 45 68 89 90

 每次将数组遍历一遍,找到当前最小的数,排序好这个数后,重复这个过程,直到全部排完。


旅行商问题


问题解析


     旅行商问题:给定n个城市,从一个城市出发找出一条连通n个城市的最短路径,且回到出发城市前,对每个城市都只访问一次。


       旅行商问题可以用加权图来建模,即顶点代表城市,边代表城市间的距离。这样,旅行商问题实际就可以表述为求一个图的最短哈密顿回路(注:对图的每个顶点只穿越一次的回路)问题。


       哈密顿回路可定义为n+1个相邻顶点v(i0 ),v(i1 ),…,v(in-1 ),v(i0 )的序列,其中,中间的n-1个顶点互不相同。一旦中间的n-1个顶点的顺序确定,哈密顿回路及其线路长度就确定了。


       穷举查找解决旅行商问题:首先穷举n-1个顶点的所有排列组合并计算路径长度,然后从中查找出线路最短的一个。


代码实现


普通程序运行时间计时


   使用库中的函数来完成计时,不过精度只有毫秒‘

#include <time.h>   //引入头文件
int main()
{
    clock_t start,end;   //定义clock_t变量
    start = clock();     //开始时间
    fun()  //需计时的函数
    end = clock();   //结束时间
    cout<<"time = "<<double(end-start)/CLOCKS_PER_SEC<<"s"<<endl;  //输出时间(单位:s)
}


高精度程序运行时间计时


       这个高精度计数的方法想要用到<windows.h>的头文件,是以微秒为单位,但是QueryPerformanceCounter()确切的精确计时的最小单位是与系统有关的。

#include <windows.h>   //引入头文件
int main()
{
    LARGE_INTEGER t1,t2,tc;
    QueryPerformanceFrequency(&tc);
    QueryPerformanceCounter(&t1);
    fun()  //需计时的函数
    QueryPerformanceCounter(&t2);
    time=(double)(t2.QuadPart-t1.QuadPart)/(double)tc.QuadPart; 
    cout<<"time = "<<time<<endl;  //输出时间(单位:s)
}


随机数生成(一秒内)


       生成随机数最常见的方法是用内置的函数rand()函数,再由srand()函数(用于给rand()函数设定种子)配合time() 函数获取不同的种子,由此来生成随机数。但是有一个缺点,那就是time()获取的时间是秒,而程序运行速度较快时,在同一秒中生生成了多个随机数,那么他们的种子就都是相同的,生成的随机数也是一样的。所以这个方法并不是特别实用。


       在这里我们来介绍一种利用硬件可以在一秒内生成多个随机数的方法,其原理与上面类似,不过这个取的种子的精度并不是秒,而是微秒,这样就能最大程度的保障每一次生成随机数的种子都是不同的。

/*
Random:一秒内生成不同随机数
参数: 
  @n : 为精度,即运算中保留小数点后几位
  @min:随机数的最小取值 
  @max: 随机数的最大取值
*/ 
int Random(int n,int min,int max)
{
  LARGE_INTEGER seed;
  QueryPerformanceFrequency(&seed);//返回硬件支持的高精度计数器的频率
  QueryPerformanceCounter(&seed);//函数返回高精确度性能计数器的值,它可以以微妙为单位计
  srand(seed.QuadPart);    //初始化一个以微秒为单位的时间种子
    int Precision = pow(10,n) - 1;
    return (int)((rand() % Precision / (float)(Precision + 1))*pow(10,n)) % max + min;
}


解决旅行商问题


代码实现:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <profileapi.h>
#include <Math.h>
#include <windows.h>
using namespace std;
#define maxx 9999
int l[maxx][maxx];    //存储两个城市之间的距离
int n,min_l = 65535; //城市数量,最短路径
int sum[maxx];        //标记每条路线的路程总长度
int go_city = 0;      //标记从第go_city个城市出发
int visited[maxx];    //visited[i]=1时,第i个城市已经去过;反之则为visited[i]=0;
int path_index = 1;   //已经去过的城市数目。
int path[maxx][maxx]; //存储经过城市的路线
int route = 0;        //存储第几条路线
int min_path[maxx];   //存储最短路径 
int recursion(int index)
{
    if(path_index != n)
    {
        for(int i=1;i <= n;i++)
        {
            if(visited[i] == 0)
            {
                visited[i] = 1;
                path[route][path_index] = index;
                path_index++;
                recursion(i);
                //回溯
                path_index--;
                visited[i] = 0;
            }
        }
    }
    else
    {
        path[route][path_index] = index;
        path[route][path_index + 1] = go_city;
        //计算每条路线的路程总长度
        sum[route] = 0;
        for(int i=1;i<=n;i++)
        {
            sum[route] += l[ path[route][i] ][ path[route][i+1] ];
            //当route+1后,path[route][i]的前面需要保持,后面变化。
            path[route+1][i] = path[route][i];
        }
        if(min_l > sum[route])        //如果线路长度小于最小值,则更新最短路径和最短距离
        {
          for(int i=1;i<=n;i++)
          {
              min_path[i] = path[route][i];
          }
            min_l = sum[route];
        }
        route++;
    }
    return 0;
}
/*
Random:一秒内生成不同随机数
参数: 
  @n : 为精度,即运算中保留小数点后几位
  @min:随机数的最小取值 
  @max: 随机数的最大取值
*/ 
int Random(int n,int min,int max)
{
  LARGE_INTEGER seed;
  QueryPerformanceFrequency(&seed);//返回硬件支持的高精度计数器的频率
  QueryPerformanceCounter(&seed);//函数返回高精确度性能计数器的值,它可以以微妙为单位计
  srand(seed.QuadPart);    //初始化一个以微秒为单位的时间种子
    int Precision = pow(10,n) - 1;
    return (int)((rand() % Precision / (float)(Precision + 1))*pow(10,n)) % max + min;
}
/*int Random(int l,int r)//生成范围在l~r的随机数 
{ 
  srand(time(0));  //设置时间种子
  return rand()%(r-l+1)+l;//生成区间r~l的随机数 
}*/
int main()
{
    memset(visited,0,sizeof(visited));
    cout << "请输入城市数量:";
    cin >> n;
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            //printf("请输入%d号城市到%d号城市之间的距离:",i,j);
            //cin >> l[i][j];
            l[i][j] = Random(5,0,100);
            l[j][i] = l[i][j];
        }
    }
    printf("\n\n");
    for(int a = 1;a < n+1;a++)
    {
      for(int b= 1;b < n+1;b++)
      {
        printf("%d\t",l[a][b]);
    }
    printf("\n");
  }
    cout << "请输入出发的城市是第几个城市:";
    cin >> go_city;
    visited[go_city] = 1;
  LARGE_INTEGER t1,t2,tc;
    QueryPerformanceFrequency(&tc);   //返回每秒嘀哒声的个数 
    QueryPerformanceCounter(&t1);   //返回的嘀哒声的频率 
    recursion(go_city);
    cout << "最短路程的线路为: ";
    for(int i=1;i<=n;i++)
    {
        cout << min_path[i] << " --> ";
        min_path[i] = min_path[i];
        if(i == n)
        {
          cout << min_path[1] << endl;
    }
    }
    cout << "最短路程长度为: ";
    cout << min_l << endl;
    QueryPerformanceCounter(&t2);
    double time=(double)(t2.QuadPart-t1.QuadPart)/(double)tc.QuadPart; 
    cout<<"time = "<<time<<endl;  //输出时间(单位:)
    return 0;
}
相关文章
|
8月前
|
算法 PyTorch 算法框架/工具
昇腾 msmodelslim w8a8量化代码解析
msmodelslim w8a8量化算法原理和代码解析
631 5
|
10月前
|
搜索推荐 UED Python
实现一个带有昼夜背景切换的动态时钟:从代码到功能解析
本文介绍了一个使用Python和Tkinter库实现的动态时钟程序,具有昼夜背景切换、指针颜色随机变化及整点和半点报时功能。通过设置不同的背景颜色和随机变换指针颜色,增强视觉吸引力;利用多线程技术确保音频播放不影响主程序运行。该程序结合了Tkinter、Pygame、Pytz等库,提供了一个美观且实用的时间显示工具。欢迎点赞、关注、转发、收藏!
444 94
|
8月前
|
传感器 监控 Java
Java代码结构解析:类、方法、主函数(1分钟解剖室)
### Java代码结构简介 掌握Java代码结构如同拥有程序世界的建筑蓝图,类、方法和主函数构成“黄金三角”。类是独立的容器,承载成员变量和方法;方法实现特定功能,参数控制输入环境;主函数是程序入口。常见错误包括类名与文件名不匹配、忘记static修饰符和花括号未闭合。通过实战案例学习电商系统、游戏角色控制和物联网设备监控,理解类的作用、方法类型和主函数任务,避免典型错误,逐步提升编程能力。 **脑图速记法**:类如太空站,方法即舱段;main是发射台,static不能换;文件名对仗,括号要成双;参数是坐标,void不返航。
338 5
|
9月前
|
人工智能 文字识别 自然语言处理
保单AI识别技术及代码示例解析
车险保单包含基础信息、车辆信息、人员信息、保险条款及特别约定等关键内容。AI识别技术通过OCR、文档结构化解析和数据校验,实现对保单信息的精准提取。然而,版式多样性、信息复杂性、图像质量和法律术语解析是主要挑战。Python代码示例展示了如何使用PaddleOCR进行保单信息抽取,并提出了定制化训练、版式分析等优化方向。典型应用场景包括智能录入、快速核保、理赔自动化等。未来将向多模态融合、自适应学习和跨区域兼容性发展。
|
11月前
|
自然语言处理 搜索推荐 数据安全/隐私保护
鸿蒙登录页面好看的样式设计-HarmonyOS应用开发实战与ArkTS代码解析【HarmonyOS 5.0(Next)】
鸿蒙登录页面设计展示了 HarmonyOS 5.0(Next)的未来美学理念,结合科技与艺术,为用户带来视觉盛宴。该页面使用 ArkTS 开发,支持个性化定制和无缝智能设备连接。代码解析涵盖了声明式 UI、状态管理、事件处理及路由导航等关键概念,帮助开发者快速上手 HarmonyOS 应用开发。通过这段代码,开发者可以了解如何构建交互式界面并实现跨设备协同工作,推动智能生态的发展。
668 10
鸿蒙登录页面好看的样式设计-HarmonyOS应用开发实战与ArkTS代码解析【HarmonyOS 5.0(Next)】
|
10月前
|
SQL Java 数据库连接
如何在 Java 代码中使用 JSqlParser 解析复杂的 SQL 语句?
大家好,我是 V 哥。JSqlParser 是一个用于解析 SQL 语句的 Java 库,可将 SQL 解析为 Java 对象树,支持多种 SQL 类型(如 `SELECT`、`INSERT` 等)。它适用于 SQL 分析、修改、生成和验证等场景。通过 Maven 或 Gradle 安装后,可以方便地在 Java 代码中使用。
3234 11
|
11月前
|
PHP 开发者 容器
PHP命名空间深度解析:避免命名冲突与提升代码组织####
本文深入探讨了PHP中命名空间的概念、用途及最佳实践,揭示其在解决全局命名冲突、提高代码可维护性方面的重要性。通过生动实例和详尽分析,本文将帮助开发者有效利用命名空间来优化大型项目结构,确保代码的清晰与高效。 ####
175 20
|
12月前
|
机器学习/深度学习 存储 人工智能
强化学习与深度强化学习:深入解析与代码实现
本书《强化学习与深度强化学习:深入解析与代码实现》系统地介绍了强化学习的基本概念、经典算法及其在深度学习框架下的应用。从强化学习的基础理论出发,逐步深入到Q学习、SARSA等经典算法,再到DQN、Actor-Critic等深度强化学习方法,结合Python代码示例,帮助读者理解并实践这些先进的算法。书中还探讨了强化学习在无人驾驶、游戏AI等领域的应用及面临的挑战,为读者提供了丰富的理论知识和实战经验。
584 5
|
存储 安全 Java
系统安全架构的深度解析与实践:Java代码实现
【11月更文挑战第1天】系统安全架构是保护信息系统免受各种威胁和攻击的关键。作为系统架构师,设计一套完善的系统安全架构不仅需要对各种安全威胁有深入理解,还需要熟练掌握各种安全技术和工具。
495 10
|
前端开发 JavaScript 开发者
揭秘前端高手的秘密武器:深度解析递归组件与动态组件的奥妙,让你代码效率翻倍!
【10月更文挑战第23天】在Web开发中,组件化已成为主流。本文深入探讨了递归组件与动态组件的概念、应用及实现方式。递归组件通过在组件内部调用自身,适用于处理层级结构数据,如菜单和树形控件。动态组件则根据数据变化动态切换组件显示,适用于不同业务逻辑下的组件展示。通过示例,展示了这两种组件的实现方法及其在实际开发中的应用价值。
203 1

推荐镜像

更多
  • DNS