遗传算法一个小实现,及与对抗生成网络 变分自编码器的相似性


这里没有讲遗传算法的理论,理论先去看其他文章吧。
参考https://blog.youkuaiyun.com/baiyuxuan123123/article/details/114818224这个文章,编码做了一下这个题:


有一配送中心,负责将货物配送到指定的各个配送点。每个配送点有一定的货物需求量,用货物重量表示。
配送中心有若干辆车,每辆车有一定的载重量和里程限制,车的载重和行驶里程不可超过指定的值。
一辆车可负责一个或多个点的配送任务,且每个配送点只被服务一次。
在某一时刻,所有负责配送的车同时从配送中心出发,分别完成各自的配送任务后,再回到配送中心。
根据以上条件,设计一个最优配送方案。对于每个方案,应给出每辆车负责的配送点及其先后顺序。

为衡量方案之间的优劣,我们给出如下三个指标:
配送总时间t:从配送开始 到最后一辆车返回物流中心所经历的时间
车辆总里程s:所有配送车辆的里程之和
车辆总数n:用于配送的车辆数量
在实际应用中,我们为这三个指标分别分配一个权重 wt  ws  和 wn  用如下公式作为每个方案的得分:得分越高,方案越优。
score = − ( wt ⋅ t + ws ⋅ s + wn ⋅ n ) 


from collections import namedtuple
import random
import numpy as np


# ***** begin 配置信息 *****
max_iter = 500
population_size = 100
mutate_max_try_times = 20
select_ratio = 0.5

max_car_no = 10
min_car_no = 6    # 如果car_no设置得较小,可能会鉴权失败多次,效率差
cargo_site_total = 20
site_location_range = tuple(range(-100, 101, 5))
car_carrying_range = tuple(range(80, 501, 10))
car_speed_range = tuple(range(20, 51, 10))
move_cargo_second = 2    # 简化处理的每次装卸货时间, 也可以使用与货物重量相关的变量
max_cargo_weight = 100
max_car_distance = 1200

time_weight = 0.4
distance_weight = 0.3
cargo_no_weight = 1 - time_weight - distance_weight
# ***** end 配置信息 *****


# 配置计算  生成数据
cargo_sites = list(range(1, cargo_site_total + 1))
cargo_site_info = namedtuple("cargo_site_info", ("site_location_x", "site_location_y", "cargo_weight"))
cargo_site_info_dict = {site_no: cargo_site_info(
                            random.choice(site_location_range),
                            random.choice(site_location_range),
                            random.randint(0, max_cargo_weight))
                        for site_no in cargo_sites}
# 补充起点和终点的site_location信息,计算距离时会用到。最后一个weight用不到
cargo_site_info_dict[0] = cargo_site_info(0, 0, 0)

car_info = namedtuple("car_info", ("speed", "volume"))
car_info_dict = {car_no: car_info(random.choice(car_speed_range),
                                  random.choice(car_carrying_range))
                 for car_no in range(1, max_car_no + 1)}


def get_fitness_info(cargo_site_car_list, cargo_car_loc2carno):
    car_ix_consume_time, distances = calculate_fitness_info(cargo_site_car_list, cargo_car_loc2carno)
    return -(time_weight * max(car_ix_consume_time.values()) + distance_weight * sum(distances)
             + cargo_no_weight * len(cargo_car_loc2carno)), car_ix_consume_time


def calculate_fitness_info(cargo_site_car_list, cargo_car_loc2carno):
    distances = []
    car_ix_consume_time = {}    # car_ix 不是 car_no, 只为后面变异提供参考
    car_ix = 0
    last_site_no = 0   # 其实不会有0号site  这里是为了计算距离
    distance = 0
    for ix, x in enumerate(cargo_site_car_list):
        tmp_distance = np.linalg.norm(
            np.array([cargo_site_info_dict[x].site_location_x,
                      cargo_site_info_dict[x].site_location_y]) -
            np.array([cargo_site_info_dict[last_site_no].site_location_x,
                      cargo_site_info_dict[last_site_no].site_location_y]))
        if x > 0:
            distance += tmp_distance
            last_site_no = x
        else:
            distances.append(distance)
            consume_time = (distance / car_info_dict[cargo_car_loc2carno[ix]].speed) + move_cargo_second
            car_ix += 1
            car_ix_consume_time[car_ix] = consume_time
            last_site_no = 0
            distance = 0
    return car_ix_consume_time, distances


def get_a_possible_try():
    cargo_site_list = random.sample(cargo_sites, cargo_site_total)
    try_times = 0
    while True:
        car_list = random.sample(range(1, max_car_no + 1), random.randint(min_car_no, max_car_no))
        # 不生成在cargo_sites的第一位和最后一位
        car_location = random.sample(range(2, cargo_site_total - 1), len(car_list) - 1)
        car_location.append(cargo_site_total)  # 最后 一辆车
        car_location.sort()
        car_location2no = {location: car_no for location, car_no in zip(car_location, car_list)}
        # 其实不做强鉴权 或以一定概率 保持不合法的解 也是可以的,后面可能变异成 合法的解
        if cargo_site_and_car_location_authok(cargo_site_list, car_location, car_location2no):
            break
        try_times += 1
    if try_times >= 40:
        print("try times is %s:" % try_times)
    cargo_site_car_list, cargo_car_loc2carno = get_cargo_site_car_list(car_location, cargo_site_list, car_list)
    return cargo_site_car_list, cargo_car_loc2carno


def get_cargo_site_car_list(car_location, cargo_site_list, car_list):
    cargo_site_car_list = cargo_site_list.copy()
    for location in car_location[::-1]:
        cargo_site_car_list.insert(location, 0)
    cargo_car_loc2carno = get_cargo_car_loc2carno(cargo_site_car_list, car_list)
    return cargo_site_car_list, cargo_car_loc2carno


def get_cargo_car_loc2carno(cargo_site_car_list, car_list):
    ix = 0
    cargo_car_loc2carno = {}
    for location_ix, location in enumerate(cargo_site_car_list):
        if location == 0:
            cargo_car_loc2carno[location_ix] = car_list[ix]
            ix += 1
    return cargo_car_loc2carno


def cargo_site_and_car_location_authok(cargo_site_list, car_location, car_location2no):
    # 如果生成连续两个0  两辆车连在一起 后一辆未载重 放弃
    if (np.diff(np.array(car_location)) == 0).any():
        return False
    # 如果car_no较小,可能会鉴权失败多次,效率差
    # print("cargo_site_list and car_location is ", cargo_site_list, car_location)
    last_index = 0
    for location in car_location:
        tmp_cargo_sites = cargo_site_list[last_index: location]
        if sum([cargo_site_info_dict[cargo_site_no].cargo_weight for cargo_site_no in tmp_cargo_sites]
               ) > car_info_dict[car_location2no[location]].volume:
            return False
        last_index = location
    return True


def cargo_site_car_list_authok(cargo_site_car_list, cargo_car_loc2carno):
    last_index = 0
    for location_ix, location in enumerate(cargo_site_car_list):
        if location == 0:
            tmp_cargo_sites = cargo_site_car_list[last_index: location_ix]
            if sum([cargo_site_info_dict[cargo_site_no].cargo_weight for cargo_site_no in tmp_cargo_sites]
                   ) > car_info_dict[cargo_car_loc2carno[location_ix]].volume:
                # print("location_index", location_index, car_info_dict[car_ix + 1],  car_ix + 1,)
                return False
            last_index = location_ix
    return True


def get_population_by_size(size=population_size):
    population_info = [get_a_possible_try() for _ in range(size)]
    return population_info


def get_fitness_by_length(population_info):
    population_fitness_info = []
    for cargo_site_car_list, cargo_car_loc2carno in population_info:
        population_fitness_info.append((*get_fitness_info(cargo_site_car_list, cargo_car_loc2carno),
                                        cargo_site_car_list, cargo_car_loc2carno))
    return population_fitness_info


def cross_and_mutation_by_fitness(population_fitness_info):
    new_pop = []
    pass


def select_mutate_multiply(population_fitness_info, select_ratio=select_ratio):
    new_population_info = []
    # select
    population_fitness_info.sort(key=lambda x: x[0], reverse=True)
    new_population_fitness_info = population_fitness_info[:int(len(population_fitness_info) * select_ratio + 1)]
    # 在select 后的基础上变异
    for fitness_score, car_ix_consume_time, cargo_site_car_list, cargo_car_loc2carno in new_population_fitness_info:
        new_population_info.append(mutate(
            cargo_site_car_list, cargo_car_loc2carno, car_ix_consume_time))
    # 繁殖新的一批
    new_population_info.extend([get_a_possible_try() for _ in range(int(len(population_fitness_info) * select_ratio))])
    return new_population_info


def mutate(cargo_site_car_list, cargo_car_loc2carno, car_ix_consume_time, max_try_times=mutate_max_try_times):
    try_times = 0
    while try_times <= max_try_times:
        # 这只是一种变异的方式 也可以根据fitness的各项指标 分别设计
        to_move_min_ix, to_move_max_ix, to_move_max_value = get_move_info(
            cargo_site_car_list, cargo_car_loc2carno, car_ix_consume_time)
        new_cargo_site_car_list, new_cargo_car_loc2carno = get_move_result(
            cargo_site_car_list, cargo_car_loc2carno, to_move_min_ix, to_move_max_ix, to_move_max_value)
        if cargo_site_car_list_authok(new_cargo_site_car_list, new_cargo_car_loc2carno):
            # print("mutate success. try times is %s:" % try_times)
            return new_cargo_site_car_list, new_cargo_car_loc2carno
        try_times += 1
    # print("mutate fail after try times is %s:" % max_try_times)
    return cargo_site_car_list, cargo_car_loc2carno


def get_move_info(cargo_site_car_list, cargo_car_loc2carno, car_ix_consume_time):
    # 把耗时最长的车辆对应的货物点 移一个加到耗时最短的车辆对应的货物点
    sorted_car_time = sorted(car_ix_consume_time.items(), key=lambda x: x[1])
    min_ix = sorted_car_time[0][0]
    max_ix = sorted_car_time[-1][0]

    larger_range = list(cargo_car_loc2carno.keys())[max(max_ix - 2, 0): max_ix]
    smaller_range = list(cargo_car_loc2carno.keys())[max(min_ix - 2, 0): min_ix]

    to_move_min_ix = random.choice(list(range(smaller_range[0], smaller_range[1]))
                                   ) if len(smaller_range) > 1 else smaller_range[0]
    to_move_max_ix = random.choice(list(range(larger_range[0] + 1, larger_range[1]))
                                   ) if len(larger_range) > 1 else larger_range[0]
    to_move_max_value = cargo_site_car_list[to_move_max_ix]
    return to_move_min_ix, to_move_max_ix, to_move_max_value


def get_move_result(cargo_site_car_list, cargo_car_loc2carno, to_move_min_ix, to_move_max_ix, to_move_max_value):
    new_cargo_site_car_list = []
    for location_ix, location in enumerate(cargo_site_car_list):
        if location_ix != to_move_max_ix:
            new_cargo_site_car_list.append(location)
        if location_ix == to_move_min_ix:
            new_cargo_site_car_list.append(to_move_max_value)

    # car_list 也可以在get_a_possible_try中 保存下来减少计算
    car_list = [car_no for (_, car_no) in sorted(cargo_car_loc2carno.items(), key=lambda x: x[0])]
    new_cargo_car_loc2carno = get_cargo_car_loc2carno(new_cargo_site_car_list, car_list)
    return new_cargo_site_car_list, new_cargo_car_loc2carno


def print_final_result(population_info):
    population_fitness_info = get_fitness_by_length(population_info)
    population_fitness_info.sort(key=lambda x: x[0], reverse=True)
    fitness_score, car_ix_consume_time, cargo_site_car_list, cargo_car_loc2carno = population_fitness_info[0]
    print("\nthe final best score is %s cargo_site_car_list is %s, where the 0s representing car_list is %s."
          % (round(fitness_score, 2), cargo_site_car_list,
          [car_no for (_, car_no) in sorted(cargo_car_loc2carno.items(), key=lambda x: x[0])]))
    return None


def run():
    population_info = get_population_by_size()
    for ix in range(max_iter):
        population_fitness_info = get_fitness_by_length(population_info)
        population_info = select_mutate_multiply(population_fitness_info)
        if ix % 10 == 0:
            print("run times %s, score is: %s" % (ix, np.mean(np.array([x[0] for x in population_fitness_info]))))
    print_final_result(population_info)


if __name__ == '__main__':
    run()


没有做可视化,目前只通过打印日志,看到分数的下降。  
小说明,每次运行时 货物点等数据都是动态生成的。当然如果想做其他观察可以生成后保存下来 每次重新load.


另外: 看到这篇介绍 https://blog.youkuaiyun.com/lqk1985/article/details/7103875, 看到这里面的图, 你第一想到什么?
根据本人学习顺序,第一反应当然是对抗生成网络。

评价扇贝像不像Firefox的函数“适应函数”  不就相当于判别器D么? 变异 不就相当于生成器(虽然没有依据正态分布和均匀分布去产生)么? 不过这里的变异 没法弱化D的判别能力,因为GAN里的生成器的损失函数 包含了判别器D的输出。这里的变异,只是变异本身。
“当然,它也有缺点,比如说早期的优势基因可能会很快通过交换基因的途径散播到整个种群中,这样有可能导致早熟(premature),也就是说整个种群的基因过早同一化,得不到足够好的结果” --他竟然提到了 模型坍塌!!  内味更多了,是不?
那再类比想想, 遗传算法中的变异,有没有机会 去影响到适应函数,让适应函数去自我提升(提升适应函数 是为了反过来再提升变异)?  好像没法子呵? 如果你想到了,可以通知我一下。


旁边的高人妹子 说她看到图片,第一想到的是 一个不可描述的超空间,用某种方式 不停的去接近它。 这不就是VAE(主要是变分推断)么? 


与强化学习的 value-based, policy-based比呢,也有那么一点点相似之处?  
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值