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

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 蛮力法解决旅行商问题(穷举查找求最短路径)含解析与代码实现

目录


蛮力算法

蛮力算法的初步运用

旅行商问题

问题解析

代码实现

普通程序运行时间计时

高精度程序运行时间计时

随机数生成(一秒内)

解决旅行商问题


正文


蛮力算法


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


什么是蛮力法?

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

例如:计算 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;
}
相关文章
|
21天前
|
存储 安全 Java
系统安全架构的深度解析与实践:Java代码实现
【11月更文挑战第1天】系统安全架构是保护信息系统免受各种威胁和攻击的关键。作为系统架构师,设计一套完善的系统安全架构不仅需要对各种安全威胁有深入理解,还需要熟练掌握各种安全技术和工具。
60 10
|
21天前
|
前端开发 JavaScript 开发者
揭秘前端高手的秘密武器:深度解析递归组件与动态组件的奥妙,让你代码效率翻倍!
【10月更文挑战第23天】在Web开发中,组件化已成为主流。本文深入探讨了递归组件与动态组件的概念、应用及实现方式。递归组件通过在组件内部调用自身,适用于处理层级结构数据,如菜单和树形控件。动态组件则根据数据变化动态切换组件显示,适用于不同业务逻辑下的组件展示。通过示例,展示了这两种组件的实现方法及其在实际开发中的应用价值。
28 1
|
1月前
|
机器学习/深度学习 人工智能 算法
揭开深度学习与传统机器学习的神秘面纱:从理论差异到实战代码详解两者间的选择与应用策略全面解析
【10月更文挑战第10天】本文探讨了深度学习与传统机器学习的区别,通过图像识别和语音处理等领域的应用案例,展示了深度学习在自动特征学习和处理大规模数据方面的优势。文中还提供了一个Python代码示例,使用TensorFlow构建多层感知器(MLP)并与Scikit-learn中的逻辑回归模型进行对比,进一步说明了两者的不同特点。
64 2
|
1月前
|
存储 搜索推荐 数据库
运用LangChain赋能企业规章制度制定:深入解析Retrieval-Augmented Generation(RAG)技术如何革新内部管理文件起草流程,实现高效合规与个性化定制的完美结合——实战指南与代码示例全面呈现
【10月更文挑战第3天】构建公司规章制度时,需融合业务实际与管理理论,制定合规且促发展的规则体系。尤其在数字化转型背景下,利用LangChain框架中的RAG技术,可提升规章制定效率与质量。通过Chroma向量数据库存储规章制度文本,并使用OpenAI Embeddings处理文本向量化,将现有文档转换后插入数据库。基于此,构建RAG生成器,根据输入问题检索信息并生成规章制度草案,加快更新速度并确保内容准确,灵活应对法律与业务变化,提高管理效率。此方法结合了先进的人工智能技术,展现了未来规章制度制定的新方向。
34 3
|
1月前
|
SQL 监控 关系型数据库
SQL错误代码1303解析与处理方法
在SQL编程和数据库管理中,遇到错误代码是常有的事,其中错误代码1303在不同数据库系统中可能代表不同的含义
|
1月前
|
SQL 安全 关系型数据库
SQL错误代码1303解析与解决方案:深入理解并应对权限问题
在数据库管理和开发过程中,遇到错误代码是常见的事情,每个错误代码都代表着一种特定的问题
|
2月前
|
敏捷开发 安全 测试技术
软件测试的艺术:从代码到用户体验的全方位解析
本文将深入探讨软件测试的重要性和实施策略,通过分析不同类型的测试方法和工具,展示如何有效地提升软件质量和用户满意度。我们将从单元测试、集成测试到性能测试等多个角度出发,详细解释每种测试方法的实施步骤和最佳实践。此外,文章还将讨论如何通过持续集成和自动化测试来优化测试流程,以及如何建立有效的测试团队来应对快速变化的市场需求。通过实际案例的分析,本文旨在为读者提供一套系统而实用的软件测试策略,帮助读者在软件开发过程中做出更明智的决策。
|
2月前
|
SQL 人工智能 机器人
遇到的代码部份解析
/ 模拟后端返回的数据
18 0
|
2月前
|
设计模式 存储 算法
PHP中的设计模式:策略模式的深入解析与应用在软件开发的浩瀚海洋中,PHP以其独特的魅力和强大的功能吸引了无数开发者。作为一门历史悠久且广泛应用的编程语言,PHP不仅拥有丰富的内置函数和扩展库,还支持面向对象编程(OOP),为开发者提供了灵活而强大的工具集。在PHP的众多特性中,设计模式的应用尤为引人注目,它们如同精雕细琢的宝石,镶嵌在代码的肌理之中,让程序更加优雅、高效且易于维护。今天,我们就来深入探讨PHP中使用频率颇高的一种设计模式——策略模式。
本文旨在深入探讨PHP中的策略模式,从定义到实现,再到应用场景,全面剖析其在PHP编程中的应用价值。策略模式作为一种行为型设计模式,允许在运行时根据不同情况选择不同的算法或行为,极大地提高了代码的灵活性和可维护性。通过实例分析,本文将展示如何在PHP项目中有效利用策略模式来解决实际问题,并提升代码质量。
|
3月前
|
开发者 图形学 Java
揭秘Unity物理引擎核心技术:从刚体动力学到关节连接,全方位教你如何在虚拟世界中重现真实物理现象——含实战代码示例与详细解析
【8月更文挑战第31天】Unity物理引擎对于游戏开发至关重要,它能够模拟真实的物理效果,如刚体运动、碰撞检测及关节连接等。通过Rigidbody和Collider组件,开发者可以轻松实现物体间的互动与碰撞。本文通过具体代码示例介绍了如何使用Unity物理引擎实现物体运动、施加力、使用关节连接以及模拟弹簧效果等功能,帮助开发者提升游戏的真实感与沉浸感。
78 1

推荐镜像

更多