diff --git a/GA.js b/GA.js index 2dddecc..aada0c1 100644 --- a/GA.js +++ b/GA.js @@ -62,7 +62,7 @@ var resultData = []; // 渲染视图 draw(resultData); - // console.log(resultData); + })(100, 10, 100, 100, 0.2); @@ -126,28 +126,15 @@ function checkParam(_taskNum, _nodeNum, _iteratorNum, _chromosomeNum, _cp) { */ function calAdaptability(chromosomeMatrix) { adaptability = []; - // 计算每条染色体的任务长度 - for (var chromosomeIndex=0; chromosomeIndex maxLength) { - maxLength = sumLength; - } - } - + var chromosomeTaskLengths = calTaskLengthOfEachChromosome(chromosomeMatrix); + for (var i=0; i maxLength) { maxLength = sumLength; } } - - timeArray_oneIt.push(maxLength); + chromosomeTaskLengths.push(maxLength); } - resultData.push(timeArray_oneIt); + return chromosomeTaskLengths; } + /** * 繁衍新一代染色体 * @param chromosomeMatrix 上一代染色体 @@ -309,6 +303,7 @@ function createGeneration(chromosomeMatrix) { // 计算当前染色体的任务处理时间 calTime_oneIt(newChromosomeMatrix); + return newChromosomeMatrix; } @@ -323,7 +318,6 @@ function createGeneration(chromosomeMatrix) { // 计算当前染色体的任务处理时间 calTime_oneIt(newChromosomeMatrix); - return newChromosomeMatrix; } diff --git a/README.md b/README.md index 6745eb1..4fdc730 100644 --- a/README.md +++ b/README.md @@ -114,15 +114,15 @@ timeMatrix[i][j]表示将任务i分配给节点j处理所需的时间,它通 一条染色体就是一个一位数组,一位数组的下标表示任务的编号,数组的值表示节点的编号。那么chromosome[i]=j的含义就是:将任务i分配给了节点j。 -上面的例子中,任务集合为Tasks={2,4,6,8},节点集合为Nodes={2,1},那么染色体chromosome={3,2,1,0}的含义是: -- 将任务0分配给3号节点 -- 将任务1分配给2号节点 +上面的例子中,任务集合为Tasks={2,4,6,8},节点集合为Nodes={2,1},那么染色体chromosome={1,0,1,0}的含义是: +- 将任务0分配给1号节点 +- 将任务1分配给0号节点 - 将任务2分配给1号节点 - 将任务3分配给0号节点 ### 适应度矩阵 -通过上文可知,在遗传算法中扮演者“上帝”角色的是适应度函数,它会评判每一条染色体的适应度,并保留适应度高的染色体、淘汰适应度差的染色体。那么在算法实现时,我们需要一个适应度矩阵,记录当前N条染色体的适应度,如下所示: -> adaptability={0.6, 2, 3.2, 1.8} +通过上文可知,在遗传算法中扮演者“上帝”角色的是适应度函数,它会评判每一条染色体的适应度,并保留适应度高的染色体、淘汰适应度差的染色体。那么在算法实现时,我们需要一个适应度矩阵,记录当前N条染色体的适应度(当N==4时的例子),如下所示: +> adaptability=[0.6, 2, 3.2, 1.8] adaptability数组的下标表示染色体的编号,而adaptability[i]则表示编号为i的染色体的适应度。 @@ -130,10 +130,10 @@ adaptability数组的下标表示染色体的编号,而adaptability[i]则表 ### 选择概率矩阵 通过上文可知,每次进化过程中,都需要根据适应度矩阵计算每一条染色体在下一次进化中被选择的概率,这个矩阵如下所示: -> selectionProbability={0.1, 0.4, 0.2, 0.3} +> selectionProbability=[0.1, 0.4, 0.2, 0.3] 矩阵的下标表示染色体的编号,而矩阵中的值表示该染色体对应的选择概率。其计算公式如下: -> selectionProbability[i] = adaptability[i] / 适应度之和 +> selectionProbability[i] = adaptability[i] / 染色体适应度之和 ## 遗传算法的实现 > 上述一切知识点铺垫完成之后,接下来我们就可以上代码了,相信Talk is cheap, show you the code! diff --git a/python/ga.py b/python/ga.py new file mode 100644 index 0000000..0b3ce9b --- /dev/null +++ b/python/ga.py @@ -0,0 +1,198 @@ +#encoding=utf-8 +import random +import math +import heapq + + +class GA(object): + def __init__(self): + self.tasks = [] # 任务集合, tasks[i]表示第i个任务的长度 + self.taskNum = 100 #任务数量 + self.nodes = [] # 处理节点集合, node[i]表示第i个处理节点的处理速度 + self.nodeNum = 10 #节点数量 + self.taskLengthRange = [10, 100] # 任务长度取值范围 + self.nodeSpeedRange = [10, 100] # 节点处理速度取值范围 + self.timeMatrix = [] #任务处理时间二维矩阵, 记录单个任务在不同节点上的处理时间 + self.iteratorNum = 100 # 迭代次数 + self.chromosomeNum = 10 # 染色体数量 + self.cp = 0.2 # 染色体复制比例(每代中保留适应度较高的染色体直接成为下一代) + self.crossoverMutationNum = self.chromosomeNum - int(self.chromosomeNum * self.cp) # 参与交叉变异的染色体数量 + self.result = [] # 最终结果, 任务处理总时间结果集合([迭代次数][染色体编号]) + + + def ga(self): + # 任务 + self.tasks = self.initRandomArray(self.taskNum, self.taskLengthRange) + # 处理节点 + self.nodes = self.initRandomArray(self.nodeNum, self.nodeSpeedRange) + # 初始化任务处理时间矩阵 + self.initTimeMatrix() + # 初始化第一代染色体 + chromosomeMatrix = self.createGeneration() + # 迭代繁衍 + for i in range(self.iteratorNum): + adaptability = self.calAdaptability(chromosomeMatrix) # 计算上一代各染色体的适应度 + chromosomeMatrix = self.createGeneration(chromosomeMatrix, adaptability) # 生成新一代染色体 + + + def initRandomArray(self, l, r): + ''' + 创建随机数组 + :param l: 数组长度 + :param r: 数组值取值范围, 整数 + :return: + ''' + return [random.randint(r[0], r[1]) for i in range(l)] + + + def initTimeMatrix(self): + ''' + 初始化任务处理时间矩阵 + ''' + for i in range(len(self.tasks)): + timeMatrix_i = [] + for j in range(len(self.nodes)): + timeMatrix_i.append(self.tasks[i] / self.nodes[j]) + self.timeMatrix.append(timeMatrix_i) + + + def createGeneration(self, oldChromosomeMatrix=None, adaptability=None): + ''' + 繁衍新一代染色体(染色体长度就是任务的数量), 并计算任务处理时间 + :return: 新一代染色体二维矩阵 + ''' + chromosomeMatrix = [] + if not oldChromosomeMatrix: # 第一代染色体, 随机生成 + for i in range(self.chromosomeNum): + chromosome = [] # 一条染色体 + for j in range(self.taskNum): # 每个任务随机指定处理节点 + chromosome.append(random.randint(0, self.nodeNum-1)) + chromosomeMatrix.append(chromosome) + # 保存当前迭代染色体的任务处理时间 + self.result.append(self.calTaskLengthOfEachChromosome(chromosomeMatrix)) + else: + selectionProbability = self.calSelectionProbability(adaptability) # 计算自然选择概率 + # 交叉生成 self.crossoverMutationNum 条染色体 + chromosomeMatrix = self.crossover(oldChromosomeMatrix, selectionProbability) + self.mutation(chromosomeMatrix) # 变异 + self.copy(oldChromosomeMatrix, chromosomeMatrix, adaptability) # 复制 + # 保存当前迭代染色体的任务处理时间 + self.result.append(self.calTaskLengthOfEachChromosome(chromosomeMatrix)) + return chromosomeMatrix + + + def calAdaptability(self, chromosomeMatrix): + ''' + 计算染色体适应度 + :param chromosomeMatrix: 染色体矩阵 + :return: 染色体适应度一维矩阵(下标: 染色体, 值:染色体适应度) + ''' + adaptabilitity = [] # 适应度一维矩阵(下标: 染色体, 值:染色体适应度) + # 计算每条染色体的任务长度 + chromosomeTaskLengths = self.calTaskLengthOfEachChromosome(chromosomeMatrix) + # 计算染色体适应度 + for i in range(len(chromosomeTaskLengths)): + adaptabilitity.append(1.0/chromosomeTaskLengths[i]) + return adaptabilitity + + + def calSelectionProbability(self, adaptabilitity): + ''' + 计算自然选择的概率 + :param adaptabilitity: 染色体适应度 + :return: 一维矩阵(下标: 染色体编号, 值:该染色体被选择的概率) + ''' + sumAdaptability = sum(adaptabilitity) # 计算适应度总和 + return [adaptabilitity[i]/sumAdaptability for i in range(len(adaptabilitity))] # 计算每条染色体被选择的概率 + + + def crossover(self, oldChromosomeMatrix, selectionProbability): + ''' + 交叉生成{self.crossoverMutationNum}条染色体 + :param oldChromosomeMatrix: 上一代染色体矩阵 + :param selectionProbability: 自然选择概率 + :return: 交叉后生成的新染色体矩阵 + ''' + newChromosomeMatrix = [] + for i in range(self.crossoverMutationNum): + # 采用轮盘赌选择父母染色体 + fatherChromosome = oldChromosomeMatrix[self.rws(selectionProbability)][:] # [:]表示复制 + motherChromosome = oldChromosomeMatrix[self.rws(selectionProbability)][:] + crossIndex = random.randint(0, self.taskNum-1) + sonChromosome = fatherChromosome[0:crossIndex]+motherChromosome[crossIndex:] + newChromosomeMatrix.append(sonChromosome) + return newChromosomeMatrix + + + def mutation(self, chromosomeMatrix): + ''' + 对染色体矩阵进行变异(直接操作染色体矩阵) + :param chromosomeMatrix: 染色体矩阵 + :return: 变异后的染色体矩阵 + ''' + # 随机找一条染色体 + chromosomeIndex = random.randint(0, self.crossoverMutationNum-1) + # 随机找一个任务 + taskIndex = random.randint(0, self.taskNum-1) + # 随机找一个处理节点 + nodeIndex = random.randint(0, self.nodeNum-1) + chromosomeMatrix[chromosomeIndex][taskIndex] = nodeIndex + return chromosomeMatrix + + + def calTaskLengthOfEachChromosome(self, chromosomeMatrix): + ''' + 计算每条染色体处理任务的总时间长度 + :param chromosomeMatrix: 染色体矩阵 + :return: 染色体处理时间长度一维矩阵 + ''' + chromosomeTaskLengths = [] + for i in range(self.chromosomeNum): + maxLength = float('-inf') + for nodeIndex in range(self.nodeNum): + chromosomeTaskLength = sum( + [self.timeMatrix[taskIndex][nodeIndex] for taskIndex in range(self.taskNum) if + chromosomeMatrix[i][taskIndex] == nodeIndex]) + maxLength = max(maxLength, chromosomeTaskLength) + chromosomeTaskLengths.append(maxLength) + return chromosomeTaskLengths + + + def rws(self, selectionProbability): + ''' + 轮盘赌方法选择染色体 + :param selectionProbability: 自然选择概率 + :return: 被选中染色体编号 + ''' + rand = random.random() #生产0-1之间的随机数 + sum = 0 + for chromosomeIndex in range(len(selectionProbability)): + sum += selectionProbability[chromosomeIndex] + if(sum >= rand): + return chromosomeIndex + + + def copy(self, oldChromosomeMatrix, newChromosomeMatrix, adaptability): + ''' + 从上一代复制适应度最高的N个染色体到下一代 + :param oldChromosomeMatrix: 上一代染色体矩阵 + :param newChromosomeMatrix: 新一代染色体矩阵 + :param adaptability: 适应度矩阵 + :return: None + ''' + N = int(self.chromosomeNum * self.cp) #需要复制的染色体数量N=染色体数量*复制比例, 向下取整 + if N <= 0: + return + # 选出适应度最高的的N条染色体(N=染色体数量*复制比例, 向下取整) + chromosomeIndexes = map(adaptability.index, heapq.nlargest(N, adaptability)) + # 进行复制 + for i in range(len(chromosomeIndexes)): + chromosome = oldChromosomeMatrix[chromosomeIndexes[i]] + newChromosomeMatrix.append(chromosome) + + + +if __name__ == '__main__': + ga = GA() + ga.ga() + print ga.result \ No newline at end of file