import random
import numpy as np

def objective_function(x):
    return (x[0] - 1.0)**2 + (x[1] + 2)**2

def create_individual(bounds):
    return np.array([random.uniform(low, high) for low, high in bounds])

def mutate(individual, bounds, mutation_rate = 0.01):
    for i in range(len(individual)):
        if random.random() < mutation_rate:
            individual[i] = random.uniform(bounds[i][0], bounds[i][1])

def crossover(parent1, parent2):
    crossover_point = random.randint(1, len(parent1) - 1)
    child1 = np.concatenate((parent1[:crossover_point], parent2[crossover_point:]))
    child2 = np.concatenate((parent2[:crossover_point], parent1[crossover_point:]))
    return child1, child2

def select_best_by_random_group(population, scores, k = 3):
    selected = random.choices(population, k = k)

# selectedから、scoresに対応するindexを検索してscoresに入れる
#    selected_scores = [scores[i] for i, ind in enumerate(population) if any((ind == s).all() for s in selected)]
    selected_scores = []
    selected_params = []
    for i, ind in enumerate(population):
        for s in selected:
            if (ind == s).all():
                selected_scores.append(scores[i])
                selected_params.append(s)
                break
#    if not selected_scores:  # selected_scoresが空の場合の対処
#        return random.choice(population)

    idx = np.argmin(selected_scores)
    return selected_params[idx]

def genetic_algorithm(objective_function, bounds, population_size, generations, mutation_rate=0.01):
    population = [create_individual(bounds) for _ in range(population_size)]
    best_individual = population[0]
    best_score = objective_function(best_individual)

    for generation in range(generations):
        scores = [objective_function(ind) for ind in population]
        new_population = []
        for i in range(population_size // 2):
            parent1 = select_best_by_random_group(population, scores)
            parent2 = select_best_by_random_group(population, scores)
            child1, child2 = crossover(parent1, parent2)
            mutate(child1, bounds, mutation_rate)
            mutate(child2, bounds, mutation_rate)
            new_population.extend([child1, child2])

        population = new_population
        current_best = min(population, key=objective_function)
        current_best_score = objective_function(current_best)
        if current_best_score < best_score:
            best_individual, best_score = current_best, current_best_score

    return best_individual, best_score

# 例として、2次関数の最小値を求める
bounds = [(-10, 10), (-10, 10)]
population_size = 50
generations = 100

best_individual, best_score = genetic_algorithm(objective_function, bounds, population_size, generations)
print(f"Best individual: {best_individual}")
print(f"Best score: {best_score}")
