Files
AIM-PIbd-31-Anisin-R-S/lab_10/lab10.ipynb
2025-05-16 23:03:07 +04:00

21 KiB
Raw Blame History

Лабораторная 10

Датасет: Knapsack Problem Data (https://www.kaggle.com/datasets/warcoder/knapsack-problem)

In [2]:
import pandas as pd

df = pd.read_csv(".//static//csv//knapsack_5_items.csv")

df
Out[2]:
<style scoped=""> .dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; } </style>
Weights Prices Capacity Best picks Best price
0 [46 40 42 38 10] [12 19 19 15 8] 40 [0. 1. 0. 0. 0.] 19.0
1 [11 31 4 6 7] [ 2 8 18 16 3] 64 [1. 1. 1. 1. 1.] 47.0
2 [32 49 27 37 24] [19 16 16 4 1] 87 [1. 0. 1. 0. 1.] 36.0
3 [20 35 22 23 16] [19 17 19 9 1] 21 [1. 0. 0. 0. 0.] 19.0
4 [ 7 12 19 13 20] [10 11 18 15 5] 50 [0. 1. 1. 1. 0.] 44.0
... ... ... ... ... ...
9995 [18 12 11 49 32] [12 3 17 19 7] 41 [1. 1. 1. 0. 0.] 32.0
9996 [20 2 24 7 7] [17 12 4 3 8] 17 [0. 1. 0. 1. 1.] 23.0
9997 [43 43 5 15 23] [15 5 7 2 7] 62 [1. 0. 1. 0. 0.] 22.0
9998 [49 9 15 21 39] [11 15 3 12 19] 65 [0. 1. 1. 0. 1.] 37.0
9999 [25 36 42 19 39] [15 12 7 18 12] 79 [1. 0. 0. 1. 0.] 33.0

10000 rows × 5 columns

Хромосома - набор 0 и 1, 1 - предмет выбран, 0 - не выбран. Ген - одно значение в хромосоме.

Функция генерации начальной популяции

In [4]:
import random

def create_individual(length):
    individual = []
    for _ in range(length):
        individual.append(random.randint(0, 1))
    return individual

def create_population(length, size):
    population = []
    for _ in range(size):
        population.append(create_individual(length))
    return population

create_population(5, 10)
Out[4]:
[[0, 0, 1, 1, 1],
 [1, 0, 0, 1, 1],
 [0, 1, 1, 1, 0],
 [1, 1, 0, 1, 1],
 [0, 0, 0, 0, 1],
 [0, 1, 0, 1, 1],
 [1, 1, 0, 0, 1],
 [1, 0, 1, 1, 1],
 [1, 0, 0, 0, 0],
 [1, 0, 1, 1, 0]]

Фитнес-функция

In [5]:
def evaluate_fitness(individual, weights, prices, capacity):
    total_value = 0
    total_weight = 0
    
    for selected, weight, price in zip(individual, weights, prices):
        if selected:
            total_value += price
            total_weight += weight
            
    if total_weight > capacity:
        return 0
    return total_value

evaluate_fitness([0, 1, 1, 1, 0], [7, 12, 19, 13, 20], [10, 11, 18, 15, 5], 50)
Out[5]:
44

Оператор кроссинговера

In [6]:
def crossover(parent1, parent2):
    point = random.randint(1, len(parent1) - 1)
    return (parent1[:point] + parent2[point:], parent2[:point] + parent1[point:])

crossover([0, 1, 1, 1, 0], [1, 0, 1, 0, 0])
Out[6]:
([0, 1, 1, 0, 0], [1, 0, 1, 1, 0])

Операторы мутации

In [7]:
import random

def mutate_flip_bits(individual, mutation_rate):
    for i in range(len(individual)):
        chance = random.random()
        if chance < mutation_rate:
            individual[i] = 1 - individual[i]

def mutate_swap_genes(individual, mutation_rate):
    chance = random.random()
    if chance < mutation_rate and len(individual) >= 2:
        i, j = random.sample(range(len(individual)), 2)
        individual[i], individual[j] = individual[j], individual[i]

individual = [0, 1, 1, 1, 0]
print("До мутации:", individual)

mutate_flip_bits(individual, 0.5)
print("После побитовой мутации:", individual)

mutate_swap_genes(individual, 1)
print("После свапа генов:", individual)
До мутации: [0, 1, 1, 1, 0]
После побитовой мутации: [1, 1, 0, 1, 0]
После свапа генов: [1, 0, 1, 1, 0]

Реализация генетического алгоритма и его применение

In [8]:
import ast
import re

population_size = 100
num_generations = 10
mutation_rate = 0.1
mutation_strategy = 'flip'

def select_parents(population, weights, prices, capacity):
    fitness_list = [evaluate_fitness(ind, weights, prices, capacity) for ind in population]
    total_fitness = sum(fitness_list)

    if total_fitness == 0:
        return random.choice(population), random.choice(population)

    selection_probs = [fitness / total_fitness for fitness in fitness_list]
    selected = random.choices(population, weights=selection_probs, k=2)
    return selected[0], selected[1]

def genetic_algorithm(weights, prices, capacity,
                      population_size=100, num_generations=10,
                      mutation_rate=0.1, mutation_strategy='flip'):

    num_elements = len(weights)
    population = create_population(num_elements, population_size)

    for generation in range(num_generations):
        next_population = []

        for _ in range(population_size // 2):
            parent1, parent2 = select_parents(population, weights, prices, capacity)
            child1, child2 = crossover(parent1, parent2)

            if mutation_strategy == 'flip':
                mutate_flip_bits(child1, mutation_rate)
                mutate_flip_bits(child2, mutation_rate)
            elif mutation_strategy == 'swap':
                mutate_swap_genes(child1, mutation_rate)
                mutate_swap_genes(child2, mutation_rate)

            next_population.append(child1)
            next_population.append(child2)

        population = next_population

    best_individual = None
    best_fitness = -1

    for individual in population:
        fitness = evaluate_fitness(individual, weights, prices, capacity)
        if fitness > best_fitness:
            best_fitness = fitness
            best_individual = individual

    return best_individual, best_fitness

picks = []
best_prices = []

def fix_list_string(s):
    s = re.sub(r'\[\s*', '[', s)
    s = re.sub(r'\s*\]', ']', s)
    s = re.sub(r'\s+', ',', s)
    return s

for _, row in df.iterrows():
    weights = ast.literal_eval(fix_list_string(row['Weights']))
    prices = ast.literal_eval(fix_list_string(row['Prices']))
    capacity = row['Capacity']

    best_individual, best_value = genetic_algorithm(
        weights, prices, capacity,
        population_size, num_generations,
        mutation_rate, mutation_strategy
    )
    
    picks.append(best_individual)
    best_prices.append(best_value)

df['algorithmPicks'] = picks
df['algorithmPrice'] = best_prices

pd.concat([df.head(5), df.tail(5)])
Out[8]:
<style scoped=""> .dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; } </style>
Weights Prices Capacity Best picks Best price algorithmPicks algorithmPrice
0 [46 40 42 38 10] [12 19 19 15 8] 40 [0. 1. 0. 0. 0.] 19.0 [0, 1, 0, 0, 0] 19
1 [11 31 4 6 7] [ 2 8 18 16 3] 64 [1. 1. 1. 1. 1.] 47.0 [1, 1, 1, 1, 1] 47
2 [32 49 27 37 24] [19 16 16 4 1] 87 [1. 0. 1. 0. 1.] 36.0 [1, 0, 1, 0, 1] 36
3 [20 35 22 23 16] [19 17 19 9 1] 21 [1. 0. 0. 0. 0.] 19.0 [1, 0, 0, 0, 0] 19
4 [ 7 12 19 13 20] [10 11 18 15 5] 50 [0. 1. 1. 1. 0.] 44.0 [0, 1, 1, 1, 0] 44
9995 [18 12 11 49 32] [12 3 17 19 7] 41 [1. 1. 1. 0. 0.] 32.0 [1, 1, 1, 0, 0] 32
9996 [20 2 24 7 7] [17 12 4 3 8] 17 [0. 1. 0. 1. 1.] 23.0 [0, 1, 0, 1, 1] 23
9997 [43 43 5 15 23] [15 5 7 2 7] 62 [1. 0. 1. 0. 0.] 22.0 [1, 0, 1, 0, 0] 22
9998 [49 9 15 21 39] [11 15 3 12 19] 65 [0. 1. 1. 0. 1.] 37.0 [0, 1, 1, 0, 1] 37
9999 [25 36 42 19 39] [15 12 7 18 12] 79 [1. 0. 0. 1. 0.] 33.0 [1, 0, 0, 1, 0] 33

Ответы алгоритма совпадают с теми ответами, которые уже имелись в наборе данных