Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 19 additions & 25 deletions GA.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ var resultData = [];

// 渲染视图
draw(resultData);
// console.log(resultData);

})(100, 10, 100, 100, 0.2);


Expand Down Expand Up @@ -126,28 +126,15 @@ function checkParam(_taskNum, _nodeNum, _iteratorNum, _chromosomeNum, _cp) {
*/
function calAdaptability(chromosomeMatrix) {
adaptability = [];

// 计算每条染色体的任务长度
for (var chromosomeIndex=0; chromosomeIndex<chromosomeNum; chromosomeIndex++) {
var maxLength = Number.MIN_VALUE;
for (var nodeIndex=0; nodeIndex<nodeNum; nodeIndex++) {
var sumLength = 0;
for (var taskIndex=0; taskIndex<taskNum; taskIndex++) {
if (chromosomeMatrix[chromosomeIndex][taskIndex] == nodeIndex) {
sumLength += timeMatrix[taskIndex][nodeIndex];
}
}

if (sumLength > maxLength) {
maxLength = sumLength;
}
}

var chromosomeTaskLengths = calTaskLengthOfEachChromosome(chromosomeMatrix);
for (var i=0; i<chromosomeTaskLengths.length; ++i) {
// 适应度 = 1/任务长度
adaptability.push(1/maxLength);
adaptability.push(1/chromosomeTaskLengths[i]);
}
}


/**
* 计算自然选择概率
* @param adaptability
Expand Down Expand Up @@ -268,8 +255,16 @@ function copy(chromosomeMatrix, newChromosomeMatrix) {
* @param chromosomeMatrix
*/
function calTime_oneIt(chromosomeMatrix) {
// 计算每条染色体的任务长度
var timeArray_oneIt = [];
resultData.push(calTaskLengthOfEachChromosome(chromosomeMatrix));
}


/**
* 计算每条染色体的任务长度
* @param chromosomeMatrix
*/
function calTaskLengthOfEachChromosome(chromosomeMatrix) {
var chromosomeTaskLengths = [];
for (var chromosomeIndex=0; chromosomeIndex<chromosomeNum; chromosomeIndex++) {
var maxLength = Number.MIN_VALUE;
for (var nodeIndex=0; nodeIndex<nodeNum; nodeIndex++) {
Expand All @@ -279,17 +274,16 @@ function calTime_oneIt(chromosomeMatrix) {
sumLength += timeMatrix[taskIndex][nodeIndex];
}
}

if (sumLength > maxLength) {
maxLength = sumLength;
}
}

timeArray_oneIt.push(maxLength);
chromosomeTaskLengths.push(maxLength);
}
resultData.push(timeArray_oneIt);
return chromosomeTaskLengths;
}


/**
* 繁衍新一代染色体
* @param chromosomeMatrix 上一代染色体
Expand All @@ -309,6 +303,7 @@ function createGeneration(chromosomeMatrix) {

// 计算当前染色体的任务处理时间
calTime_oneIt(newChromosomeMatrix);

return newChromosomeMatrix;
}

Expand All @@ -323,7 +318,6 @@ function createGeneration(chromosomeMatrix) {

// 计算当前染色体的任务处理时间
calTime_oneIt(newChromosomeMatrix);

return newChromosomeMatrix;
}

Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,26 +114,26 @@ 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的染色体的适应度。

在负载均衡调度这个实例中,我们将N个任务执行总时长作为适应度评判的标准。当所有任务分配完后,如果总时长较长,那么适应度就越差;而总时长越短,则适应度越高。

### 选择概率矩阵
通过上文可知,每次进化过程中,都需要根据适应度矩阵计算每一条染色体在下一次进化中被选择的概率,这个矩阵如下所示:
> 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!
Expand Down
198 changes: 198 additions & 0 deletions python/ga.py
Original file line number Diff line number Diff line change
@@ -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