import numpy as np
import pandas as pd

# Заданные параметры для функции
a, b, c, d = 14, 2, -26, 1

# Целевая функция
def target_function(x):
    return a + b * x + c * x**2 + d * x**3

# Диапазон x
x_min, x_max = -10, 53

# Длина битовой строки 
chromosome_length = 6
population_size = 6  #количество особей

#Функция для генерации случайных значений X в заданном диапазоне
def generate_random_x_values(size, x_min=-10, x_max=53):
    return np.random.randint(x_min, x_max + 1, size=size)

# Функция для генерации случайных чисел для отбора
def generate_random_selection_numbers(size):
    return np.random.randint(0, 101, size=size)

# Функция для генерации сбалансированных групп
def generate_balanced_groups(size):
    groups = []
    for i in range(1, (size // 2) + 1):
        groups.extend([i, i])
    np.random.shuffle(groups)
    return groups

# Функция скрещивания
def crossover(parent1, parent2):
    # Преобразование в бинарный формат
    parent1_bin = f"{parent1:06b}"
    parent2_bin = f"{parent2:06b}"
    
    # Скрещивание по описанному правилу
    offspring1_bin = parent1_bin[:3] + parent2_bin[3:]
    offspring2_bin = parent2_bin[:3] + parent1_bin[3:]
    
    # Преобразуем обратно в десятичное представление
    offspring1 = int(offspring1_bin, 2)
    offspring2 = int(offspring2_bin, 2)
    
    # Вывод информации о процессе скрещивания
    print(f"\nРодитель 1 (десятичный): {parent1}, (бинарный): {parent1_bin}")
    print(f"Родитель 2 (десятичный): {parent2}, (бинарный): {parent2_bin}")
    print(f"Потомок 1 (десятичный): {offspring1}, (бинарный): {offspring1_bin}")
    print(f"Потомок 2 (десятичный): {offspring2}, (бинарный): {offspring2_bin}")
    
    return offspring1, offspring2

# Функция мутации
def mutate(value, mutation_rate=0.1):
    if np.random.random() < mutation_rate:
        bin_str = f"{value:06b}"
        pos = np.random.randint(0, len(bin_str))
        mutated_str = bin_str[:pos] + ('1' if bin_str[pos] == '0' else '0') + bin_str[pos+1:]
        return int(mutated_str, 2)
    return value

# Функция для одного поколения
def run_generation(population, generation_number, predetermined_values=None):
    print(f"\nПоколение {generation_number}")
    
    if predetermined_values and generation_number == 1:
        x_values = predetermined_values['x_values']
        random_numbers = predetermined_values['random_numbers']
        group_ids = predetermined_values['group_ids']
    else:
        x_values = population
        random_numbers = generate_random_selection_numbers(population_size)
        group_ids = generate_balanced_groups(population_size)
    
    # Создаем DataFrame для текущего поколения
    table = pd.DataFrame({
        'Особь': x_values,
    })
    
    # Вычисление целевой функции для каждого x
    table['f(x)'] = table['Особь'].apply(target_function)
    
    # Вычисляем сумму значений функции для нормализации
    total_fx = table['f(x)'].sum()
    table['P_i'] = table['f(x)'] / total_fx
    table['S_i'] = table['P_i'] * 100
    
    # Вычисляем границы для отбора
    boundaries = [0]
    for si in table['S_i']:
        boundaries.append(boundaries[-1] + si)
    table['Границы'] = [(boundaries[i], boundaries[i + 1]) for i in range(len(boundaries) - 1)]
    
    table['Случайные числа'] = random_numbers
    
    # Определяем, какая особь соответствует каждой границе
    def get_selected_individual(rnd_number):
        for idx, (low, high) in enumerate(table['Границы']):
            if low <= rnd_number < high:
                return idx
        return None
    
    table['Пул'] = table['Случайные числа'].apply(get_selected_individual)
    
    # Добавляем группы
    table['Группы'] = group_ids
    
    # Создаем пары для скрещивания
    pairing_indices = []
    for group, members in table.groupby('Группы'):
        selected_inds = members['Пул'].tolist()
        for i in range(0, len(selected_inds) - 1, 2):
            pairing_indices.append((selected_inds[i], selected_inds[i + 1]))
    
    # Создаем потомков
    offspring_list = []
    for parent1_idx, parent2_idx in pairing_indices:
        parent1 = table.at[parent1_idx, 'Особь']
        parent2 = table.at[parent2_idx, 'Особь']
        off1, off2 = crossover(parent1, parent2)
        off1 = mutate(off1)
        off2 = mutate(off2)
        offspring_list.extend([off1, off2])
    
    table['Потомки'] = pd.Series(offspring_list)
    
    print("\nСтатистика поколения:")
    print(f"Максимальное значение f(x): {table['f(x)'].max()}")
    print(f"Минимальное значение f(x): {table['f(x)'].min()}")
    print(f"Среднее значение f(x): {table['f(x)'].mean():.2f}")
    print("\nТаблица поколения:")
    print(table)
    
    return offspring_list, table['f(x)'].max(), table['f(x)'].min()

# Основной цикл алгоритма
def run_genetic_algorithm(num_generations=5, use_predetermined=False):
    # Предопределенные значения
    predetermined_values = None
    if use_predetermined:
        predetermined_values = {
            'x_values': [36, 35, 29, 40, 30, 51],  # Предопределенные значения X
            'random_numbers': [91, 21, 12, 7, 24, 15],  # Предопределенные случайные числа
            'group_ids': [1, 3, 2, 1, 2, 3]  # Предопределенные группы
        }
        population = predetermined_values['x_values']
    else:
        population = generate_random_x_values(population_size)
    
    # Сохраняем статистику по поколениям
    generation_stats = []
    
    for gen in range(1, num_generations + 1):
        new_population, max_fx, min_fx = run_generation(
            population, 
            gen, 
            predetermined_values if gen == 1 and use_predetermined else None
        )
        generation_stats.append({
            'Поколение': gen,
            'Макс. f(x)': max_fx,
            'Мин. f(x)': min_fx
        })
        population = new_population
    
    # Выводим итоговую статистику
    print("\nИтоговая статистика по всем поколениям:")
    stats_df = pd.DataFrame(generation_stats)
    print(stats_df)
    
    # Находим глобальные максимум и минимум
    global_max = stats_df['Макс. f(x)'].max()
    global_min = stats_df['Мин. f(x)'].min()
    print(f"\nГлобальный максимум f(x): {global_max}")
    print(f"Глобальный минимум f(x): {global_min}")

# Добавляем аналитическое решение для сравнения
def find_analytical_solution():
    # Находим критические точки, решая уравнение f'(x) = 0
    # f'(x) = b + 2cx + 3dx^2
    coeffs = [3*d, 2*c, b]
    roots = np.roots(coeffs)
    
    # Проверяем граничные точки и критические точки
    test_points = np.concatenate([roots, [-10, 53]])
    test_points = test_points[np.isreal(test_points)]  # Оставляем только реальные корни
    test_points = test_points[(test_points >= -10) & (test_points <= 53)]  # Фильтруем по интервалу
    
    # Вычисляем значения функции в этих точках
    values = [target_function(x) for x in test_points]
    
    analytical_min = min(values)
    analytical_max = max(values)
    
    print("\nАналитическое решение:")
    print(f"Максимум f(x): {analytical_max}")
    print(f"Минимум f(x): {analytical_min}")


USE_PREDETERMINED_VALUES = True  #false для случайнойгенерации


np.random.seed(42) 
run_genetic_algorithm(5, USE_PREDETERMINED_VALUES)
find_analytical_solution()



напиши обьяснения для этого кода чтобы ямог обьяснить