文章目录
- 背景
- 要求
- 代码
-
- 顺序代码
- 并行代码
- 在线体验
背景
生命游戏中,对于任意细胞,规则如下:
- 每个细胞有两种状态-存活或死亡,每个细胞与以自身为中心的周围八个细胞产生互动。
- 当前细胞为存活状态时,当周围低于2个(不包含2个)存活细胞时, 该细胞变成死亡状态。(模拟生命数量稀少)
- 当前细胞为存活状态时,当周围有2个或3个存活细胞时, 该细胞保持原样。
- 当前细胞为存活状态时,当周围有3个以上的存活细胞时,该细胞变成死亡状态。(模拟生命数量过多)
- 当前细胞为死亡状态时,当周围有3个存活细胞时,该细胞变成存活状态。 (模拟繁殖)
本实验中,需要在一个平面上使用细胞自动机模型实现生命游戏模拟。可以把最初的细胞结构定义为种子,当所有在种子中的细胞同时被以上规则处理后, 可以得到第一代细胞图。按规则继续处理当前的细胞图,可以得到下一代的细胞图,周而复始。
要求
- 设定平面大小至少为2020(WidthHeight),具体数值可以自行设定。
- 实现顺序代码后,使用MPI实现一个简单的并行情况代码。
代码
顺序代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 42
#define M 72
//平面大小40×70,周围一圈作为边界
char cell = '@'; //细胞形状
void init(char cells[N][M]);
void show(char cells[N][M]);
void evolve(char cells[N][M]);
int around(char cells[N][M], int x, int y);
int main(int argc, char *argv[])
{
if(argc != 2)
{ //用户输入代数
printf("Usage:%s number\n", argv[0]);
return 1;
}
char cells[N][M];
int i = 0, G=atoi(argv[1]);
init(cells);
show(cells);
while(i++ < G) {
sleep(1);
evolve(cells);
show(cells);
printf("\ngeneration %d\n",i);
}
return 0;
}
void init(char cells[N][M])
{ //随机初始化培养皿
int i,j;
srand(time(0));
for(i=0;i<N;i++)
for(j=0;j<M;j++)
if(rand()%2 || 0==i || 0==j
|| N-1 == i || M-1 == j)
cells[i][j]=' ';
else
cells[i][j]=cell;
}
void show(char cells[N][M])
{
int i,j;
system("clear");
for(i=0;i<N;i++) {
for(j=0;j<M;j++)
putchar(cells[i][j]);
putchar('\n');
}
}
void evolve(char cells[N][M])
{ //细胞进化,实际平面要大一圈,作为培养皿壁,方便统一
int i,j, lives;
char tmp[N][M];
for(i=1;i<N-1;i++) {
for(j=1;j<M-1;j++) {
lives = around(cells, i, j);
if(cell == cells[i][j]){
lives--;
if(lives < 2 || lives > 3)
tmp[i][j]=' ';
else
tmp[i][j]=cell;
}else if(lives == 3)
tmp[i][j]=cell;
else
tmp[i][j]=' ';
}
}
for(i=1;i<N-1;i++)
for(j=1;j<M-1;j++)
cells[i][j]=tmp[i][j];
}
int around(char cells[N][M], int x, int y)
{ //一个细胞周围细胞个数
int i,j,count=0;
for(i=x-1;i<x+2;i++)
for(j=y-1;j<y+2;j++)
if(cells[i][j]==cell)
count++;
return count;
}
并行代码
#include <stdio.h>
#include <stdlib.h>
#include "mpi.h"
#include <time.h>
#define N 42
#define M 72
#define G 30
//代数为30
char cell = '@';
void init(char cells[N][M]);
void show(char cells[N][M]);
void liner(char array[][M], char *line, int len, int check);
void matrix(char array[][M], char *line, int len);
void evolve(char cells[N][M], int n);
int around(char cells[N][M], int x, int y);
int main(int argc, char *argv[])
{
MPI_Init(&argc, &argv);
MPI_Status status;
int rank, size;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
int i,g=0, step=(N-2)/(size-1), flag = (N-2)%(size-1);
char line[(step+2)*M];
if(!rank){ //主进程
int from, length;
char cells[N][M];
if(flag){
from = size*step;
length = N-from;
}
init(cells);
show(cells);
while(g++ < G) {
sleep(1);
for(i=1;i<size;i++){
liner(&cells[(i-1)*step], line, step+2, 0);
MPI_Send(line, (step+2)*M, MPI_CHAR,
i, 0, MPI_COMM_WORLD);
}
for(i=1; i<size; i++){
MPI_Recv(line, step*M, MPI_CHAR,
i, 0, MPI_COMM_WORLD, &status);
matrix(&cells[(i-1)*step+1], line, step);
}
if(flag){
evolve(&cells[from], length);
}
show(cells);
printf("\ngeneration %d\n",g);
}
}else { //子进程
char buf[step+2][M];
while(g++ < G) {
sleep(1);
MPI_Recv(line, (step+2)*M, MPI_CHAR,
0, 0, MPI_COMM_WORLD, &status);
matrix(buf, line, step+2);
evolve(buf, step+2);
liner(buf, line, step+2, 1);
MPI_Send(line, step*M, MPI_CHAR,
0, 0, MPI_COMM_WORLD);
}
}
MPI_Finalize();
return 0;
}
void init(char cells[N][M])
{
int i,j;
srand(time(0));
for(i=0;i<N;i++)
for(j=0;j<M;j++)
if(rand()%2 || 0==i || 0==j
|| N-1 == i || M-1 == j)
cells[i][j]=' ';
else
cells[i][j]=cell;
}
void show(char cells[N][M])
{
int i,j;
system("clear");
for(i=0;i<N;i++) {
printf("%d ",i);
for(j=0;j<M;j++)
putchar(cells[i][j]);
putchar('\n');
}
}
void liner(char array[][M], char *line, int len, int check)
{ //将二维数组转换成一维数组
int i,j;
for(i=0+check;i<len-check;i++){
for(j=0;j<M;j++){
*line++=array[i][j];
}
}
}
void matrix(char array[][M], char *line, int len)
{ //将一维数组转换成二维数组
int i,j;
for(i=0;i<len;i++){
for(j=0;j<M;j++){
array[i][j]=*line++;
}
}
}
void evolve(char cells[N][M], int n)
{
int i,j, lives;
char tmp[n][M];
for(i=1;i<n-1;i++) {
for(j=1;j<M-1;j++) {
lives = around(cells, i, j);
if(cell == cells[i][j]){
lives--;
if(lives < 2 || lives > 3)
tmp[i][j]=' ';
else
tmp[i][j]=cell;
}else if(lives == 3)
tmp[i][j]=cell;
else
tmp[i][j]=' ';
}
}
for(i=1;i<n-1;i++)
for(j=1;j<M-1;j++)
cells[i][j]=tmp[i][j];
}
int around(char cells[N][M], int x, int y)
{
int i,j,count=0;
for(i=x-1;i<x+2;i++)
for(j=y-1;j<y+2;j++)
if(cells[i][j]==cell)
count++;
return count;
}
数据划分方法如下:
上图以p=4为例,主进程将数组按行分为3部分,每一部分与相邻的部分有两行重叠,分发给子进程,子进程对数据处理的时候空出最外面的一圈,只计算里面的数据。处理完之后只将处理之后的里面的数据发送给主进程。
在线体验
John Conway’s Game of Life是一个牛批的国外网站,可以在线感受一下生命游戏。