使用omp并行技术加速最短路径算法-迪杰斯特拉(Dijkstra)算法(记录最短路径和距离)

原理:

Dijkstra算法是解决**单源最短路径**问题的**贪心算法**

它先求出长度最短的一条路径,再参照该最短路径求出长度次短的一条路径

    直到求出从源点到其他各个顶点的最短路径。

首先假定源点为u,顶点集合V被划分为两部分:集合 S 和 V-S。 初始时S中仅含有源点u,其中S中的顶点到源点的最短路径已经确定。

集合S 和V-S中所包含的顶点到源点的最短路径的长度待定,称从源点出发只经过S中的点到达V-S中的点的路径为特殊路径,

并用dist[]记录当前每个顶点对应的最短特殊路径长度。

选择特殊路径长度最短的路径,将其连接的V-S中的顶点加入到集合S中,同时更新数组dist[]。一旦S包含了所有顶点,dist[]就是从源到所有其他顶点的最短路径长度。

(1)数据结构。 设置地图的带权邻接矩阵为map[][],即如果从源点u到顶点i有边,就令map[u][i]=<u,i>的权值,否则map[u][i]=∞;

              采用一维数组dist[i]来记录从源点到i顶点的最短路径长度:采用一维数组p[i]来记录最短路径上i顶点的前驱。

(2)初始化。令集合S={u},对于集合V-S中的所有顶点x,初始化dist[i]=map[u][i],如果源点u到顶点i有边相连,初始化p[i]=u(i的前驱是u),否则p[i]=-1

(3)找最小。在集合V-S中依照贪心策略来寻找使得dist[j]具有最小值的顶点t,即dist[t]=min,则顶点t就是集合V-S中距离源点u最近的顶点。

(4)加入S战队。将顶点t加入集合S,同时更新V-S

(5)判结束。如果集合V-S为空,算法结束,否则转6

(6)借东风。在(3)中已近找到了源点到t的最短路径,那么对集合V-S中所有与顶点t相邻的顶点j,都可以借助t走捷径。

            如果dist[j]>dist[t]+map[t][j],则dist[j]=dist[t]+map[t][j],记录顶点j的前驱为t,p[j]=t,转(3)。

            //我自己在这里理解就是,从u找到与它最近的点t,在从t找到与它最近的点j,在....按照这样持续下去,直到最后一个点

    这里我再通俗的解释下这个借东风的意思。

    源点为1,如果我们找到了距离源点最近的点2,且点2与3,4相连。

        这样,我们如果要倒3,4有两种方法:

                1->2->3(4)

                1->3(4)

        这里我们就要判断是从1直接到3(4)快,还是经过2后快。假设<1,2>=2 / <2,3>=3 / <1,3>=4

            根据上面的数据,我们第一次找最小找到的是2结点,如果我们直接把2替换掉1当做源点继续找下一个最近的点,这种方法是错的。

            因为可以看出1->3只用4,而过2的话要用5。

实现代码如下:

#include<bits/stdc++.h>
#include<omp.h>
#include<time.h>
using namespace std;//1899 20296
const int N=2000;	//城市个数可修改
const int INF=1e7;	//初始化无穷大为.......
int G[N][N],dist[N],p[N],n,m;	//n为城市个数,m为城市间路线的条数
bool flag[N];	//如果flag[i]=true,说明该顶点i已经加入到集合S;否则i属于集合V-S
//#pragma omp parallel for

void Dijkstra(int u){
    #pragma omp parallel for
	for(int i=1;i<=n;i++){//********>>>--1--<<<******//
        
		dist[i]=G[u][i];	//初始化源点u到其他各个顶点的最短路径长度
		flag[i]=false;
		if(dist[i]==INF)
			p[i]=-1;	//说明源点u到顶点i无边相连,设置p[i]=-1
		else
			p[i]=u;	//说明源点u到顶点i有边相连,设置p[i]=u
	}
	flag[u]=true;//初始化集合S中,只有一个元素:源点u
	dist[u]=0;	//初始化源点u的最短路径为0,自己到自己的最短路径




   
	for(int i=1;i<=n;i++){//********>>>--2--<<<******//
		int temp=INF,t=u;

        #pragma omp parallel for
		for(int j=1;j<=n;j++){//>>--3--<<在集合V-S中寻找距离源点u最近的顶点t
			if(!flag[j] && dist[j]<temp){
				t=j;	//记录距离源点u最近的顶点
				temp=dist[j];
			}
		}

		if(t==u) return ;	//找不到t跳出循环
		flag[t]=true;	//否则,将t加入集合S

        #pragma omp parallel for
		for(int j=1;j<=n;j++){//>>--4--<<更新集合V-S中与t邻接的顶点到u的距离
			if(!flag[j] && G[t][j]<INF){//!flag[j]表示j在v-s集合中,map[t][j]<INF表示t与j邻接
				if(dist[j]>(dist[t]+G[t][j])){//经过t到达j的路径更短
					dist[j]=dist[t]+G[t][j];
					p[j]=t;	//记录j的前驱为t
				}
			}
		}	
	}	
}


void findpath(int u)
{
	int x;
	stack<int>s;
	cout << "源点为:" << u << endl;
	for (int i = 1; i <= n; i++)
	{
		x = p[i];
		while (x != -1)
		{
			s.push(x);
			x = p[x];
		}
		cout << "源点到其他各顶点的最短路径为:";
		while (!s.empty())
		{
			cout << s.top() << "--";
			s.pop();
		}
		cout << i << ";最短距离为:" << dist[i] << endl;
	}
}



int main(int argc, char *argv[]){
	int u, v, w, start;
    int number = atoi(argv[1]);   //线程数
    ifstream fin("input.txt");

    fin >> n >> m;
    for(int i=1;i<=n;i++)//初始化图的邻接矩阵
		for (int j = 1; j <= n; j++)
		{
			G[i][j] = INF;//初始化邻接矩阵为无穷大
		}
    while(fin>>u >> v >> w)
    {
        G[u][v] = min(G[u][v], w);	//邻接矩阵存储,保留最小的距离
    }

	cout << "请输所在的位置:" << endl;
	cin>>start;
	double st, ed;
    omp_set_num_threads(number);   //设置线程数
    st = omp_get_wtime();
	Dijkstra(start);
	ed = omp_get_wtime();
	findpath(start);
	cout << "Time: " << ed-st<< "s" << endl;
	return 0;
}

使用 #pragma omp parallel for进行加速:

编译:g++ ./filename.cpp -o ./filename -fopenmp

运行: ./filename n  n表示线程数

运行结果:

数据长这样:

 也可以用这一组数据来测试:

6 9
0 1 7
0 2 9
0 5 14
1 2 10
1 3 15
2 3 11
2 5 2
3 4 6
4 5 9

 数据集下载提取码:k3v2

OpenMP基本概念
OpenMP是一种用于共享内存并行系统的多线程程序设计方案,支持的编程语言包括C、C++和Fortran。OpenMP提供了对并行算法的高层抽象描述,特别适合在多核CPU机器上的并行程序设计。编译器根据程序中添加的pragma指令,自动将程序并行处理,使用OpenMP降低了并行编程的难度和复杂度。当编译器不支持OpenMP时,程序会退化成普通(串行)程序。程序中已有的OpenMP指令不会影响程序的正常编译运行。

在VS中启用OpenMP很简单,很多主流的编译环境都内置了OpenMP。在项目上右键->属性->配置属性->C/C++->语言->OpenMP支持,选择“是”即可。

OpenMP执行模式
OpenMP采用fork-join的执行模式。开始的时候只存在一个主线程,当需要进行并行计算的时候,派生出若干个分支线程来执行并行任务。当并行代码执行完成之后,分支线程会合,并把控制流程交给单独的主线程。

一个典型的fork-join执行模型的示意图如下:

OpenMP编程模型以线程为基础,通过编译制导指令制导并行化,有三种编程要素可以实现并行化控制,他们分别是编译制导、API函数集和环境变量。

编译制导
编译制导指令以#pragma omp 开始,后边跟具体的功能指令,格式如:#pragma omp 指令[子句[,子句] …]。常用的功能指令如下:

parallel:用在一个结构块之前,表示这段代码将被多个线程并行执行;
for:用于for循环语句之前,表示将循环计算任务分配到多个线程中并行执行,以实现任务分担,必须由编程人员自己保证每次循环之间无数据相关性;
parallel for:parallel和for指令的结合,也是用在for循环语句之前,表示for循环体的代码将被多个线程并行执行,它同时具有并行域的产生和任务分担两个功能;
sections:用在可被并行执行的代码段之前,用于实现多个结构块语句的任务分担,可并行执行的代码段各自用section指令标出(注意区分sections和section);
parallel sections:parallel和sections两个语句的结合,类似于parallel for;
single:用在并行域内,表示一段只被单个线程执行的代码;
critical:用在一段代码临界区之前,保证每次只有一个OpenMP线程进入;
flush:保证各个OpenMP线程的数据影像的一致性;
barrier:用于并行域内代码的线程同步,线程执行到barrier时要停下等待,直到所有线程都执行到barrier时才继续往下执行;
atomic:用于指定一个数据操作需要原子性地完成;
master:用于指定一段代码由主线程执行;
threadprivate:用于指定一个或多个变量是线程专用,后面会解释线程专有和私有的区别。
相应的OpenMP子句为: 


private:指定一个或多个变量在每个线程中都有它自己的私有副本;
firstprivate:指定一个或多个变量在每个线程都有它自己的私有副本,并且私有变量要在进入并行域或任务分担域时,继承主线程中的同名变量的值作为初值;
lastprivate:是用来指定将线程中的一个或多个私有变量的值在并行处理结束后复制到主线程中的同名变量中,负责拷贝的线程是for或sections任务分担中的最后一个线程; 
reduction:用来指定一个或多个变量是私有的,并且在并行处理结束后这些变量要执行指定的归约运算,并将结果返回给主线程同名变量;
nowait:指出并发线程可以忽略其他制导指令暗含的路障同步;
num_threads:指定并行域内的线程的数目; 
schedule:指定for任务分担中的任务分配调度类型;
shared:指定一个或多个变量为多个线程间的共享变量;
ordered:用来指定for任务分担域内指定代码段需要按照串行循环次序执行;
copyprivate:配合single指令,将指定线程的专有变量广播到并行域内其他线程的同名变量中;
copyin:用来指定一个threadprivate类型的变量需要用主线程同名变量进行初始化;
default:用来指定并行域内的变量的使用方式,缺省是shared。
利用omp_set_num_threads()来设置线程数,

利用#pragma omp parallel sections 声明下面大括号中的语句要并行多线程执行;

利用#pragma omp section 分配线程。

看懂下列helloworld的代码对openmp并行就会有一定了解。

 
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/30458.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【玩转Linux操作】Linux服务管理

&#x1f38a;专栏【玩转Linux操作】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【如愿】 大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 文章目录 &#x1f354;服务(service)管理⭐service管理指令 &…

chatgpt赋能python:Python如何快速提取指定行和列的数据?

Python如何快速提取指定行和列的数据&#xff1f; 在进行数据分析和处理时&#xff0c;常常需要从海量数据中筛选出所需的数据。这时&#xff0c;Python是一款非常强大的工具&#xff0c;可以方便地进行大规模数据清洗和筛选。本文将介绍如何使用Python快速提取指定行和列的数…

chatgpt赋能python:Python提取指定位置字符

Python 提取指定位置字符 Python 是一种高级程序语言&#xff0c;其易读性、简单易学性和易维护性使其成为最受欢迎的编程语言之一。它可以用于各种数据分析和科学计算&#xff0c;包括搜索引擎优化&#xff08;SEO&#xff09;。 在SEO中&#xff0c;提取和处理数据是一个重…

监听关闭浏览器触发事件

关闭和刷新页面都会触发&#xff0c;一般都不用来做弹窗提示&#xff0c;一般用来做数据操作 // 监听页面关闭 清除本地缓存 window.onbeforeunload function (e) { localStorage.removeItem("statement"); }; // 监听页面关闭 提醒是否关闭 现在不允许自定义内容了…

【深度学习】5-1 与学习相关的技巧 - 参数的更新(Momentum,AdaGrad, Adam )

神经网络的学习的目的是找到使损失函数的值尽可能小的参数。这是寻找最优参数的问题&#xff0c;解决这个问题的过程称为最优化。 但是神经网络的最优化问题非常难。这是因为参数空间非常复杂&#xff0c;无法轻易找到最优解。而且&#xff0c;在深度神经网络中&#xff0c;参…

selenium.chrome怎么写扩展拦截或转发请求?

Selenium WebDriver 是一组开源 API&#xff0c;用于自动测试 Web 应用程序&#xff0c;利用它可以通过代码来控制chrome浏览器&#xff01; 有时候我们需要mock接口的返回&#xff0c;或者拦截和转发请求&#xff0c;今天就来实现这个功能。 代码已开源&#xff1a; https:/…

9k字长文理解Transformer: Attention Is All You Need

作者&#xff1a;猛码Memmat 目录 Abstract1 Introduction2 Background3 Model Architecture3.1 Encoder and Decoder Stacks3.2 Attention3.2.1 Scaled Dot-Product Attention3.2.2 Multi-Head Attention3.2.3 Applications of Attention in our Model 3.3 Position-wise Feed…

C++基础(6)——类和对象(运算符重载)

前言 本文主要介绍了C中运算符重载的基本知识。 4.5.1&#xff1a;加号运算符重载&#xff08;成员函数和全局函数都可实现&#xff09; 运算符重载&#xff1a;对已有的运算符重新进行定义&#xff0c;赋予其另一种功能&#xff0c;以适应不同的数据类型 1&#xff1a;成员…

防火墙日志记录和监控在网络安全中的重要性

防火墙监视进出网络的流量&#xff0c;并保护部署网络的网络免受恶意流量的侵害。它是一个网络安全系统&#xff0c;根据一些预定义的规则监控传入和传出的流量。它以日志的形式记录有关如何管理流量的信息。日志数据包含流量的源和目标 IP 地址、端口号、协议等。为了有效地保…

Git系列:运用Git创建空白分支进行项目相关文档管理

文章目录 起因一、为什么会选择Git分支二、Git分支的简单介绍和好处三、本次的具体操作1.$git checkout --orphan XXX2.删除当前分支里的内容3.提交新的分支 总结 起因 项目管理过程中没有做好相关文档管理&#xff0c;比如需求&#xff0c;开发&#xff0c;测试等文档&#x…

科技云报道:大模型时代,AI基础软件机会何在?

科技云报道原创。 大模型时代&#xff0c;离不开算力&#xff0c;算法、数据的喂养。如果将视角放至整个产业链上&#xff0c;算法背后&#xff0c;还有一个关键要素值得被关注&#xff0c;那就是AI基础软件。 算法是实现AI功能的关键&#xff0c;而基础软件则为算法提供运行…

React项目引入Arco Design,以及Arco Design Pro 架构

创建项目 创建 react-arco 项目 pnpm create vite my-vue-app --template react安装 arco-design/web-react 安装 react 版的 arco-design 基础使用 添加一个按钮&#xff0c;App.tsx import "./App.css"; import { Button } from "arco-design/web-react…

CH2023、Adobe Character Animator 2023(动画角色制作软件)下载教程、安装教程

最后附下载地址 Adobe CH简介&#xff1a; Adobe Character Animator是一款基于动画制作的软件&#xff0c;它可以将手绘的角色通过摄像头或麦克风捕捉到的实时动作转化为动画效果。该软件结合了人工智能和动画技术&#xff0c;可以快速创建高质量的角色动画&#xff0c;并且…

2023年的深度学习入门指南(17) - 深度学习的硬件加速技术

2023年的深度学习入门指南(17) - 深度学习的硬件加速技术 有了前面的知识之后&#xff0c;想必大家对于算力需求的理解已经越来越深刻了。 除了使用CPU&#xff0c;GPU这样的通用器件之外&#xff0c;采用专用的硬件来进行加速是一个大家都能想到的选择。 其中的代表器件就是…

杂记 | 使用Docker和Nginx为网站添加HTTPS访问功能

文章目录 01 准备工作1.1 HTTPS介绍1.2 准备工作 02 编写nginx.conf03 使用docker启动nginx 01 准备工作 1.1 HTTPS介绍 HTTPS&#xff08;Hypertext Transfer Protocol Secure&#xff09;是一种通过加密通信保护网站数据传输的协议。它是 HTTP 协议的安全版本&#xff0c;通…

1.4 掌握Scala运算符

一、运算符等价于方法 &#xff08;一&#xff09;运算符即方法 op运算符与.op方法调用是等价的&#xff0c;op表示运算符&#xff1a;、-、*、/…… 演示x y与x.(y)的等价 &#xff08;二&#xff09;方法即运算符 1、单参方法 str.indexOf(‘a’) 与 str indexOf ‘a’…

stable-diffusion-webui的介绍与使用——Controlnet1.1

源码地址&#xff1a;https://github.com/lllyasviel/ControlNet | 最新版本 controlnet-v1.1 论文地址&#xff1a;2302.Adding Conditional Control to Text-to-Image Diffusion Models 扩展UI地址&#xff08;需先安装sd-webui&#xff09;&#xff1a;https://github.com/M…

【gcc, cmake, eigen, opencv,ubuntu】四.opencv安装和使用,获取opencv matiax 的指针

文章目录 ubuntu系统安装opencv1.下载opencv和opencv_contrib2.安装指导3.Linux 下 fatal error: opencv2/opencv.hpp: 没有那个文件或目录4.g 和cmake 编译使用opencv的程序5.opencv,eigen速度比较6.opencv常用类型符号7.获取opencv matiax 的指针 ubuntu系统安装opencv 1.下…

设计模式大全

使用设计模式的目的&#xff1a; 程序猿在编码的过程中面临着来自耦合性、内聚性、可维护性、可扩展性、重用性、灵活性等多方面的挑战。设计模式是为了让程序具有更好的&#xff1a; 1&#xff09;重用性&#xff0c;即相同功能的代码编写一次即可&#xff0c;不用重复编写 …

史上最全Hadoop面试题:尼恩大数据面试宝典专题1

说在前面&#xff1a; 《尼恩 大数据 面试宝典》 是 《尼恩Java面试宝典》 姊妹篇。 这里特别说明一下&#xff1a;《尼恩Java面试宝典》41个专题 PDF &#xff08;请在文末获取&#xff09;自发布以来&#xff0c; 已经收集了 好几千题&#xff0c; 足足4000多页&#xff0c…