对于网上的一些蜂群算法python实现的代码,笔者认为多数不够简洁易懂且可扩展性和可修改性不足,干脆自己实现一个吧(虽然可能也存在上述问题…)。蜂群算法原理较简单,在此不再赘述,可参考这篇博客,且其中的C++代码比较好理解。
代码如下(python3.6,画图需要matplotlib包):
- import math
- import random
- import matplotlib.pyplot as plt
-
- from typing import List
-
-
- class ProblemModel:
-
- def __init__(self, bounds=None):
- self.bounds = bounds
-
- def getIndependentVar(self):
- if self.bounds is not None:
- independentVar = []
- for bound in self.bounds:
- independentVar.append(bound[0] + random.random() * (bound[1] - bound[0]))
- return independentVar
- else:
- pass
-
- def getNewVar(self, var_1, var_2):
- if self.bounds is not None:
- newVar = []
- step_random = random.random()
- for v_1, v_2 in zip(var_1, var_2):
- newVar.append(v_1 + step_random * (v_2 - v_1))
- return newVar
- else:
- pass
-
- def getValue(self, variable):
- if len(variable) == 2:
- x = variable[0]
- y = variable[1]
- return 1+(x**2+y**2)/4000-(math.cos(x)*math.cos(y/math.sqrt(2)))
- # return -(x**2-10*math.cos(2*math.pi*x)+10)+(y**2-10*math.cos(2*math.pi*y)+10)
- # return -20*math.exp(-0.2*math.sqrt((x**2+y**2)/2))-math.exp((math.cos(2*math.pi*x)+math.cos(2*math.pi*y))/2)+20+math.e
- else:
- return 1
-
-
- class NectarSource:
- problem_src = None # static variable
-
- def __init__(self, position):
- self.position = position
- self.value = self.problem_src.getValue(position)
- if self.value >= 0:
- self.fitness = 1/(1+self.value)
- else:
- self.fitness = 1+math.fabs(self.value)
- # this definition of fitness means looking for the minimum
- self.trail = 0
-
-
- class ABCAlgor:
- LIMIT = 10 # If the num of times a source not be updated is more than this, then give up.
-
- def __init__(self, problem, employedNum, onlookerNum, maxIteration):
- NectarSource.problem_src = problem
- self.problem = problem # type:ProblemModel
- self.employedNum = employedNum
- self.onlookerNum = onlookerNum
- self.maxIteration = maxIteration
- self.nectarSrc = [] # type:List[NectarSource]
- self.bestNectar = NectarSource(self.problem.getIndependentVar())
- self.resultRecord = []
- for i in range(self.employedNum):
- self.nectarSrc.append(NectarSource(self.problem.getIndependentVar()))
-
- def updateNectarSrc(self, index):
- # produce a new nectar; if new.fit>old.fit: replace the old; else: old.trail++;
- src = self.nectarSrc[index]
- src_another = random.choice(self.nectarSrc) # type:NectarSource
- while src_another is src:
- src_another = random.choice(self.nectarSrc)
- src_new = NectarSource(self.problem.getNewVar(src.position, src_another.position))
- if src_new.fitness > src.fitness:
- self.nectarSrc[index] = src_new
- else:
- self.nectarSrc[index].trail += 1
-
- def employedProcedure(self):
- length = len(self.nectarSrc) # len(self.nectarSrc) may be changed in self.updateNectarSrc
- for i in range(length):
- self.updateNectarSrc(i)
-
- def onlookerProcedure(self):
- sum_fitness = 0
- for src in self.nectarSrc:
- sum_fitness += src.fitness
- length = len(self.nectarSrc)
- for i in range(length):
- probability_fit = self.nectarSrc[i].fitness/sum_fitness
- for onlookerBee in range(self.onlookerNum):
- if random.random() < probability_fit:
- self.updateNectarSrc(i)
-
- def updateBestNectar(self):
- # use the fitness to select the best, if the problem is finding the max, change the definition of fitness
- for src in self.nectarSrc:
- if src.fitness > self.bestNectar.fitness:
- self.bestNectar = src
-
- def scoutProcedure(self):
- length = len(self.nectarSrc)
- for i in range(length):
- if self.nectarSrc[i].trail >= self.LIMIT:
- self.nectarSrc[i] = NectarSource(self.problem.getIndependentVar())
-
- def solve(self):
- for i in range(self.maxIteration):
- self.employedProcedure()
- self.onlookerProcedure()
- self.updateBestNectar()
- self.scoutProcedure()
- self.updateBestNectar()
- self.resultRecord.append(self.bestNectar.value)
-
- def showResult(self):
- for result in self.resultRecord:
- print(result)
- print('best solution:', self.bestNectar.position)
- print('best value:', self.bestNectar.value)
- plt.plot(self.resultRecord)
- plt.title('result curve')
- plt.tight_layout()
- plt.show()
-
-
- if __name__ == '__main__':
- beesNum = 100
- employedNum = int(beesNum/2)
- onlookerNum = int(beesNum/2)
- maxIteration = 200
- problem = ProblemModel(bounds=[[-10, 10], [-10, 10]])
- abcSolution = ABCAlgor(problem, employedNum, onlookerNum, maxIteration)
- abcSolution.solve()
- abcSolution.showResult()