并行化实现生命游戏
题目描述:
生命游戏其实是一个零玩家游戏,它包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死了的细胞。一个细胞在下一个时刻生死取决于相邻八个方格中活着的或死了的细胞的数量。如果相邻方格活着的细胞数量过多,这个细胞会因为资源匮乏而在下一个时刻死去;相反,如果周围活细胞过少,这个细胞会因太孤单而死去。
具体规则如下:
1)如果一个细胞周围有3个细胞为生(一个细胞周围共有8个细胞),则该细胞为生(即该细胞若原先为死,则转为生,若原先为生,则保持不变) 。
2) 如果一个细胞周围有2个细胞为生,则该细胞的生死状态保持不变;
3) 在其它情况下,该细胞为死(即该细胞若原先为生,则转为死,若原先为死,则保持不变)
在程序中,使用0代表死,1代表生。
package main;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author LiuXingWu
* @create 2020-11-22 16:45
*/
public class ParallelGameOfLive {
private int threadCounts; // 线程数量
private CyclicBarrier barrier; // 障栅
private ExecutorService threadPool; // 线程池
private boolean isCovered = false; // 是否结束
private int[][] currentGeneration; // 当前代的细胞数组
private int[][] nextGeneration; // 下一代的细胞数组
private int evolutionaryGenerations; // 进化的代数
private int length; // 细胞数组的长度,细胞生活环境的边长, 生命游戏的格子数
private List<int[]> arrayOfAliveCell = new ArrayList<int[]>();
private String fileName; // 文件名称
// 构造函数
public ParallelGameOfLive(int threadCounts, String fileName){
this.threadCounts = threadCounts;
this.fileName = fileName;
this.readInfo(); // 从文件中读取内容
this.currentGeneration = new int[length][length]; // 初始化细胞生活的棋盘
this.nextGeneration = new int[length][length]; // 初始化棋盘
this.threadPool = Executors.newFixedThreadPool(threadCounts);
// 初始化细胞状态
for (int[] e : arrayOfAliveCell){
if (IsCoordinateValid(e[0], e[1])){
// 确定活着的细胞
currentGeneration[e[0]][e[1]] = 1;
}
}
}
// 进化
public void Evolution(){
long start = System.currentTimeMillis();
int beginX = 0;
int subLength = length / threadCounts; // 每个线程负责的区域大小为:subLength(x) * Length(y)
int endX = subLength;
for (int i = 0; i < threadCounts; i++) {
if (endX >= length) {
endX = length - 1;
}
threadPool.submit(new cellsEvolution(beginX, endX));
beginX = endX + 1;
endX += subLength;
}
threadPool.shutdown(); //isShutDown当调用shutdown()方法后返回为true。
while (!threadPool.isTerminated()); // isTerminated当调用shutdown()方法后,并且所有提交的任务完成后返回为true
long end0 = System.currentTimeMillis();
// System.out.println("生命游戏进化共用时:" + ((end0 - start) / 1000.00) + "秒," + "处理数据线程数:" + threadCounts);
getArrayOfAliveCell(); // 获取最终存活的细胞信息
WriteInfo(); // 将结果写入文件
long end1 = System.currentTimeMillis();
// System.out.println( "数据源来自:" + this.fileName + "文件,"
// + "整个程序运行共用时(包括将结果存入指定文件中):"
// + ((end1 - start) / 1000.00) + "秒
");
}
/**
* 内部类实现一代细胞进化的并行化实现
* 把棋盘竖切为多块
*/
public class cellsEvolution implements Runnable {
int beginX; // 开始X坐标
int endX; // 结束X坐标
public cellsEvolution(int beginX, int endX) {
this.beginX = beginX;
this.endX = endX;
}
@Override
public void run() {
for (int i = 0; i < evolutionaryGenerations; i++) {
isCovered = false;
cellsTransfrom(beginX, endX); // 细胞状态改变
try {
barrier.await(); // 等所有线程都到达栏栅,才开始继续进行,以便进行数据共享
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
synchronized (this) {
// 等所有线程同步后,进行换代
if (!isCovered) {
currentGeneration = nextGeneration;
nextGeneration = new int[length][length];
isCovered = true;
}
}
}
}
}
/**
* 细胞状态改变
* @param beginX
* @param endX
*/
public void cellsTransfrom(int beginX, int endX) {
for (int i = beginX; i < endX; i++) {
for (int j = 0; j < length; j++) {
singleCellEvolution(i, j); // 单细胞进化
}
}
}
// 检验指定的坐标是否合理
public boolean IsCoordinateValid(int x, int y){
return !((x < 0 || x >= length) || (y < 0 || y >= length) );
}
// 判断指定细胞在下一代的存活状况(单细胞进化)
public void singleCellEvolution(int x, int y){
int aliveSurroundingCells = 0; // 指定细胞周围存活的细胞数量
// 判断左上角的细胞
if(IsCoordinateValid(x - 1, y - 1)) {
if (currentGeneration[x - 1][y - 1] == 1){
aliveSurroundingCells++;
}
}
// 判断正上方的细胞
if(IsCoordinateValid(x, y - 1)) {
if (currentGeneration[x][y - 1] == 1) {
aliveSurroundingCells++;
}
}
// 判断右上角的细胞
if(IsCoordinateValid(x + 1, y - 1)) {
if (currentGeneration[x + 1][y - 1] == 1){
aliveSurroundingCells++;
}
}
// 判断左边的细胞
if(IsCoordinateValid(x - 1, y)) {
if (currentGeneration[x - 1][y] == 1){
aliveSurroundingCells++;
}
}
// 判断右边的细胞
if(IsCoordinateValid(x + 1, y)) {
if (currentGeneration[x + 1][y] == 1){
aliveSurroundingCells++;
}
}
// 判断左下角的细胞
if(IsCoordinateValid(x - 1, y + 1)) {
if (currentGeneration[x - 1][y + 1] == 1){
aliveSurroundingCells++;
}
}
// 判断正下方的细胞
if(IsCoordinateValid(x, y + 1)) {
if (currentGeneration[x][y + 1] == 1){
aliveSurroundingCells++;
}
}
// 判断右下角的细胞
if(IsCoordinateValid(x + 1, y + 1)) {
if (currentGeneration[x + 1][y + 1] == 1){
aliveSurroundingCells++;
}
}
// 根据指定细胞周围存活细胞的状况确定该细胞下下一代的存活情况
if (aliveSurroundingCells == 3){
// 周围有3个活细胞,该细胞无论死活在下一代都会活着
nextGeneration[x][y] = 1;
}else if (aliveSurroundingCells == 2){
// 周围有2个活细胞,该细胞保持当前状态到下一代
nextGeneration[x][y] = currentGeneration[x][y];
}else{
// 其他情况,细胞死亡
nextGeneration[x][y] = 0;
}
}
// 清空活细胞坐标数组
public void ClearArrayOfAliveCell(){
arrayOfAliveCell.clear();
}
public void getArrayOfAliveCell () {
arrayOfAliveCell.clear(); // 清空原有内容
for (int i = 0; i < length; i++) {
for (int j = 0; j < length; j++) {
if (currentGeneration[i][j] == 1) {
int[] temp = new int[2];
temp[0] = i;
temp[1] = j;
arrayOfAliveCell.add(temp);
}
}
}
}
/**
* 读取文件信息
*/
public void readInfo() {
ClearArrayOfAliveCell();
File file;
BufferedReader br = null;
String readData; // 读入的内容
String[] tempString = new String[2];
int line = 0;
try {
file = new File(this.getClass().getResource("").getPath() + this.fileName);
if (!file.exists()) {
file.createNewFile();
}
br = new BufferedReader(new FileReader(file));
while ((readData = br.readLine()) != null) {
line++;
tempString = readData.split(",");
if (line == 1) {
length = Integer.valueOf(readData);
} else if (line == 2) {
evolutionaryGenerations = Integer.valueOf(readData);
} else {
int[] temp = new int[2];
temp[0] = Integer.valueOf(tempString[0]);
temp[1] = Integer.valueOf(tempString[1]);
arrayOfAliveCell.add(temp);
}
}
} catch (IOException e0) {
e0.printStackTrace();
} finally {
try {
if (br != null) {
br.close();
}
}catch (IOException e1) {
e1.printStackTrace();
}
}
}
/**
* 将结果写入文件
*/
public void WriteInfo() {
File file;
BufferedWriter bw = null;
String writeData = "";
try {
file = new File( this.getClass().getResource("").getPath() + "resultOf" + this.fileName);
if (!file.exists()) {
System.out.println("文件不存在,创建文件");
file.createNewFile();
}
bw = new BufferedWriter(new FileWriter(file));
for (int[] i : arrayOfAliveCell) {
writeData += i[0] + "," + i[1] + "
";
}
//System.out.print(writeData);
bw.write(writeData);
} catch (IOException e0) {
e0.printStackTrace();
} finally {
try {
if (bw != null) {
bw.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
public void showInfo(){
if (arrayOfAliveCell.size() == 0) {
System.out.println("已无活细胞存在!");
} else {
for (int[] e : arrayOfAliveCell) {
System.out.println(e[0] + "," + e[1]);
}
}
}
public static void main(String[] args) {
long start1, start2, start3, end1, end2, end3;
start1 = System.currentTimeMillis();
ParallelGameOfLive game1 = new ParallelGameOfLive(1, "in_100.txt");
game1.Evolution(); // 进化
end1 = System.currentTimeMillis();
System.out.println("生命游戏进化共用时:" + ((end1 - start1) / 1000.00) + "秒," + "处理数据线程数:1
");
start2 = System.currentTimeMillis();
ParallelGameOfLive game2 = new ParallelGameOfLive(2, "in_100.txt");
game2.Evolution(); // 进化
end2 = System.currentTimeMillis();
System.out.println("生命游戏进化共用时:" + ((end2 - start2) / 1000.00) + "秒," + "处理数据线程数:2
");
start3 = System.currentTimeMillis();
ParallelGameOfLive game3 = new ParallelGameOfLive(4, "in_100.txt");
game3.Evolution(); // 进化
end3 = System.currentTimeMillis();
System.out.println("生命游戏进化共用时:" + ((end3 - start3) / 1000.00) + "秒," + "处理数据线程数:4
");
}
}