遗传算法个人入门笔记

先举一个简单的求解例子:

变量x,y

函数f(x,y) = (x-5)^2 + (y+3)^2 - 5

求最小值。

def test(x,y):
    return (x - 5)**2 + (y - 3)**2 - 5

显然,这个函数在x=5,y=3时取最小值-5。现在我们尝试用遗传算法解决之。

遗传算法主要是模拟生物进化的过程,将每一个值视作一个生物,有自己的DNA,会发生交叉变异,同时“适者生存”。

1.编码:

规定x,y的取值范围是[-10,10],并将其映射在一个长度为12的二进制列表中:

尽管严格来说,长度为12的二进制列表最大的值为2^12-1=4095。但为了化简逆映射的求解,我们视作4096,使之可被除尽。此时x,y的取值为[-10,10)。

区间长度是20,2^12 = 4096,20/4096 = 0.0048828125。可以知道二进制数每增长1,对应的10进制数大约增长0.0048828125。

写出其编码函数:

def encode(n,n_min,n_max,dna_length):
    n_range = n_max - n_min
    pies= n_range / 2**dna_length
    res = n - n_min
    n_bit = res / pies
    return get_bits_list(n_bit,dna_length)

2.解码函数:

def decode(dna,dna_length):
    result = 0
    for i in range(dna_length):
        result += dna[i] * (2**i)
    return result

将这些代码封装到一个类中:

class my_ga():
    def __init__(self,n_min,n_max,dna_length):
        self.n_min = n_min
        self.n_max = n_max
        self.dna_length = dna_length
        self.n_range = self.n_max - self.n_min
        self.pies= self.n_range / 2**self.dna_length

    def test(x,y):
        return (x - 5)**2 + (y - 3)**2 - 5

    def encode(self,n):
        res = n - self.n_min
        n_bit = res / self.pies
        print(f"n_bit is {n_bit}")
        return self.get_bits_list(n_bit)

    def decode(self,dna):
        result = 0
        for i in range(self.dna_length):
            result += dna[i] * (2**i)
            print(f"result now is {result}")
        origin = result * self.pies + self.n_min
        return origin

3.种群:

交叉与变异是建立在种群的基础上的。我们将一个(x,y)的二进制条件下的数值对视为一个个体,它们组成一个种群。先将种群数设为1000,同时,定义一个计算适应性的函数。

    def get_adapt(self):
        adapt = np.zeros(shape=(1000))

        for i in range(1000):
            adapt[i] = -self.test(self.pop_dna[i])

        adapt_max = np.max(adapt)
        adapt_min = np.min(adapt)
        adapt_normal = (adapt-adapt_min)/(adapt_max-adapt_min) + 0.001
        return adapt_normal
        self.pop_dna = np.random.randint(2,size=(1000,2,dna_length))
        self.adapt_normal = self.get_adapt()

接着定义根据适应性选择个体的函数:

def select(self):
        idx = np.random.choice(np.arange(1000),size=(1000), replace=True, p=(self.adapt_normal)/(self.adapt_normal.sum()))
        return self.pop_dna[idx]

为了让算法更灵活,我们将1000换为pop_size。

4.交叉与变异:

交叉与变异并非是必然发生的,我们需要定义一个交叉率与变异率,同时定义交叉、变异函数:

    def cross(self,child):
        if np.random.rand() < self.cross_rate:
            mother = self.pop_dna[np.random.randint(self.pop_size)]
            for j in range(2):
                cross_point = np.random.randint(low=0, high=self.pop_size)
                child[j][cross_point:] = mother[j][cross_point:]
        return child

    def muta(self,child):
        if np.random.rand() < self.muta_rate:
            muta_dna = np.random.randint(0,2)
            muta_point = np.random.randint(0,self.pop_size)
            child[muta_dna][muta_point] ^= 1
        return child
    
    def cross_muta(self):
        new_pop = self.pop_dna.copy()
        for i in range(self.pop_size):
            new_pop[i] = self.cross(new_pop[i])
            new_pop[i] = self.muta(new_pop[i])
        return new_pop

输出函数:

def get_best(self):
        the_min = 1e9
        x = 0
        y = 0
        for i in range(self.pop_size):
            if the_min > self.test(self.pop_dna[i]):
               the_min = self.test(self.pop_dna[i])
               x = self.decode(self.pop_dna[i][0])
               y = self.decode(self.pop_dna[i][1])
            
        print(f"best indiv is ({x},{y}), result is {the_min}")

测试:

ga = my_ga(n_min=-10,n_max=10,dna_length=12,pop_size=1000,cross_rate=0.8,muta_rate=0.08)

print(ga.pop_dna)
genration = 10

for _ in range(genration):
    ga.cross_muta()
    ga.select()
    ga.get_adapt()
    ga.get_best()

完整、且标准的含注释代码:

import numpy as np

class GeneticAlgorithm:
    # 初始化方法,设置遗传算法的参数
    def __init__(self, n_min, n_max, dna_length, pop_size, cross_rate, muta_rate):
        self.n_min = n_min  # 最小范围
        self.n_max = n_max  # 最大范围
        self.dna_length = dna_length  # DNA长度
        self.pop_size = pop_size  # 种群大小
        self.cross_rate = cross_rate  # 交叉概率
        self.muta_rate = muta_rate  # 变异概率
        self.n_range = n_max - n_min  # 范围差值
        self.unit_interval = self.n_range / (2**dna_length)  # 每个DNA单元对应的实际数值范围
        self.population = np.random.randint(2, size=(pop_size, 2, dna_length))  # 随机初始化种群
        self.normalized_fitness = self.calculate_fitness()  # 初始化适应度

    # 目标函数,需要最小化的函数
    def objective_function(self, individual):
        x = self.decode(individual[0])  # 解码第一个DNA片段
        y = self.decode(individual[1])  # 解码第二个DNA片段
        return (x - 5)**2 + (y - 3)**2 - 5  # 计算适应度值

    # 将数值编码为二进制DNA片段
    def encode(self, n):
        res = n - self.n_min  # 转换为范围内的数值
        n_binary = res / self.unit_interval  # 转换为二进制数
        return self.to_binary_list(n_binary)

    # 将二进制DNA片段解码为数值
    def decode(self, dna):
        result = 0
        for i in range(self.dna_length):
            result += dna[i] * (2**i)
        decoded_value = result * self.unit_interval + self.n_min
        return decoded_value

    # 计算适应度值并进行归一化
    def calculate_fitness(self):
        fitness = np.zeros(shape=(self.pop_size))  # 初始化适应度数组
        for i in range(self.pop_size):
            fitness[i] = -self.objective_function(self.population[i])  # 计算适应度值
        max_fitness = np.max(fitness)
        min_fitness = np.min(fitness)
        normalized_fitness = (fitness - 0.001 - min_fitness) / (max_fitness - min_fitness) + 0.01
        self.normalized_fitness = normalized_fitness
        return normalized_fitness

    # 选择适应度高的个体进行繁殖
    def select_population(self):
        indices = np.random.choice(np.arange(self.pop_size), size=(self.pop_size), replace=True, p=(self.normalized_fitness) / (self.normalized_fitness.sum()))
        self.population = self.population[indices]

    # 交叉操作,生成新个体
    def crossover(self, offspring):
        if np.random.rand() < self.cross_rate:
            parent = self.population[np.random.randint(self.pop_size)]
            for j in range(2):
                crossover_point = np.random.randint(low=0, high=self.dna_length)
                offspring[j][crossover_point:] = parent[j][crossover_point:]
        return offspring

    # 变异操作
    def mutate(self, offspring):
        if np.random.rand() < self.muta_rate:
            muta_dna = np.random.randint(0, 2)
            muta_point = np.random.randint(0, self.dna_length)
            offspring[muta_dna][muta_point] ^= 1
        return offspring

    # 对整个种群应用交叉和变异操作
    def apply_crossover_and_mutation(self):
        new_population = self.population.copy()
        for i in range(self.pop_size):
            new_population[i] = self.crossover(new_population[i])
            new_population[i] = self.mutate(new_population[i])
        return new_population

    # 获取当前种群中最优的个体
    def get_best_individual(self):
        best_fitness = float('inf')
        best_x, best_y = 0, 0
        for i in range(self.pop_size):
            current_fitness = self.objective_function(self.population[i])
            if best_fitness > current_fitness:
                best_fitness = current_fitness
                best_x = self.decode(self.population[i][0])
                best_y = self.decode(self.population[i][1])
        print(f"Best individual is ({best_x}, {best_y}), fitness is {best_fitness}")

# 初始化遗传算法对象
ga = GeneticAlgorithm(n_min=-10, n_max=10, dna_length=16, pop_size=500, cross_rate=0.8, muta_rate=0.08)
generations = 10

# 迭代执行遗传算法
for _ in range(generations):
    ga.apply_crossover_and_mutation()
    ga.select_population()
    ga.calculate_fitness()
    ga.get_best_individual()

输出如下:

自此入门成功......

下一篇:用遗传算法处理2024数学建模国赛C题(咕咕咕中......)

ps:由于这个函数过于简单,并没有体现进化的过程。但我懒得改了。

这是个更显著的代码:

import numpy as np

class GeneticAlgorithm:
    # 初始化方法,设置遗传算法的参数
    def __init__(self, n_min, n_max, dna_length, pop_size, cross_rate, muta_rate,dna_kind_size):
        self.n_min = n_min  # 最小范围
        self.n_max = n_max  # 最大范围
        self.dna_length = dna_length  # DNA长度
        self.pop_size = pop_size  # 种群大小
        self.cross_rate = cross_rate  # 交叉概率
        self.muta_rate = muta_rate  # 变异概率
        self.n_range = n_max - n_min  # 范围差值
        self.dna_kind_size = dna_kind_size
        self.unit_interval = self.n_range / (2**dna_length)  # 每个DNA单元对应的实际数值范围
        self.population = np.random.randint(0, dna_kind_size, size=(pop_size, dna_kind_size, dna_length))  # 随机初始化种群
        self.normalized_fitness = self.calculate_fitness()  # 初始化适应度

    # 目标函数,需要最小化的函数
    def objective_function(self, individual):
        x = self.decode(individual[0])  # 解码第一个DNA片段
        y = self.decode(individual[1])  # 解码第二个DNA片段
        z = self.decode(individual[2]) 
        return -((x - 5)**2 + (y - 3)**2 + (z + 2)**2) + 5  # 计算适应度值

    # 将二进制DNA片段解码为数值
    def decode(self, dna):
        result = 0
        for i in range(self.dna_length):
            result += dna[i] * (2**i)
        decoded_value = result * self.unit_interval + self.n_min
        return decoded_value

    # 计算适应度值并进行归一化
    def calculate_fitness(self):
        fitness = np.zeros(shape=(self.pop_size))  # 初始化适应度数组
        for i in range(self.pop_size):
            fitness[i] = self.objective_function(self.population[i])  # 计算适应度值
        max_fitness = np.max(fitness)
        min_fitness = np.min(fitness)
        normalized_fitness = (fitness - min_fitness) / (max_fitness - min_fitness + 0.00001) + 0.001
        self.normalized_fitness = normalized_fitness
        return normalized_fitness

    # 选择适应度高的个体进行繁殖
    def select_population(self):
        print("normallized_fitness_sum is ",self.normalized_fitness.sum())
        indices = np.random.choice(np.arange(self.pop_size), size=(self.pop_size), replace=True, p=(self.normalized_fitness) / (self.normalized_fitness.sum()))
        self.population = self.population[indices]

    # 交叉操作,生成新个体
    def crossover(self, offspring):
        if np.random.rand() < self.cross_rate:
            parent = self.population[np.random.randint(self.pop_size)]
            for j in range(self.dna_kind_size):
                crossover_point = np.random.randint(low=0, high=self.dna_length)
                offspring[j][crossover_point:] = parent[j][crossover_point:]
        return offspring

    # 变异操作
    def mutate(self, offspring):
        if np.random.rand() < self.muta_rate:
            muta_dna = np.random.randint(0, self.dna_kind_size)
            muta_point = np.random.randint(0, self.dna_length)
            offspring[muta_dna][muta_point] ^= 1
        return offspring

    # 对整个种群应用交叉和变异操作
    def apply_crossover_and_mutation(self):
        new_population = self.population.copy()
        for i in range(self.pop_size):
            new_population[i] = self.crossover(new_population[i])
            new_population[i] = self.mutate(new_population[i])
        return new_population

    # 获取当前种群中最优的个体
    def get_best_individual(self):
        best_fitness = float('inf')
        best_x, best_y,best_z = 0, 0, 0
        for i in range(self.pop_size):
            current_fitness = self.objective_function(self.population[i])
            if best_fitness > current_fitness:
                best_fitness = current_fitness
                best_x = self.decode(self.population[i][0])
                best_y = self.decode(self.population[i][1])
                best_z = self.decode(self.population[i][2])
        print(f"Best individual is ({best_x}, {best_y}, {best_z}), fitness is {best_fitness}")

# 初始化遗传算法对象
ga = GeneticAlgorithm(n_min=-10, n_max=10, dna_length=16, pop_size=10000, cross_rate=0.8, muta_rate=0.08,dna_kind_size = 3)
generations = 50

# 迭代执行遗传算法
for _ in range(generations):
    ga.apply_crossover_and_mutation()
    ga.select_population()
    ga.calculate_fitness()
    
ga.get_best_individual()
posted @ 2025-01-19 21:40  minatomorin  阅读(78)  评论(0)    收藏  举报