PyGAD如何实现基因无放回采样?适配座位分配约束咨询
针对骑手-座位分配问题的PyGAD适配方案
问题1:PyGAD原生支持无放回采样吗?
PyGAD不直接支持这种无放回采样的基因约束。你当前设置的gene_space会让每个基因位独立采样1-25的整数,很容易出现重复值,无法满足「每个座位仅分配给一名骑手」的核心约束。默认的初始化、交叉、变异逻辑都是基于独立基因位操作,天生不适配排列型问题。
问题2:适配无放回约束的可行方案
完全可以通过自定义PyGAD的核心算子(初始化、交叉、变异)来实现,这是PyGAD的灵活特性,不会削弱其优势,反而能精准匹配你的问题场景。具体实现如下:
1. 自定义初始化函数:生成无重复排列
每个个体对应一个1-25的随机排列(代表骑手到座位的分配),用random.sample或numpy.random.permutation实现:
import random import pygad def custom_initialization(num_generations, num_parents_mating, sol_per_pop, num_genes, gene_space, init_range_low, init_range_high): # 生成sol_per_pop个无重复的1-25排列 population = [] for _ in range(sol_per_pop): permutation = random.sample(range(1, 26), 25) population.append(permutation) return population
2. 自定义交叉算子:使用排列专用交叉算法
针对排列型问题,必须用不会产生重复值的交叉方法,推荐以下三种常用算法:
- 部分映射交叉(PMX):最常用的排列交叉,保留父代的部分映射关系
- 顺序交叉(OX):保留父代的基因顺序
- 循环交叉(CX):保留父代的循环结构
以PMX为例实现自定义交叉:
def pmx_crossover(parent1, parent2): size = len(parent1) # 随机选两个交叉点 start, end = sorted(random.sample(range(size), 2)) # 构建映射字典 mapping = {parent1[i]: parent2[i] for i in range(start, end)} mapping.update({parent2[i]: parent1[i] for i in range(start, end)}) # 生成子代1 child1 = [] for i in range(size): if start <= i < end: child1.append(parent2[i]) else: gene = parent1[i] while gene in mapping: gene = mapping[gene] child1.append(gene) # 生成子代2 child2 = [] for i in range(size): if start <= i < end: child2.append(parent1[i]) else: gene = parent2[i] while gene in mapping: gene = mapping[gene] child2.append(gene) return child1, child2
3. 自定义变异算子:使用排列专用变异方法
同样要避免产生重复值,推荐以下三种:
- 交换变异:随机交换两个位置的基因
- 反转变异:随机选择一段区间反转基因顺序
- 插入变异:随机选择一个基因插入到另一个位置
以交换变异为例实现:
def swap_mutation(offspring, mutation_probability): for i in range(len(offspring)): if random.random() < mutation_probability: # 随机选两个不同的位置 pos1, pos2 = random.sample(range(len(offspring[i])), 2) # 交换基因 offspring[i][pos1], offspring[i][pos2] = offspring[i][pos2], offspring[i][pos1] return offspring
4. 在PyGAD中配置自定义算子
初始化PyGAD时指定这些自定义函数:
ga_instance = pygad.GA( num_generations=100, num_parents_mating=4, sol_per_pop=20, num_genes=25, # 指定自定义初始化函数 initialization_func=custom_initialization, # 指定自定义交叉函数 crossover_func=pmx_crossover, # 指定自定义变异函数 mutation_func=swap_mutation, mutation_probability=0.1, # 替换为你的目标函数 fitness_func=your_fitness_function, )
关于你提到的CDF方法
这种方法确实效率低下,因为需要额外处理重复值的过滤和调整,会增加计算开销,而且无法保证遗传算子的有效性。相比之下,直接使用排列专用的遗传算子,既能严格满足约束,又能充分利用PyGAD的框架优势,是更优的选择。
内容的提问来源于stack exchange,提问作者jbuddy_13
相关产品推荐
相关产品推荐

