Untitled

 avatar
unknown
python
6 months ago
9.0 kB
3
Indexable
import random
from constraints import ensure_bean_crop_in_future_three_years, mushroom_season_constraints, no_continuous_crop_rotation_constraints_1,crop_location_season_constraints_v2,is_valid_for_greenhouse_second_season,is_valid_for_irrigated_land_second_season

def selection(particles, num_selected):
    """
    轮盘赌选择操作,根据粒子的适应度值选择一定数量的粒子参与交叉和变异。
    :param particles: 当前粒子群
    :param num_selected: 需要选择的粒子数量
    :return: 被选中的粒子列表
    """
    fitness_values = [particle.best_value for particle in particles]
    
    # 将适应度标准化为正值
    min_fitness = min(fitness_values)
    if min_fitness < 0:
        fitness_values = [fitness - min_fitness for fitness in fitness_values]
    
    total_fitness = sum(fitness_values)
    selection_probs = [fitness / total_fitness for fitness in fitness_values]
    
    # 使用权重选择粒子
    selected_particles = random.choices(particles, weights=selection_probs, k=num_selected)
    
    return selected_particles

def crossover_with_constraints(parent1, parent2, crossover_point, land_info, crop_type_mapping, generate_random_position_with_constraints, previous_year_crops_mapping):
    """
    单点交叉操作后,验证约束,若有错误使用传递的生成新粒子函数重新生成。
    """
    try:
        child1 = parent1[:crossover_point] + parent2[crossover_point:]
        child2 = parent2[:crossover_point] + parent1[crossover_point:]

        if not validate_child_constraints(child1, land_info, 2024, previous_year_crops_mapping, crop_type_mapping):
            print("子代1不符合约束,生成新粒子替代")
            child1 = generate_random_position_with_constraints(land_info, crop_type_mapping)

        if not validate_child_constraints(child2, land_info, 2024, previous_year_crops_mapping, crop_type_mapping):
           # print("子代2不符合约束,生成新粒子替代")
            child2 = generate_random_position_with_constraints(land_info, crop_type_mapping)

        return child1, child2

    except IndexError:
        #print("交叉操作中出现索引错误,生成新粒子替代")
        return generate_random_position_with_constraints(land_info, crop_type_mapping), generate_random_position_with_constraints(land_info, crop_type_mapping)


def validate_child_constraints(child, land_info, current_year, previous_year_crops_mapping, crop_type_mapping):
    """
    验证子代是否符合所有约束条件。
    :param child: 子代粒子
    :param land_info: 地块信息
    :param current_year: 当前年份
    :param previous_year_crops_mapping: 上一年种植情况
    :param crop_type_mapping: 作物类型映射
    :return: 如果子代符合所有约束,返回 True,否则返回 False
    """
    single_season_lands = [land for land in land_info if land_info[land]['type'] == '旱地']
    
    for land_idx, land_name in enumerate(land_info.keys()):
        for year_idx in range(len(child[land_idx])):  # 遍历年份
            num_seasons = 1 if land_name in single_season_lands else 2
            land_type = land_info[land_name]['type']
            
            for season_idx in range(num_seasons):  # 遍历季节
                for crop_idx in range(len(child[land_idx][year_idx][season_idx])):
                    crop_data = child[land_idx][year_idx][season_idx][crop_idx]

                    # 检查是否符合所有约束
                    if not (ensure_bean_crop_in_future_three_years(land_name, child, current_year, previous_year_crops_mapping) and
                            no_continuous_crop_rotation_constraints_1(land_type, crop_data, child[land_idx][year_idx-1][season_idx] if year_idx > 0 else []) and
                            crop_location_season_constraints_v2(land_type, season_idx, crop_data[0]) and
                            mushroom_season_constraints(land_type, season_idx, crop_data[0]) and
                            is_valid_for_greenhouse_second_season(land_type, season_idx, crop_data[0], ['榆黄菇', '香菇', '白灵菇', '羊肚菌']) and
                            is_valid_for_irrigated_land_second_season(land_type, season_idx, crop_data[0], ['大白菜', '白萝卜', '红萝卜'])):
                        return False  # 不符合约束,返回 False
                    
    return True  # 符合所有约束



def mutate_crop_selection_or_area(crop_data, land_info, crop_type_mapping, land_name):
    """
    变异操作,随机改变作物的选择或种植面积。
    :param crop_data: 当前的作物信息
    :param land_info: 地块信息
    :param crop_type_mapping: 作物类型映射
    :param land_name: 当前地块的名称,用于从 land_info 中获取面积信息
    :return: 变异后的作物信息
    """
    # 随机决定变异是改变作物还是改变面积
    try:
        if random.random() < 0.5:
            # 随机改变作物
            crop_data[0] = random.choice(list(crop_type_mapping.keys()))
        else:
            # 根据 land_name 获取地块的面积
            max_area = land_info[land_name]['area']
            # 随机改变种植面积
            crop_data[3] = random.uniform(0, max_area)
    
    except KeyError:
        print(f"KeyError: '{land_name}' 地块中缺少 'area' 属性。")
    
    return crop_data


def validate_structure(particle):
    """
    验证粒子数据结构的完整性,确保没有空的季节或作物数据。
    """
    for land_idx in range(len(particle)):
        for year_idx in range(len(particle[land_idx])):
            for season_idx in range(len(particle[land_idx][year_idx])):
                if not isinstance(particle[land_idx][year_idx][season_idx], list) or len(particle[land_idx][year_idx][season_idx]) == 0:
                    return False
    return True

def mutate(particle, land_info, crop_type_mapping, generate_random_position_with_constraints, current_year, previous_year_crops_mapping, single_season_lands, mutation_rate=0.05):
    """
    变异操作,若变异出错则使用传递的函数生成新粒子替代。
    """
    try:
        for land_idx, land_name in enumerate(land_info.keys()):
            for year_idx in range(len(particle.position[land_idx])):  # 遍历年份
                num_seasons = 1 if land_name in single_season_lands else 2
                land_type = land_info[land_name]['type']

                for season_idx in range(num_seasons):  # 遍历季节
                    for crop_idx in range(len(particle.position[land_idx][year_idx][season_idx])):
                        if random.random() < mutation_rate:
                            crop_data = particle.position[land_idx][year_idx][season_idx][crop_idx]
                            original_crop_data = crop_data[:]

                            crop_data = mutate_crop_selection_or_area(crop_data, land_info, crop_type_mapping,land_name)

                            if (ensure_bean_crop_in_future_three_years(land_name, particle.position, current_year, previous_year_crops_mapping) and
                                no_continuous_crop_rotation_constraints_1(land_type, crop_data, particle.position[land_idx][year_idx-1][season_idx] if year_idx > 0 else []) and
                                crop_location_season_constraints_v2(land_type, season_idx, crop_data[0]) and
                                mushroom_season_constraints(land_type, season_idx, crop_data[0]) and
                                is_valid_for_greenhouse_second_season(land_type, season_idx, crop_data[0], ['榆黄菇', '香菇', '白灵菇', '羊肚菌']) and
                                is_valid_for_irrigated_land_second_season(land_type, season_idx, crop_data[0], ['大白菜', '白萝卜', '红萝卜'])):
                                particle.position[land_idx][year_idx][season_idx][crop_idx] = crop_data
                            else:
                                particle.position[land_idx][year_idx][season_idx][crop_idx] = original_crop_data
        return particle

    except IndexError:
        #print("变异操作中出现索引错误,生成新粒子替代")
        return generate_random_position_with_constraints(land_info, crop_type_mapping)

def validate_structure(particle):
    """
    验证粒子数据结构的完整性,确保没有空的季节或作物数据。
    """
    for land_idx in range(len(particle)):
        for year_idx in range(len(particle[land_idx])):
            for season_idx in range(len(particle[land_idx][year_idx])):
                if not isinstance(particle[land_idx][year_idx][season_idx], list) or len(particle[land_idx][year_idx][season_idx]) == 0:
                    return False
    return True
Editor is loading...
Leave a Comment