You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于定长实数向量的遗传算法实现:编码与初始化疑问

问题描述

我正尝试实现基于定长实数向量的遗传算法(Genetic Algorithm),目前找到一个采用二进制编码的简易Python实现,但在数组初始化与算法边界设置上存在困惑。以下是原代码中的二进制解码片段:

def decode(bounds, n_bits, bitstring):
    decoded = list()
    largest = 2**n_bits
    for i in range(len(bounds)):
        # extract the substring
        start, end = i * n_bits, (i * n_bits)+n_bits
        substring = bitstring[start:end]
        # convert bitstring to a string of chars
        chars = ''.join([str(s) for s in substring])
        # convert string to integer
        integer = int(chars, 2)
        # scale integer to desired range
        value = bounds[i][0] + (integer/largest) * (bounds[i][1] - bounds[i][0])
        # store
        decoded.append(value)
    return decoded

请问能否将其改写为用实数数组而非比特串来编码解的形式?完整代码来自文章《Simple Genetic Algorithm From Scratch in Python》,完整代码如下:

# genetic algorithm search for continuous function optimization
from numpy.random import randint
from numpy.random import rand


# objective function
def objective(x):
    return x[0] ** 2.0 + x[1] ** 2.0


# decode bitstring to numbers
def decode(bounds, n_bits, bitstring):
    decoded = list()
    largest = 2 ** n_bits
    for i in range(len(bounds)):
        # extract the substring
        start, end = i * n_bits, (i * n_bits) + n_bits
        substring = bitstring[start:end]
        # convert bitstring to a string of chars
        chars = ''.join([str(s) for s in substring])
        # convert string to integer
        integer = int(chars, 2)
        # scale integer to desired range
        value = bounds[i][0] + (integer / largest) * (bounds[i][1] - bounds[i][0])
        # store
        decoded.append(value)
    return decoded


# tournament selection
def selection(pop, scores, k=3):
    # first random selection
    selection_ix = randint(len(pop))
    for ix in randint(0, len(pop), k - 1):
        # check if better (e.g. perform a tournament)
        if scores[ix] < scores[selection_ix]:
            selection_ix = ix
    return pop[selection_ix]


# crossover two parents to create two children
def crossover(p1, p2, r_cross):
    # children are copies of parents by default
    c1, c2 = p1.copy(), p2.copy()
    # check for recombination
    if rand() < r_cross:
        # select crossover point that is not on the end of the string
        pt = randint(1, len(p1) - 2)
        # perform crossover
        c1 = p1[:pt] + p2[pt:]
        c2 = p2[:pt] + p1[pt:]
    return [c1, c2]


# mutation operator
def mutation(bitstring, r_mut):
    for i in range(len(bitstring)):
        # check for a mutation
        if rand() < r_mut:
            # flip the bit
            bitstring[i] = 1 - bitstring[i]


# genetic algorithm
def genetic_algorithm(objective, bounds, n_bits, n_iter, n_pop, r_cross, r_mut):
    # initial population of random bitstring
    pop = [randint(0, 2, n_bits * len(bounds)).tolist() for _ in range(n_pop)]
    # keep track of best solution
    best, best_eval = 0, objective(decode(bounds, n_bits, pop[0]))
    # enumerate generations
    for gen in range(n_iter):
        # decode population
        decoded = [decode(bounds, n_bits, p) for p in pop]
        # evaluate all candidates in the population
        scores = [objective(d) for d in decoded]
        # check for new best solution
        for i in range(n_pop):
            if scores[i] < best_eval:
                best, best_eval = pop[i], scores[i]
                print(">%d, new best f(%s) = %f" % (gen, decoded[i], scores[i]))
        # select parents
        selected = [selection(pop, scores) for _ in range(n_pop)]
        # create the next generation
        children = list()
        for i in range(0, n_pop, 2):
            # get selected parents in pairs
            p1, p2 = selected[i], selected[i + 1]
            # crossover and mutation
            for c in crossover(p1, p2, r_cross):
                # mutation
                mutation(c, r_mut)
                # store for next generation
                children.append(c)
        # replace population
        pop = children
    return [best, best_eval]


# define range for input
bounds = [[-5.0, 5.0], [-5.0, 5.0]]
# define the total iterations
n_iter = 100
# bits per variable
n_bits = 16
# define the population size
n_pop = 100
# crossover rate
r_cross = 0.9
# mutation rate
r_mut = 1.0 / (float(n_bits) * len(bounds))
# perform the genetic algorithm search
best, score = genetic_algorithm(objective, bounds, n_bits, n_iter, n_pop, r_cross, r_mut)
print('Done!')
decoded = decode(bounds, n_bits, best)
print('f(%s) = %f' % (decoded, score))

改写后的实数编码遗传算法实现

当然可以直接用实数数组编码解,这样就不需要解码步骤,直接对实数向量进行选择、交叉、突变操作即可。以下是改写后的完整代码:

# 实数编码的遗传算法:连续函数优化
from numpy.random import rand
from numpy.random import randint


# 目标函数(以二元二次函数为例)
def objective(x):
    return x[0] ** 2.0 + x[1] ** 2.0


# 锦标赛选择
def selection(pop, scores, k=3):
    # 随机选择第一个候选者
    selection_ix = randint(len(pop))
    for ix in randint(0, len(pop), k - 1):
        # 锦标赛比较,保留更优解(这里是最小化问题)
        if scores[ix] < scores[selection_ix]:
            selection_ix = ix
    return pop[selection_ix]


# 实数交叉:模拟二进制交叉(SBX),适合连续变量
def crossover(p1, p2, bounds, r_cross):
    c1, c2 = p1.copy(), p2.copy()
    if rand() < r_cross:
        for i in range(len(p1)):
            # 生成0-1之间的随机数
            u = rand()
            if u <= 0.5:
                beta = (2 * u) ** (1 / (3 + 1))  # 分布指数设为3,可调整
            else:
                beta = (1 / (2 * (1 - u))) ** (1 / (3 + 1))
            # 计算交叉后的子代值
            c1[i] = 0.5 * ((1 + beta) * p1[i] + (1 - beta) * p2[i])
            c2[i] = 0.5 * ((1 - beta) * p1[i] + (1 + beta) * p2[i])
            # 确保值在边界范围内
            c1[i] = max(bounds[i][0], min(c1[i], bounds[i][1]))
            c2[i] = max(bounds[i][0], min(c2[i], bounds[i][1]))
    return [c1, c2]


# 实数突变:高斯突变
def mutation(individual, bounds, r_mut):
    for i in range(len(individual)):
        if rand() < r_mut:
            # 生成高斯分布的突变值,均值为当前值,标准差设为范围的10%
            sigma = (bounds[i][1] - bounds[i][0]) * 0.1
            mutated = individual[i] + rand() * sigma * 2 - sigma
            # 确保突变后的值在边界内
            individual[i] = max(bounds[i][0], min(mutated, bounds[i][1]))


# 遗传算法主函数
def genetic_algorithm(objective, bounds, n_iter, n_pop, r_cross, r_mut):
    # 初始化种群:直接生成符合边界的实数向量
    pop = [[bounds[i][0] + rand() * (bounds[i][1] - bounds[i][0]) for i in range(len(bounds))] 
           for _ in range(n_pop)]
    # 跟踪最优解
    best, best_eval = pop[0], objective(pop[0])
    # 迭代进化
    for gen in range(n_iter):
        # 评估所有个体
        scores = [objective(ind) for ind in pop]
        # 更新最优解
        for i in range(n_pop):
            if scores[i] < best_eval:
                best, best_eval = pop[i], scores[i]
                print(f">第{gen}代,找到新最优解 f({best}) = {best_eval:.6f}")
        # 选择父代
        selected = [selection(pop, scores) for _ in range(n_pop)]
        # 生成子代
        children = []
        for i in range(0, n_pop, 2):
            p1, p2 = selected[i], selected[i+1]
            # 交叉
            for c in crossover(p1, p2, bounds, r_cross):
                # 突变
                mutation(c, bounds, r_mut)
                children.append(c)
        # 替换种群
        pop = children
    return [best, best_eval]


# 配置参数
bounds = [[-5.0, 5.0], [-5.0, 5.0]]  # 变量范围
n_iter = 100  # 迭代代数
n_pop = 100  # 种群大小
r_cross = 0.9  # 交叉概率
r_mut = 1.0 / len(bounds)  # 突变概率(每个变量的突变概率)

# 运行遗传算法
best, score = genetic_algorithm(objective, bounds, n_iter, n_pop, r_cross, r_mut)
print('计算完成!')
print(f'最优解 f({best}) = {score:.6f}')

关键改动说明

  • 种群初始化:直接在bounds定义的范围内生成随机实数向量,无需二进制编码和解码步骤,简化流程。
  • 交叉操作:使用模拟二进制交叉(SBX),这是针对连续变量设计的交叉方式,能保持解的连续性和多样性,替代原代码的位交叉。
  • 突变操作:采用高斯突变,在当前个体值附近生成符合高斯分布的突变值,同时确保结果不超出边界,替代原代码的位翻转。
  • 去掉解码步骤:所有操作直接针对实数向量,评估时无需解码,效率更高。

内容的提问来源于stack exchange,提问作者No_Name

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 04:25:21