RCPSP问题作为一个NP-hard问题在规模较大的情况下很难直接求解,因此用遗传算法或者改进遗传算法求解是研究此问题的常用手段。

首先设置Activity类:将项目中的每个活动作为对象,储存各个项目的信息

import java.util.ArrayList;

/**
 * 活动类 包括 属性:1.代号 id 2.工期 duration 3.四种资源的需求量r1 r2 r3 r4 4.紧前活动集合 preActivities
 * 5.紧后活动集合 sucActivities 6.开始时间 es 7.结束时间ef 8.是否安排 visited 方法:1.
 * 检查所有紧前活动是否都加入到局部调度计划
 * 
 * @author 11400
 *
 */
public class Activity {

	public int id, duration, r1, r2, r3, r4, es, ef;
	public boolean visited;
	ArrayList<Activity> preActivities, sucActivities;

	public Activity() {
		super();
		this.id = -1;
		this.duration = 0;
		this.r1 = 0;
		this.r2 = 0;
		this.r3 = 0;
		this.r4 = 0;
		this.es = 0;
		this.ef = 0;
		this.visited = false;
		this.preActivities = new ArrayList<>();
		this.sucActivities = new ArrayList<>();

	}

	public Activity(int id) {
		super();
		this.id = id;
		this.duration = 0;
		this.r1 = 0;
		this.r2 = 0;
		this.r3 = 0;
		this.r4 = 0;
		this.es = 0;
		this.ef = this.es+this.duration;
		this.visited = false;
		this.preActivities = new ArrayList<>();
		this.sucActivities = new ArrayList<>();

	}

	/**
	 * 方法: 检查活动的所有紧前活动是否都已经安排
	 * 
	 * @return
	 */
	public boolean allPreActivitiesVisited() {
		boolean visited = true;
		Activity point = null;
		for (int i = 0; i < preActivities.size(); i++) {
			point = preActivities.get(i);
			if (point.visited == false) {
				visited = false;
			}
		}
		return visited;
	}

}

其次创建网络图类AonDiagram类,将整个活动网络作为对象。

/**
 * 网络图类
 * 包含
 * 属性:1.所包含的活动 activityArray 2.四种可更新资源 R1 R2 R3 R4
 * 方法:1.增加活动 2.构造活动 3.设置紧前紧后关系 4.设置资源限量 5.获取活动数
 * @author 11400
 *
 */

import java.util.ArrayList;

public class AonDiagram {
	public ArrayList<Activity> activityArray;
	public int R1, R2, R3, R4;
	public AonDiagram() {
		super();
		this.activityArray = new ArrayList<>();
	}
	/**
	 * 方法:
	 * 向网络图中增加活动
	 * @param act
	 */
	public void addActivity(Activity act) {
		this.activityArray.add(act);
	}
	/**
	 * 方法:
	 * 设置活动各个属性
	 * @param id
	 * @param duration
	 * @param r1
	 * @param r2
	 * @param r3
	 * @param r4
	 */
	public Activity setActivity(int id, int duration, int r1,int r2, int r3, int r4) {
		Activity act = new Activity();
		act.id = id;
		act.duration = duration;
		act.r1 = r1;
		act.r2 = r2;
		act.r3 = r3;
		act.r4 = r4;
		return act;
	}
	/**
	 * 方法:设置活动间的紧前紧后关系
	 * @param id1
	 * @param id2
	 */
	public void setRelation(int id1, int id2) {
		Activity act1 = null;
		Activity act2 = null;
		Activity point = null;
		for (int i = 0; i < activityArray.size(); i++) {
			point = activityArray.get(i);
			if (point.id == id1) {
				act1 = point;
			}
			if (point.id == id2) {
				act2 = point;
			}
			if (act1 != null && act2 != null) {
				break;
			}
		}
		act1.sucActivities.add(act2);
		act2.preActivities.add(act1);		
	}
	/**
	 * 方法:
	 * 设置资源限量
	 * @param R1
	 * @param R2
	 * @param R3
	 * @param R4
	 */
	public void setResAvail(int R1, int R2, int R3, int R4) {
		this.R1 = R1;
		this.R2 = R2;
		this.R3 = R3;
		this.R4 = R4;
	}
	/**
	 * 方法:查询网络图中的活动数目
	 * @return
	 */
	public int getAonSize() {
		int size = activityArray.size();
		return size;
		
	}
	public void reset() {
		for (int i = 0; i < activityArray.size(); i++) {
			Activity act = activityArray.get(i);
			act.ef = 0;
			act.es = 0;
		}
	}
	
	
}

设置完 AonDiagram类和Activity类之后,就可以读取标准化文件中的活动以及项目相关信息。

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Scanner;

import rcpsp_ver_1.Data_RCPSP;

public class InputData {
	public AonDiagram aonDiagram;

	public InputData() {
		super();
		// TODO Auto-generated constructor stub
	}

	public AonDiagram InPut(String path) throws Exception {
		Scanner file = new Scanner(new BufferedReader(new FileReader(path)));
		String check_succ = "successors";
		String check_dur = "------------------------------------------------------------------------";
		String check_res = "RESOURCEAVAILABILITIES:";
		String check_job = "):";
		String check_RES = "RESOURCES";
		String check;
		int k = 0;
		int n = 0;
		AonDiagram netWork = new AonDiagram();

		// ArrayList<ArrayList<Integer>> activity_Set;
		for (int i = 0; i < 100; i++) {
			check = file.next();
			if (check.equals(check_job)) {
				n = file.nextInt();
				break;
				// System.out.println("N-->"+n);
			}
		}
		for (int i = 1; i <= n; i++) {
			Activity act = new Activity(i);
			netWork.addActivity(act);
		}

		for (int i = 0; i < 100; i++) {
			check = file.next();
			 //System.out.println(check);
			// 搜索第一个表
			if (check.equals(check_succ)) {
				for (int j = 0; j < n; j++) {
					int preId = file.nextInt();
					file.next(); // mode跳过
					int successorsSum = file.nextInt();
					 //System.out.println("suc"+successorsSum);
					for (int m = 0; m < successorsSum; m++) {
						int sucId = file.nextInt();
						netWork.setRelation(preId, sucId);
					}
				}
			}

			// 搜索第二个表
			if (check.equals(check_dur)) {
				//System.out.println(check);
				for (int j = 0; j < n; j++) {
					Activity act = netWork.activityArray.get(j);
					file.nextInt();
					file.nextInt();
					act.duration = file.nextInt();
					//System.out.println(act.duration);
					act.r1 = file.nextInt();
					act.r2 = file.nextInt();
					act.r3 = file.nextInt();
					act.r4 = file.nextInt();
				}
			}
			// 搜索总资源量
			if (check.equals(check_res)) {
				file.nextLine();
				file.nextLine();
				netWork.R1 = file.nextInt();
				netWork.R2 = file.nextInt();
				netWork.R3 = file.nextInt();
				netWork.R4 = file.nextInt();
				break;
			}
		}
		file.close();
		return netWork;
	}

}

由于最简单的应用GA求解RCPSP问题是用活动代号进行编码因此编码过程较为简单,而解码过程较为复杂,在此程序里使用串行调度算法进行解码。

因此创建串行调度算法类SerialSchedule:

/**
 * 串行调度类:
 * 包含:
 * 属性:网络图 aon, 合格活动集合En, 局部调度计划Ps, 活动序列priority
 * 方法:
 */
import java.util.ArrayList;
import java.util.Arrays;

import ilog.concert.IloException;

public class SerialSchedule {
	public AonDiagram netWork;
	public ArrayList<Integer> En, Ps, priority;
	public ArrayList<Activity> PsA;
	public int fn;
	

	public SerialSchedule(AonDiagram netWork, ArrayList<Integer> priority) {
		super();
		thisWork = netWork;
		this.priority = new ArrayList<>();
		for (int i = 0; i < priority.size(); i++) {
			this.priority.add(priority.get(i));
		}
		this.En = new ArrayList<>();
		this.Ps = new ArrayList<>();
		this.PsA = new ArrayList<>();

	}

	/**
	 * 方法:串行调度方法过程
	 * 
	 * @return
	 */
	public int executeSchedule() {
		int J = netWork.getAonSize();
		Ps.add(1);
		Activity act = netWork.activityArray.get(0);
		act.visited = true;// 设置活动1的visited值为true表示已经加入到Ps中
		for (int i = 2; i <= J; i++) {
			
			addRelateActivityToEn(act);
			
			act = selectActFromEn();
			
			searchStartFinishTime(act);
			
			Ps.add(act.id);
			
		}
		fn = netWork.activityArray.get(J-1).ef;
		return fn;
	}

	/**
	 * 方法:实现将当前从合格活动En中选出的优先级最高的活动的符合条件(所有紧前活动都已加入到Ps)的紧后活动加入到En‘作为下一阶段的En
	 * 
	 * @param lastStageSelectedAct
	 */
	public void addRelateActivityToEn(Activity lastStageSelectedAct) {
		ArrayList<Activity> sucActArray = lastStageSelectedAct.sucActivities;
		int sucActSum = sucActArray.size(); // 上一阶段加入到局部调度计划中去的活动的所有紧后活动
		for (int i = 0; i < sucActSum; i++) { // 验证每一个上一阶段加入局部调度计划的活动的每一个活动是否符合加入到En的条件
			Activity checkAct = sucActArray.get(i); // 获得单个的紧后活动
			int preActAddedSum = 0; // 计算被加入到Ps中的紧前活动数量
			ArrayList<Activity> preActArray = checkAct.preActivities;// 所有紧前活动组成的动态数组
			int preActSum = preActArray.size(); // 紧前活动数量
			for (int j = 0; j < preActSum; j++) {// 检查每个紧前活动是否加入到Ps中
				int preId = preActArray.get(j).id;
				if (Ps.contains(preId)) {
					preActAddedSum++;
				}
			}
			if (preActAddedSum == preActSum) { // 若所有紧前活动都被加入到Ps,则将当前活动Id加入到En
				En.add(checkAct.id);
			}
		}
		//System.out.println("En "+ En);
	}

	/**
	 * 方法:从En中挑选出优先值最高的一个活动
	 * 
	 * @return
	 */
	public Activity selectActFromEn() {
		Activity act = new Activity();
		int selectedActNum = -1;
		for (int i = 0; i < priority.size(); i++) {
			int actNum = priority.get(i);
			if (En.contains(actNum)) {
				selectedActNum = actNum;
				En.remove((Integer) selectedActNum);
				break;
			}
		}
		int actNum = netWork.getAonSize();
		for (int i = 0; i < actNum; i++) {
			int checkActId = netWork.activityArray.get(i).id;
			if (selectedActNum == checkActId) {
				act = netWork.activityArray.get(i);
			}
		}
		PsA.add(act);
		return act;
	}

	/**
	 * 方法:判断某个时刻正在进行的活动对资源的占用量
	 */
	public int[] getResourceAtoneTime(int t) {
		int[] reSum = new int[4];
		ArrayList<Activity> actArray = netWork.activityArray;
		for (int i = 0; i < actArray.size(); i++) {
			Activity curAct = actArray.get(i);
			int es = curAct.es;
			int ef = curAct.ef;
			if (es < t && ef > t) {
				reSum[0] = reSum[0] + curAct.r1;
				reSum[1] = reSum[1] + curAct.r2;
				reSum[2] = reSum[2] + curAct.r3;
				reSum[3] = reSum[3] + curAct.r4;
			}
		}
		System.out.println("t = "+t+Arrays.toString(reSum));
		return reSum;
	}
	/**
	 * 方法:判断某个时刻正在进行的活动对资源的占用量,改进版本
	 */
	public int[] getResourceAtoneTime1(int t) {
		int[] reSum = new int[4];
		for (int i = 0; i < PsA.size(); i++) {
			Activity curAct = PsA.get(i);
			int es = curAct.es;
			int ef = curAct.ef;
			if (es <= t && ef >= t) {
				reSum[0] = reSum[0] + curAct.r1;
				reSum[1] = reSum[1] + curAct.r2;
				reSum[2] = reSum[2] + curAct.r3;
				reSum[3] = reSum[3] + curAct.r4;
			}
		}
		return reSum;
	}

	/**
	 * 方法:计算从En中选出来的活动的开始时间和结束时间
	 * 
	 * @param act
	 */
	public void searchStartFinishTime(Activity act) {
		ArrayList<Activity> preActArray = act.preActivities;
		for (int i = 0; i < preActArray.size(); i++) { // 先根据act的紧前活动的最早结束时间确定act的最早开始时间
			Activity preAct = preActArray.get(i);
			//preAct.ef = preAct.es + preAct.duration;
			//act.es = Integer.MIN_VALUE;
			if (act.es < preAct.ef) {
				act.es = preAct.ef;
			}
		}
		int t = act.es;
		int[] resSumAtT = new int[4];
		while (true) {
			resSumAtT = getResourceAtoneTime1(t);
				if (netWork.R1 - resSumAtT[0] >= act.r1 && netWork.R2 - resSumAtT[1] >= act.r2
						&& netWork.R3 - resSumAtT[2] >= act.r3 && netWork.R4 - resSumAtT[3] >= act.r4) {
					act.es = t;
					act.ef = t+act.duration;
					break;
				} else {
					t++;
				}			
		}
	}
	public void calFn() {
		int fn = executeSchedule();
	}
}

在接下来就可以正式进行遗传算法过程:

首先创建个体类Individual(即染色体)

/**
 * 个体类:
 * 包括
 * 属性:项目工期projectDuration, 适应值 fitness, 优先序列 priority
 * 方法:1.生成优先序列 creatPriority
 * @author 11400
 *
 */

import java.util.ArrayList;
import java.util.Random;

public class Individual {
	public int projectDuration;
	public ArrayList<Integer> priority;
	public float fitness;
	public float chosenRate;
	public float cumProbability;
	Individual next;

	public Individual() {
		super();
		this.projectDuration = 0;
		this.priority = new ArrayList<>();
		this.fitness = 0.0f;
		this.next = null;
		this.cumProbability = 0.0f;
		this.chosenRate = 0.0f;
	}

	/**
	 * 方法: 随机生成一个优先关系可行的活动序列
	 * 
	 * @param diagram
	 */
	public void creatPriority(AonDiagram diagram) {
		priority.add(1);// 加入虚活动1
		Random rand = new Random();
		while (priority.size() < diagram.getAonSize() - 1) {
			int randActId = 1 + rand.nextInt(diagram.getAonSize() - 1);// 随机一个活动代号
			if (priority.contains(randActId)) {
				continue;
			}
			Activity act = diagram.activityArray.get(randActId - 1);// 在AonDiagram中找到这个随机活动
			ArrayList<Activity> preActArray = act.preActivities;// 获得随机生成的活动的所有紧前活动
			int preActSum = 0;
			for (int i = 0; i < preActArray.size(); i++) {// 判断随机生成的活动的所有紧前活动是否已经全部加入到priority中
				int preActId = preActArray.get(i).id;
				if (priority.contains(preActId)) {
					preActSum++;
				}
			}
			if (preActSum == preActArray.size()) {
				priority.add(randActId);
			}
		}
		priority.add(diagram.getAonSize());

	}
	
	/**
	 * 计算当前个体的适应度1/projectDuration,在计算适应度的同时会把当前个体的projectDuration计算出来
	 */
	public void calFitness(AonDiagram netWork) {
		SerialSchedule schedule = new SerialSchedule(netWork, priority);
		this.projectDuration = schedule.executeSchedule();
		this.fitness = 1.0f/this.projectDuration;
		netWork.reset();
	}
	
	/**
	 * 深拷贝
	 * 
	 * @return
	 */
	public Individual clone() {
		Individual ind = new Individual();
		for (int i = 0; i < this.priority.size(); i++) {
			int actNum = this.priority.get(i);
			ind.priority.add(actNum);			
		}
		ind.chosenRate = this.chosenRate;
		ind.fitness = this.fitness;
		ind.projectDuration = this.projectDuration;
		
		return ind;
	}
	
}

一般情况下会给出优先关系即类中的priority属性,同时使用链表表示种群因此加入了next属性指向下一个个体。

接下来创建种群类Population:

/**
 * 种群类:
 * 包含:
 * 属性:头结点head 
 * 方法:add();向种群中增加个体
 * @author 11400
 *
 */
public class Population {
	public Individual head;
	public int popSize;
	
	public Population(int popSize) {
		super();
		this.head = new Individual();
		this.popSize = popSize;
	}
	/**
	 * 方法:向种群中增加个体
	 * @param ind
	 */
	public void add(Individual ind) {
		Individual point = head;
		while (point.next != null) {
			point = point.next;;
		}
		point.next = ind;
	}
	
	
	
}

之后进行选择、交叉和变异过程,并可视化

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;

import GAExcerseTSP.realTimeChart;
import rcpsp_ver_1.outPutChart;

/**
 * 遗传算法进行类:
 * 
 * @author 11400
 *
 */
public class GeneticAlgorithm {
	public AonDiagram netWork;
	public int maxGen;// 最大进化代数
	public float pc;// 交叉概率
	public float pm;// 变异概率
	public int popSize;//种群容量
	public realTimeChart chart;

	public GeneticAlgorithm(AonDiagram netWork, int maxGen, float pc, float pm,int popSize) {
		super();
		thisWork = netWork;
		this.maxGen = maxGen;
		this.pc = pc;
		this.pm = pm;
		this.popSize = popSize;

	}

	public Individual run(Population pop) {
		 chart = new realTimeChart("Project Duration", "Gen");
		creatInitialPop(pop);
		
		//printPri(pop);
		for (int i = 0; i < maxGen; i++) {
			//System.out.println("round "+i);	
			//printPri(pop);
			select(pop);
			//outPutIndProDur(pop);
			//System.out.println("after select ");	
			//printPri(pop);
			
			
			
			cross(pop);
			//System.out.println("after cross ");	
			//printPri(pop);
			
			mutate(pop);
			//System.out.println("after mutate ");	
			//printPri(pop);
			//outPutIndProDur(pop);
		}
		calIndChosenRate(pop);
		Individual bestInd = searchTalentInd(pop);
		
		return bestInd;
	}

	/**
	 * 方法:创建初始种群
	 * 
	 * @param pop
	 */
	public void creatInitialPop(Population pop) {
		//System.out.println("creat initial");
		for (int i = 0; i < pop.popSize; i++) {
			Individual ind = new Individual();
			ind.creatPriority(netWork);
			pop.add(ind);
		}
	}

	/**
	 * 计算个体的被选择概率同时在过程中会计算出每个个体的适应度
	 * 
	 * @param pop
	 */
	public void calIndChosenRate(Population pop) {
		Individual point = pop.head.next;
		float totalFitness = 0;
		int sum = 0;
		while (point != null) { // 适应度加和
			point.calFitness(netWork);
			sum++;
			//System.out.println("Ind "+sum+" Fitness "+ point.fitness);
			//System.out.println("Ind "+sum+" Project Duration "+ point.projectDuration+"\n");
			totalFitness += point.fitness;
			point = point.next;
		}
		point = pop.head.next;
		while (point != null) { // 计算被选择的概率
			point.chosenRate = point.fitness / totalFitness;
			point = point.next;
			
		}
		
	}

	/**
	 * 计算种群内个体的累积选择概率
	 * 
	 * @param pop
	 */
	public void calCumulativeProbability(Population pop) {
		
		Individual point = pop.head;
		while (point.next != null) {
			point.next.cumProbability = point.cumProbability + point.next.chosenRate;
			point = point.next;
		}
	}

	/**
	 * 找到适应度最大的个体
	 * 
	 * @param pop
	 * @return
	 */
	public Individual searchTalentInd(Population pop) {
		Individual point = pop.head.next;
		Individual talentInd = new Individual();
		talentInd.projectDuration = Integer.MAX_VALUE;
		while (point != null) {
			//System.out.println("Project Duration " + point.projectDuration);
			if (point.projectDuration < talentInd.projectDuration) {
				talentInd = point.clone();
			}
			if(point.projectDuration < 39) {
				System.out.println(point.projectDuration+"-->"+point.priority);
			}
			point = point.next;
		}
		//chart.plot(talentInd.projectDuration);
		return talentInd;
	}

	/**
	 * 选择过程: 首先将种群中的适应值最高的个体复制占种群容量的1/5 再按照轮盘赌的方式选出剩下的个体
	 * 
	 * @param pop
	 */
	public void select(Population pop) {
		//System.out.println("select ");
		calIndChosenRate(pop);
		//outPutIndProDur(pop);
		calCumulativeProbability(pop);
		//outPutIndProDur(pop);
		Population newPop = new Population(popSize);
		// 复制适应度最高的个体
		int talentCopyNum = pop.popSize / 5;
		int restCopyNum = pop.popSize - talentCopyNum;
		Individual talentInd = searchTalentInd(pop);
		chart.plot(talentInd.projectDuration);
		//System.out.println("Talent ind "+talentInd.priority);
		for (int i = 0; i < talentCopyNum; i++) {
			Individual indClone = talentInd.clone();
			newPop.add(indClone);
		}
		// 轮盘赌
		for (int i = 0; i < restCopyNum; i++) {
			float randomRate = (float) Math.random();
			Individual point = pop.head.next;
			while (point != null) {
				if (randomRate < point.cumProbability) {
					Individual ind = point.clone();
					newPop.add(ind);
					break;
				}
				point = point.next;
			}
		}
		pop.head = newPop.head; // 将原pop更新为newPop

	}
	/**
	 * 交叉过程:
	 * 总共交叉次数为popSize/2
	 * @param pop
	 */
	public void cross(Population pop) {
		// Population crossPop = new Population();
		int popSize = pop.popSize;
		int jobNum = netWork.getAonSize();
		for (int crossTime = 0; crossTime < popSize / 2; crossTime++) {

			float randomPc = (float) Math.random();
			if (randomPc < pc) {
				Random rand = new Random();
				int fatherNum = 1 + rand.nextInt(popSize);
				int motherNum = 1 + rand.nextInt(popSize);
				while(fatherNum == motherNum) {
					motherNum = 1+rand.nextInt(popSize);
				}
				
				Individual father = null;
				Individual mother = null;
				Individual son = new Individual();
				Individual daughter = new Individual();
				Individual point = pop.head.next;
				int calNum = 0;
				while (point != null) {
					calNum++;
					if (calNum == fatherNum) {
						father = point;
					}
					if (calNum == motherNum) {
						mother = point;
					}
					point = point.next;
				}
				int r1 = rand.nextInt(jobNum);
				int r2 = rand.nextInt(jobNum);
				if (r1 > r2) {
					int temp = r1;
					r1 = r2;
					r2 = temp;
				}
				for (int i = 0; i < r1; i++) {
					int fatherJobNum = father.priority.get(i);
					int motherJobNum = mother.priority.get(i);
					son.priority.add(fatherJobNum);
					daughter.priority.add(motherJobNum);
				}
				for (int i = r1; i < r2; i++) {
					for (int j = 0; j < jobNum; j++) {
						int fatherJobNum = father.priority.get(j);
						if (!daughter.priority.contains(fatherJobNum)) {
							daughter.priority.add(fatherJobNum);
							break;
						}
					}
					for (int j = 0; j < jobNum; j++) {
						int motherJobNum = mother.priority.get(j);
						if (!son.priority.contains(motherJobNum)) {
							son.priority.add(motherJobNum);
							break;
						}
					}
				}
				for (int i = r2; i < jobNum; i++) {
					for (int j = 0; j < jobNum; j++) {
						int fatherJobNum = father.priority.get(j);
						if (!son.priority.contains(fatherJobNum)) {
							son.priority.add(fatherJobNum);
							break;
						}
					}
					for (int j = 0; j < jobNum; j++) {
						int motherJobNum = mother.priority.get(j);
						if (!daughter.priority.contains(motherJobNum)) {
							daughter.priority.add(motherJobNum);
							break;
						}
					}
				}
				point = pop.head;
				while(point != null) { //用子代替换掉亲代
					if (point.next == father) {
						point.next = son;
						son.next = father.next;
						continue;
					}
					if (point.next == mother) {
						point.next = daughter;
						daughter.next = mother.next;
						continue;
					}
					point = point.next;
				}
			}
		}

	}
	/**
	 * 变异
	 * 首先确定一个活动,确定其所有紧前活动的最靠右位置,再确定其所有紧后活动最靠左位置,这之间的位置即为该活动的可移动区域
	 * @param pop
	 */
	public void mutate(Population pop) {
		int jobNum = netWork.getAonSize();
		Individual point = pop.head.next;
		while (point != null) {
			float randomPm = (float) Math.random();
			if (randomPm < pm) {
				Random rand = new Random();
				int randomActNum = 2 + rand.nextInt(jobNum-3); // 获取要改变位置的活动代号
				int curRandomActNumPos = -1; //获取要改变位置的活动的当前位置
				for(int i = 0; i < jobNum; i++) {
					if (randomActNum == point.priority.get(i)) {
						curRandomActNumPos = i;
						break;
					}
				}				
				Activity randomAct = null;	//获得活动代号对应的Activity
				ArrayList<Activity> actSet = netWork.activityArray;
				for (int i = 0; i < actSet.size(); i++) {
					Activity act = actSet.get(i);
					if (act.id == randomActNum) {
						randomAct = act;
						break;
					}
				}
				ArrayList<Activity> preActArray = randomAct.preActivities;//找到要变换位置的活动的最靠右的紧前活动的位置
				int maxPreActPosition = -1;
				for (int i = 0; i < preActArray.size(); i++) {
					Activity preAct = preActArray.get(i);
					for (int j = 0; j < jobNum; j++) {
						if (preAct.id == point.priority.get(j)) {
							if (maxPreActPosition < j) {
								maxPreActPosition = j;
							}
							break;
						}
					}
				}
				ArrayList<Activity> sucActArray = randomAct.sucActivities;//找到最靠左的紧后活动的位置
				int minSucActPosition = Integer.MAX_VALUE;
				for (int i = 0; i < sucActArray.size(); i++) {
					Activity sucAct = sucActArray.get(i);
					for (int j = 0; j < jobNum; j++) {
						if (sucAct.id == point.priority.get(j)) {
							if (minSucActPosition > j) {
								minSucActPosition = j;
							}
							break;
						}
					}
				}
				while (minSucActPosition - maxPreActPosition > 2) {//只有当最右的紧前活动位置与最左的紧后活动之间有两个以上位置时才能对当前活动的位置进行改变
					int changePositionTo = maxPreActPosition + 1 + rand.nextInt(minSucActPosition - maxPreActPosition - 1);
					if (changePositionTo == curRandomActNumPos) {
						continue;
					}
					if (curRandomActNumPos < changePositionTo) {//当把选中的活动代号往右移动时
						int temp = point.priority.get(curRandomActNumPos);
						for (int i = curRandomActNumPos; i < changePositionTo; i++) {							
							point.priority.set(i, point.priority.get(i+1));   //priority将序列左移									
						}
						point.priority.set(changePositionTo, temp);
						break;
					}
					if (curRandomActNumPos > changePositionTo) {//当吧选中的活动代号往左移动时
						int temp = point.priority.get(curRandomActNumPos);
						for (int i = curRandomActNumPos; i > changePositionTo; i--) {
							point.priority.set(i, point.priority.get(i-1));//priority右移
						}
						point.priority.set(changePositionTo, temp);
						break;
					}
				}
				
			}
			point = point.next;
		}
	}
	
	/**
	 * 辅助方法:计算种群实际容量
	 * @param pop
	 */
	public void getPopSize(Population pop) {
		Individual point = pop.head.next;
		int sum = 0;
		while (point != null) {
			sum++;
			point = point.next;
		}
		System.out.println("Pop Size " + sum);
	}
	/**
	 * 辅助方法:
	 * 打印每个个体的priority
	 * @param pop
	 */
	public void printPri(Population pop) {
		Individual point = pop.head.next;
		while (point != null) {
			System.out.println(point.priority);
			point = point.next;
		}
	}
	
	/**
	 * 辅助方法:输出每个个体的project duration
	 * @param pop
	 */
	public void outPutIndProDur(Population pop) {
		System.out.println("current pop duration:");
		Individual point = pop.head.next;
		while (point != null) {
			System.out.println(point.projectDuration);
			point = point.next;
		}
	}
}

再加上一个简洁的运行程序

/**
 * 求解主程序
 * 
 * @author 11400
 *
 */
public class RunSolve {
	public static void main(String[] args) throws Exception {
		int popSize = 100;// 种群容量一般取20~100
		int maxGen = 500;// 遗传算法最大进化代数一般取100~500;
		float pc = 0.85f;// 交叉概率一般为0.4~0.99
		float pm = 0.1f;// 变异概率,一般为0.0001~0.1
		
		String path = "C:\\Users\\11400\\eclipse-workspace\\CPM\\CPM\\Data\\j301_1.sm";
		InputData data = new InputData();
		AonDiagram netWork = data.InPut(path);
		//System.out.println(netWork.getAonSize());
		Population pop = new Population(popSize);
		GeneticAlgorithm algorithm = new GeneticAlgorithm(netWork, maxGen, pc, pm, popSize);
		
		Individual result = algorithm.run(pop);
		System.out.println("Shortest Project Duration is "+ result.projectDuration);

	}
}

大功告成!!!

其中的realTimeChart类是网上找的


import java.util.LinkedList;
import java.util.List;

import javax.swing.JFrame;

import org.knowm.xchart.SwingWrapper;
import org.knowm.xchart.XYChart;
import org.knowm.xchart.XYChartBuilder;
import org.knowm.xchart.style.Styler.ChartTheme;
import org.knowm.xchart.style.Styler.LegendLayout;
import org.knowm.xchart.style.Styler.LegendPosition;

public class realTimeChart {
	
	 
	/**
	 * Logarithmic Y-Axis
	 *
	 * <p>
	 * Demonstrates the following:
	 *
	 * <ul>
	 * <li>Logarithmic Y-Axis
	 * <li>Building a Chart with ChartBuilder
	 * <li>Place legend at Inside-NW position
	 */	
	 
		private SwingWrapper<XYChart> swingWrapper;
		private XYChart chart;
		private JFrame frame;
	 
		private String title;// 标题
		private String seriesName;// 系列,此处只有一个系列。若存在多组数据,可以设置多个系列
		private List<Double> seriesData;// 系列的数据
		private int size = 1000;// 最多显示多少数据,默认显示1000个数据
	 
		public int getSize() {
			return size;
		}
	 
		public void setSize(int size) {
			this.size = size;
		}
	 
		public String getSeriesName() {
			return seriesName;
		}
	 
		public void setSeriesName(String seriesName) {
			this.seriesName = seriesName;
		}
	 
		public String getTitle() {
			return title;
		}
	 
		public void setTitle(String title) {
			this.title = title;
		}
	 
		/**
		 * 实时绘图
		 * 
		 * @param seriesName
		 * @param title
		 */
		public realTimeChart(String title, String seriesName) {
			super();
			this.seriesName = seriesName;
			this.title = title;
		}
	 
		public realTimeChart(String title, String seriesName, int size) {
			super();
			this.title = title;
			this.seriesName = seriesName;
			this.size = size;
		}
	 
		public void plot(double data) {
			if (seriesData == null) {
				seriesData = new LinkedList<>();
			}
	 
			if (seriesData.size() == this.size) {
				seriesData.clear();
			}
	 
			seriesData.add(data);
	 
			if (swingWrapper == null) {
	 
				// Create Chart
				chart = new XYChartBuilder().width(600).height(450).theme(ChartTheme.Matlab).title(title).build();
				chart.addSeries(seriesName, null, seriesData);
				chart.getStyler().setLegendPosition(LegendPosition.OutsideS);// 设置legend的位置为外底部
				chart.getStyler().setLegendLayout(LegendLayout.Horizontal);// 设置legend的排列方式为水平排列
				chart.getStyler().setMarkerSize(0);
	 
				swingWrapper = new SwingWrapper<XYChart>(chart);
				frame = swingWrapper.displayChart();
				frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);// 防止关闭窗口时退出程序
			} else {
	 
				// Update Chart
				chart.updateXYSeries(seriesName, null, seriesData, null);
				swingWrapper.repaintChart();
			}
		}
	}

大概是这样,也可以用别的来画,不过现在懒得再弄了。

运行完成运气好的化能找到最优解,不好的话我也没办法,并且串行调度算法中有点小问题,会导致找到的最短工期比最优解还短,有功夫再研究。

运行完差不多就这样

 结束!!

如还有其他问题,欢迎指正~

测试算例是PSPLIB中的j301-1,附上链接:Single Mode Data Sets (tum.de)

更多推荐

Java:代码实现遗传算法求解RCPSP问题