Ubuntun搭建并行计算环境
实验一、Linux环境下算法的测试
1、卡布列克常数6174
原理过程
验证卡布列克运算,任意一个四位数,只要它们各个位上的数字是不全相同的,就有这样的规律:
(1)将组成该四位数的四个数字由大到小排列,形成由这四个数字构成的最大的四位数;
(2)将组成该四位数的四个数字由小到大排列,形成由这四个数字构成的最小的四位数(如果四个数中含有0,则得到的数不足四位);
(3)求两个数的差,得到一个新的四位数(高位零保留)。
重复以上过程,最后得到的结果是6174,这个数被称为卡布列克数。例如:
4321-1234=3087
8730-378=8352
8532-2358=6174
7641-1467=6174
代码实现
c
#include <stdio.h>
int count = 0;
void vr6174(int num);
void sort(int num, int *each);
void max_min(int *each, int *max, int *min);
int main()
{
int n;
printf("Please input n: ");
scanf("%d", &n);
printf("Output:\n");
if (n > 9999 || n == 0)
{
printf("Input error!\n");
return 0;
}
vr6174(n);
return 0;
}
void vr6174(int num)
{
int each[4], max, min;
if (num != 6174 && num)
{
sort(num, each);
max_min(each, &max, &min);
num = max - min;
printf("[%d]: %d-%d=%d\n", ++count, max, min, num);
vr6174(num);
}
}
void sort(int num, int *each)
{
int i, *j, *k, temp;
for (i = 0; i <= 4; i++)
{
j = each + 3 - i;
*j = num % 10;
num /= 10;
}
for (i = 0; i < 3; i++)
{
for (j = each, k = each + 1; j <= each + 3 - 1; j++, k++)
if (*j > *k)
{
temp = *j;
*j = *k;
*k = temp;
}
}
}
void max_min(int *each, int *max, int *min)
{
int *i;
*min = 0;
for (i = each; i < each + 4; i++)
*min = *min * 10 + *i;
*max = 0;
for (i = each + 3; i >= each; i--)
*max = *max * 10 + *i;
}
2、蒙特卡洛算法
原理过程
1.首先,我们定义了一个宏NUM_POINTS,表示要生成的随机点的数量。这个值越大,估计的精度越高,但计算时间也越长。
2.我们使用rand()函数生成两个0到1之间的随机数,分别表示点的x和y坐标。
3.判断这个点是否在单位圆内。如果点到原点的距离小于等于1,那么它就在单位圆内。这里我们使用了勾股定理来计算距离:sqrt(x^2 + y^2) <= 1。
4.如果点在单位圆内,我们将points_inside_circle计数器加1。
5.最后,我们用落在单位圆内的点数除以总点数,然后乘以4,得到π的估计值。这是因为单位正方形的面积是1,而单位圆的面积是π/4,所以落在圆内的点数与总点数的比例应该接近π/4。乘以4后,我们就得到了π的估计值。
算法实验
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define NUM_POINTS 1000000
int main() {
int points_inside_circle = 0;
double x, y;
srand(time(NULL)); // 初始化随机数生成器
for (int i = 0; i < NUM_POINTS; i++) {
x = (double)rand() / RAND_MAX; // 生成0到1之间的随机数
y = (double)rand() / RAND_MAX; // 生成0到1之间的随机数
if (x * x + y * y <= 1) {
points_inside_circle++; // 如果点在单位圆内,计数器加1
}
}
double pi_estimate = 4.0 * points_inside_circle / NUM_POINTS;
printf("Estimated value of Pi: %f\n", pi_estimate);
return 0;
}
3、冰雹猜想验证(验证范围1~100)
原理过程
代码实现
#include <stdio.h>
void collatz(int n) {
if (n == 1) {
return;
} else if (n % 2 == 0) {
collatz(n / 2);
} else {
collatz(3 * n + 1);
}
}
int main() {
for (int i = 1; i <= 100; i++) {
printf("Number: %d\n", i);
collatz(i);
printf("\n");
}
return 0;
}
4、亲和数验证(验证范围1~10000)
原理过程
代码实现
c
#include <stdio.h>
int sum_of_divisors(int n) {
int sum = 1;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
sum += i;
if (i != n / i) {
sum += n / i;
}
}
}
return sum;
}
int main() {
for (int a = 1; a <= 10000; a++) {
int b = sum_of_divisors(a);
if (b > a && sum_of_divisors(b) == a) {
printf("Amicable numbers: %d and %d\n", a, b);
}
}
return 0;
}
实验二、MPI分布式运行框架搭建
1、前置操作
1.1、静态IP配置
/etc/netplan/
文件下的网络配置文件
yaml
network:
version: 2
ethernets:
ens33: # 请将 ens33 替换为您的实际接口名称
dhcp4: no
addresses:
- 192.168.60.181/24 # 静态 IP 地址和子网掩码
routes:
- to: default
via: 192.168.60.2 # 默认网关地址
nameservers:
addresses:
- 8.8.8.8
- 8.8.4.4 # DNS 服务器地址
需要改正的参数
- enp0s3:使用
ip a
查看网卡地址 - addresse:改变需要配置的IP地址
- via:更改网关
刷新配置
sudo netplan apply
我的配置
01-netcfg.yaml
yaml
network:
version: 2
ethernets:
ens33:
dhcp4: no
addresses:
- 192.168.60.181/24
routes:
- to: default
via: 192.168.60.2
nameservers:
addresses:
- 8.8.8.8
- 8.8.4.4
动态IP的配置
yaml
network:
ethernets:
ens33:
dhcp4: true
version: 2
1.2、虚拟机配置静态IP
vmware虚拟机设置静态ip并且通过xshell连接参考:https://www.cnblogs.com/qq1141100952com/p/15745106.html
1.3、构建互相免密钥访问(使用rsa方式生成)
构建四台计算节点之间互相免密钥访问(使用rsa方式生成)
先确定/etc/hosts
文件配置了node1、node2、node3、node4的
192.168.60.181 node01
192.168.60.182 node02
192.168.60.183 node03
192.168.60.184 node04
构建秘钥
ssh-keygen -t rsa -b 2048
赋值秘钥到其他节点上
ssh-copy-id root@node2
ssh-copy-id root@node3
ssh-copy-id root@node4
我的
ssh-copy-id peng@node02
ssh-copy-id peng@node01
ssh-copy-id peng@node03
2、安装NFS服务器
https://blog.csdn.net/ssp584731180/article/details/131113101
2.1、安装 NFS 和 NFS Utils
bash
sudo apt update
sudo apt install -y nfs-kernel-server nfs-common
在 node01 上配置 NFS 服务
创建共享目录 /var/mpi4.0/
(如果不存在):
bash
sudo mkdir -p /var/mpi4.0/
修改 NFS 配置文件,允许其他节点挂载该目录:
打开 /etc/exports
文件并添加如下内容:
bash
/var/mpi4.0/ node02(rw,sync,no_subtree_check) node03(rw,sync,no_subtree_check) node04(rw,sync,no_subtree_check)
这行配置表示允许 node02
、node03
和 node04
挂载该目录,并授予读写权限。
启动并使 NFS 服务开机自启:
bash
sudo systemctl enable --now nfs-server
应用配置:
bash
sudo exportfs -ra
验证共享:
bash
sudo exportfs -v
2.2、在其他节点配置NFS
创建挂载目录 /var/mpi4.0/
(与主节点路径一致):
bash
sudo mkdir -p /var/mpi4.0/
需要配置权限
bash
sudo chmod 777 /var/mpi4.0
配置自动挂载:
打开 /etc/fstab
文件,添加以下内容
bash
192.168.1.1:/var/mpi4.0/ /var/mpi4.0/ nfs defaults 0 0
此行配置会将 node01
的 /var/mpi4.0/
目录挂载到本地的 /var/mpi4.0/
,并设置开机自动挂载。
挂载目录
bash
sudo mount -a
**验证挂载是否成功 **
bash
df -h
应该能看到挂载在 /var/mpi4.0/
的共享目录。
3、安装和测试MPI
3.1、安装MPI
下载MPI
wget https://www.mpich.org/static/downloads/4.0/mpich-4.0.tar.gz
解压
tar -xvf mpich-4.0.tar.gz
FCFLAGS=-fallow-argument-mismatch ./configure --prefix=/var/mpi4.0
./configure --prefix=/var/mpi4.0 --disable-fortran
make
sudo make install
配置环境变量
vi ~/.bashrc
# .bashrc
# User specific aliases and functions
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
PATH="$PATH:/var/mpi4.0/bin" # 新增路径
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
刷新
source ~/.bashrc
验证路径是否正确配置
which mpicc
which mpiexec
创建主机名称集合文件
在 /var/mpi4.0/bin
下创建一个 csmpd.hosts
文件,内容如下:
node01
node02
node03
node04
测试(我的在)
在 /var/mpi4.0/examples/
目录下可以找到一些示例程序。编译并运行其中一个:
mpicc -o my_mpi_example /var/mpi4.0/examples/cpi.c
运行时指定主机文件 csmpd.hosts
:
mpiexec -np 4 -f /var/mpi4.0/bin/csmpd.hosts ./my_mpi_example
mpiexec -n 4 -f /var/mpi4.0/bin/csmpd.hosts /var/mpi4.0/code/hello
3.2、测试MPI
c
/*hello.c*/
#include <stdio.h>
#include "mpi.h"
int main( int argc, char *argv[] )
{
int rank;
int size;
MPI_Init( 0, 0 );
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
printf( "Hello world from process %d of %d\n", rank, size );
MPI_Finalize();
return 0;
}
编译
mpicc --o hello hello.c
运行
$mpiexec --n <processes> ./hello
$mpiexec -f csmpd.hosts --n <processes> ./hello
实验三、运行5.4、5.5、5.7、5.8
程序5.4
c
/*文件名:who.c*/
#include "mpi.h"
#include <stdio.h>
int main(int argc,char **argv)
{
int myid, numprocs;
int namelen;
char processor_name[MPI_MAX_PROCESSOR_NAME];
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);//获得本进程ID
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);//获得总的进程数目
MPI_Get_processor_name(processor_name,&namelen);//获得本进程的机器名
printf("Hello World! Process %d of %d on %s\n",myid, numprocs, processor_name);
MPI_Finalize();
}
程序5.5
c
/*文件名:message.c*/
#include <stdio.h>
#include "mpi.h"
int main(int argc, char** argv)
{
int myid, numprocs, source;
MPI_Status status;
char message[100];
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
if (myid != 0)
{
strcpy(message, "Hello World!");//为发送字符串赋值
//发送字符串时长度要加1,从而包括串结束标志
MPI_Send(message,strlen(message)+1, MPI_CHAR, 0,99,MPI_COMM_WORLD);
}
else
{
//除0进程的其他进程接收来自于0进程的字符串数据
for (source = 1; source < numprocs; source++)
{
MPI_Recv(message, 100, MPI_CHAR, source, 99,MPI_COMM_WORLD, &status);
printf("I am process %d. I recv string '%s' from process %d.\n", myid, message,source);
}
}
MPI_Finalize();
}
程序5.7
c
/*文件名 inte.c*/
#define N 100000000
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "mpi.h"
int main(int argc, char** argv)
{
int myid,numprocs;
int i;
double local=0.0;
double inte,tmp=0.0,x;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
srand((int)time(0));//设置随机数种子
/*各节点分别计算一部分积分值*/
/*以下代码在不同节点运行的结果不同*/
for(i=myid;i<N;i=i+numprocs)
{
x=10.0*rand()/(RAND_MAX+1.0);//求函数值
tmp=x*x/N;
local=tmp+local;//各节点计算面积和
}
//计算总的面积和,得到积分值
MPI_Reduce(&local,&inte,1,MPI_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD);
if(myid==0)
{
printf("The integal of x*x=%16.15f\n",inte);
}
MPI_Finalize();
}
程序5.8
c
/*文件名 myreduce.c*/
#define N 100000000
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "mpi.h"
void Myreduce(double *sendbuf, double *recvbuf,int count,int root);//定义自己的reduce函数
int main(int argc, char** argv)
{
int myid,numprocs;
int i;
double local=0.0;
double inte,tmp=0.0,x;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
/*采用归约对y=x*x在[1,10]区间求积分*/
srand((int)time(0));
for(i=myid;i<N;i=i+numprocs)
{
x=10.0*rand()/(RAND_MAX+1.0);
tmp=x*x/N;
local=tmp+local;
}
Myreduce(&local,&inte,1,0);//调用自定义的规约函数
if(myid==0)
{
printf("The integal of x*x=%16.15f\n",inte);
}
MPI_Finalize();
}
/*自定义的归约函数,sendbuf为发送缓冲区,recvbuf为接收缓冲区,count为数据个数,root为指定根节点*/
/*该函数实现归约求和的功能*/
void Myreduce(double *sendbuf,double *recvbuf,int count,int root)
{
MPI_Status status;
int i;
int myid,numprocs;
*recvbuf=0.0;
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
double *tmp;
//非root节点向root节点发送数据
if(myid!=root)
{
MPI_Send(sendbuf,count,MPI_DOUBLE,root,99,MPI_COMM_WORLD);
}
//root节点接收数据并对数据求和,完成规约操作
if(myid==root)
{
*recvbuf=*sendbuf;
for(i=0;i<numprocs;i++)
{
if(i!=root)
{
MPI_Recv(tmp,count,MPI_DOUBLE,i,99,MPI_COMM_WORLD,&status);
*recvbuf=*recvbuf+*tmp;
}
}
}
}
实验四、卡布列克常数的MPI测试
思路
初始化 MPI 环境 :使用 MPI_Init
初始化 MPI 环境,并获取每个节点的 rank
和总节点数 size
。
分配任务 :根据 rank
和 size
将四位数区间分割给不同的节点。比如,如果是 4 个节点,可以让每个节点负责一个子区间。
卡布列克运算:
- 将当前数字分解成 4 位数的数组。
- 排序并生成最大数和最小数。
- 计算两者的差值,如果差值不为 6174,重复操作,直到达到 6174 或步骤达到上限。
输出结果:当一个数达到了 6174,打印步骤,结果,以及这个数所需的步骤数。
汇总与退出:所有节点完成计算后,MPI 程序结束,释放资源。
代码
kaprekar_mpi.c
c
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TARGET 6174
// 对数字分解成千位、百位、十位和个位
void split_digits(int num, int digits[4]) {
digits[0] = num / 1000;
digits[1] = (num / 100) % 10;
digits[2] = (num / 10) % 10;
digits[3] = num % 10;
}
// 排序数字数组
void sort_digits(int digits[4], int ascending) {
for (int i = 0; i < 3; i++) {
for (int j = i + 1; j < 4; j++) {
if ((ascending && digits[i] > digits[j]) || (!ascending && digits[i] < digits[j])) {
int temp = digits[i];
digits[i] = digits[j];
digits[j] = temp;
}
}
}
}
// 生成最大和最小数字
int form_number(int digits[4]) {
return digits[0] * 1000 + digits[1] * 100 + digits[2] * 10 + digits[3];
}
// 卡布列克猜想的核心算法
int kaprekar_routine(int num) {
int steps = 0;
while (num != TARGET && steps < 7) {
int digits[4];
split_digits(num, digits);
sort_digits(digits, 0); // 降序排列
int large = form_number(digits);
sort_digits(digits, 1); // 升序排列
int small = form_number(digits);
num = large - small;
steps++;
}
return steps;
}
int main(int argc, char *argv[]) {
MPI_Init(&argc, &argv);
int rank, size;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
int start = 1000 + (9000 / size) * rank;
int end = (rank == size - 1) ? 9999 : start + (9000 / size) - 1;
printf("Node %d processing range %d to %d\n", rank, start, end);
for (int i = start; i <= end; i++) {
if (i % 1111 == 0) continue; // 排除全相同的数
int steps = kaprekar_routine(i);
if (steps <= 7) {
printf("Node %d: %d reaches 6174 in %d steps\n", rank, i, steps);
}
}
MPI_Finalize();
return 0;
}
split_digits
:将数字分解为千、百、十、个位。sort_digits
:对数字数组进行排序,用于生成最大和最小数字。form_number
:将分解后的数字数组重新组合成整数。kaprekar_routine
:核心算法,计算每个四位数到达 6174 所需的步骤数。MPI_Comm_rank
和MPI_Comm_size
:获取当前节点的编号和节点总数,以分配任务。
运行
-
先安装 MPI(例如使用
mpich
或openmpi
)。 -
编译代码:
mpicc -o kaprekar_mpi kaprekar_mpi.c
-
运行代码(假设使用 4 个节点):
mpirun -np 4 ./kaprekar_mpi
实验五、基于蒙特卡洛算法求π值的MPI程序设计
思路
代码
以下代码实现了基于蒙特卡洛方法估算 π 值的 MPI 并行程序。程序将随机点生成和圆内点的计数分配给多个进程,每个进程独立计算局部结果,并将其汇总到主进程以得到最终 π 值。
c
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define TOTAL_POINTS 1000000 // 每个进程要生成的总点数,可以根据需要调整
int main(int argc, char **argv) {
int myid, numprocs;
long count = TOTAL_POINTS;
long local_m = 0, global_m = 0; // 局部和全局的圆内点数
double x, y;
double local_pi, pi_estimate;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myid); // 获取当前进程的进程号
MPI_Comm_size(MPI_COMM_WORLD, &numprocs); // 获取通信域中的总进程数
srand(time(NULL) + myid); // 设置随机种子,确保每个进程生成不同随机序列
// 每个进程生成随机点并统计在单位圆内的点数
for (long i = 0; i < count; i++) {
x = (double)rand() / RAND_MAX;
y = (double)rand() / RAND_MAX;
if ((x * x + y * y) <= 1) {
local_m++;
}
}
// 计算局部 π 值
local_pi = 4.0 * local_m / count;
printf("Process %d of %d on pi = %f\n", myid, numprocs, local_pi);
// 使用 MPI_Reduce 汇总所有进程的圆内点数
MPI_Reduce(&local_m, &global_m, 1, MPI_LONG, MPI_SUM, 0, MPI_COMM_WORLD);
// 主进程计算总 π 值
if (myid == 0) {
pi_estimate = 4.0 * global_m / (count * numprocs);
printf("Estimated value of π: %f\n", pi_estimate);
}
MPI_Finalize();
return 0;
}
- 设置随机数 :每个进程通过
srand
设置不同的随机数种子,以确保生成的随机点不同。 - 蒙特卡洛方法计算 π 值
- 每个进程独立生成
TOTAL_POINTS
个随机点(x, y)
。 - 判断点是否在单位圆内,并统计圆内点数
local_m
。
- 每个进程独立生成
- MPI 汇总
- 使用
MPI_Reduce
将各进程的local_m
汇总到global_m
。
- 使用
- 结果计算
- 主进程 (
myid == 0
) 使用汇总的圆内点数计算 π 的近似值,并打印结果。
- 主进程 (
运行
将代码保存为 mtpi.c
,然后在终端中进行编译和运行:
bash
# 编译代码
mpicc -o mtpi mtpi.c
# 使用 4 个进程运行
mpirun -np 4 ./mtpi
实验六、使用 MPI 技术验证角谷猜想
思路
角谷猜想(Collatz conjecture)是一个数学猜想,对任何一个正整数 n
,若 n
为偶数,则把它除以 2,若 n
为奇数,则把它乘以 3 再加 1,得到一个新的数。对新的数重复上述步骤,最终总会得到 1。
代码
c
#include <mpi.h>
#include <stdio.h>
void verify_collatz(long n) {
while (n != 1) {
if (n % 2 == 0) {
n = n / 2;
} else {
n = 3 * n + 1;
}
}
}
int main(int argc, char **argv) {
int myid, numprocs;
long start, end, range;
long i;
// 设置验证数据的范围
long min_range = 1;
long max_range = 100000; // 可根据需求增大此数值,验证更大范围的数
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myid); // 获取当前进程号
MPI_Comm_size(MPI_COMM_WORLD, &numprocs); // 获取进程总数
// 将范围划分给各个进程
range = (max_range - min_range + 1) / numprocs;
start = min_range + myid * range;
end = (myid == numprocs - 1) ? max_range : start + range - 1;
printf("Process %d is verifying numbers from %ld to %ld\n", myid, start, end);
// 遍历每个数并验证角谷猜想
for (i = start; i <= end; i++) {
verify_collatz(i);
}
// 所有进程完成计算后输出结果
if (myid == 0) {
printf("Verification of the Collatz conjecture from %ld to %ld completed.\n", min_range, max_range);
}
MPI_Finalize();
return 0;
}
verify_collatz
函数执行角谷猜想的验证过程。
主程序中,各个 MPI 进程会分配到一个范围的数字,分别验证这些数字是否满足角谷猜想。
各进程完成计算后,主进程(myid == 0
)输出验证完成的消息。
运行
编译代码
bash
mpicc -o collatz collatz.c
运行代码
bash
mpirun -np 4 ./collatz
实验七、使用 MPI 技术验证亲和数组合的个数
思路
亲和数组合指的是满足一定条件的数对或数集合。这里的实验将并行计算所有可能的亲和数组合数量,通过划分数据范围来分配到不同的计算节点,并验证这些组合满足的特定亲和条件。
代码
c
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
// 判断是否为亲和数组合
int is_amicable(int a, int b) {
int sum_a = 0, sum_b = 0;
for (int i = 1; i <= a / 2; i++) {
if (a % i == 0) sum_a += i;
}
for (int i = 1; i <= b / 2; i++) {
if (b % i == 0) sum_b += i;
}
return (sum_a == b && sum_b == a);
}
int main(int argc, char **argv) {
int myid, numprocs;
long range_start, range_end, total_pairs = 0, local_pairs = 0;
long range = 100000; // 大范围以延长运行时间
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myid); // 获取当前进程号
MPI_Comm_size(MPI_COMM_WORLD, &numprocs); // 获取进程总数
// 划分数据范围
long range_per_proc = range / numprocs;
range_start = myid * range_per_proc + 1;
range_end = (myid == numprocs - 1) ? range : range_start + range_per_proc - 1;
printf("Process %d is calculating from %ld to %ld\n", myid, range_start, range_end);
// 计算亲和数组合数量
for (long a = range_start; a <= range_end; a++) {
for (long b = a + 1; b <= range; b++) {
if (is_amicable(a, b)) {
local_pairs++;
}
}
}
// 汇总所有进程的结果
MPI_Reduce(&local_pairs, &total_pairs, 1, MPI_LONG, MPI_SUM, 0, MPI_COMM_WORLD);
// 主进程输出结果
if (myid == 0) {
printf("Total number of amicable pairs in the range: %ld\n", total_pairs);
}
MPI_Finalize();
return 0;
}
is_amicable
函数用于判断两个数是否为亲和数组合。若数 a
的因子和等于 b
,且数 b
的因子和等于 a
,则这两个数为亲和数组合。
主程序中,每个 MPI 进程被分配到一段范围来检查亲和数组合。
最终,通过 MPI_Reduce
汇总各进程的亲和数组合总数。
为了确保运行时间超过 20 分钟,可以适当调大 range
的值(例如,增加至 500000 或更大),从而增加计算复杂度和运行时间。
运行
编译代码
mpicc -o amicable_pairs amicable_pairs.c
运行代码
mpirun -np 4 ./amicable_pairs
使用Docker搭建并行计算环境
1、拉取镜像
docker pull ubuntu:22.04
2、启动镜像
docker run -d --name node1 -p 30001:22 ubuntu:22.04 sleep infinity
docker run -d --name node2 -p 30001:22 ubuntu:22.04 sleep infinity
docker run -d --name node3 -p 30001:22 ubuntu:22.04 sleep infinity
docker run -d --name node4 -p 30001:22 ubuntu:22.04 sleep infinity
查看运行中的容器
docker ps
进入节点
docker exec -it node1 /bin/bash
3、安装相关软件
安装ssh服务
apt install -y openssh-server
service ssh start
确保主机上的防火墙没有阻止访问映射的端口(如 2222
)。你可以使用以下命令查看防火墙状态:
-
对于
iptables
:iptables -L
-
对于
ufw
(如果使用):ufw status
如果端口被阻止,可以添加规则以允许访问:
ufw allow 2222
确保 SSH 配置文件 /etc/ssh/sshd_config
中允许使用密码进行身份验证:
PermitRootLogin yes
PasswordAuthentication yes
修改后,重启 SSH 服务以使更改生效:
service ssh restart
修改ubuntu的密码,修改密码后然后就可以使用xshell登录了。
passwd root
安装vim【可选】
apt install vim
安装net-tools【可选】
sudo apt install net-tools
安装iproute2【可选】
apt install -y iproute2
安装curl和wget【可选】
apt install -y curl
apt install -y wget
4、使用XShell连接
使用XShell批量连接节点