|
| 1 | +import numpy as np |
| 2 | + |
| 3 | +class GeneticAlgorithm: |
| 4 | + def __init__(self, func, bounds, pop_size=20, generations=100, mutation_rate=0.1, crossover_rate=0.8, selection_method='tournament'): |
| 5 | + self.func = func |
| 6 | + self.bounds = bounds |
| 7 | + self.pop_size = pop_size |
| 8 | + self.generations = generations |
| 9 | + self.mutation_rate = mutation_rate |
| 10 | + self.crossover_rate = crossover_rate |
| 11 | + self.selection_method = selection_method |
| 12 | + |
| 13 | + def initialize_population(self): |
| 14 | + # Create random population within the bounds |
| 15 | + dim = len(self.bounds) |
| 16 | + return np.array([np.random.uniform(self.bounds[d][0], self.bounds[d][1], self.pop_size) for d in range(dim)]).T |
| 17 | + |
| 18 | + def fitness(self, individual): |
| 19 | + # Calculate fitness (for minimization, return function value) |
| 20 | + return self.func(*individual) |
| 21 | + |
| 22 | + def selection(self, population, fitness_values): |
| 23 | + if self.selection_method == 'tournament': |
| 24 | + return self.tournament_selection(population, fitness_values) |
| 25 | + elif self.selection_method == 'roulette': |
| 26 | + return self.roulette_wheel_selection(population, fitness_values) |
| 27 | + |
| 28 | + def tournament_selection(self, population, fitness_values): |
| 29 | + indices = np.random.choice(len(population), size=2, replace=False) |
| 30 | + return population[indices[0]] if fitness_values[indices[0]] < fitness_values[indices[1]] else population[indices[1]] |
| 31 | + |
| 32 | + def roulette_wheel_selection(self, population, fitness_values): |
| 33 | + fitness_sum = np.sum(1.0 / (1 + fitness_values)) # inverse for minimization |
| 34 | + pick = np.random.uniform(0, fitness_sum) |
| 35 | + current = 0 |
| 36 | + for individual, fitness in zip(population, fitness_values): |
| 37 | + current += 1.0 / (1 + fitness) |
| 38 | + if current > pick: |
| 39 | + return individual |
| 40 | + |
| 41 | + def crossover(self, parent1, parent2): |
| 42 | + if np.random.rand() < self.crossover_rate: |
| 43 | + # Uniform crossover |
| 44 | + mask = np.random.rand(len(parent1)) < 0.5 |
| 45 | + child = np.where(mask, parent1, parent2) |
| 46 | + return child |
| 47 | + return parent1 |
| 48 | + |
| 49 | + def mutate(self, individual): |
| 50 | + for i in range(len(individual)): |
| 51 | + if np.random.rand() < self.mutation_rate: |
| 52 | + individual[i] += np.random.uniform(-1, 1) |
| 53 | + individual[i] = np.clip(individual[i], self.bounds[i][0], self.bounds[i][1]) |
| 54 | + return individual |
| 55 | + |
| 56 | + def run(self): |
| 57 | + population = self.initialize_population() |
| 58 | + best_individual = None |
| 59 | + best_fitness = float('inf') |
| 60 | + |
| 61 | + for generation in range(self.generations): |
| 62 | + fitness_values = np.array([self.fitness(ind) for ind in population]) |
| 63 | + |
| 64 | + # Track the best solution |
| 65 | + current_best_index = np.argmin(fitness_values) |
| 66 | + if fitness_values[current_best_index] < best_fitness: |
| 67 | + best_fitness = fitness_values[current_best_index] |
| 68 | + best_individual = population[current_best_index] |
| 69 | + |
| 70 | + # Create new generation |
| 71 | + new_population = [] |
| 72 | + for _ in range(self.pop_size): |
| 73 | + parent1 = self.selection(population, fitness_values) |
| 74 | + parent2 = self.selection(population, fitness_values) |
| 75 | + child = self.crossover(parent1, parent2) |
| 76 | + child = self.mutate(child) |
| 77 | + new_population.append(child) |
| 78 | + |
| 79 | + population = np.array(new_population) |
| 80 | + |
| 81 | + # Termination Condition: Minimal improvement |
| 82 | + if generation > 1 and abs(best_fitness - self.fitness(best_individual)) < 1e-6: |
| 83 | + break |
| 84 | + |
| 85 | + return best_individual, best_fitness |
| 86 | + |
| 87 | +# Example Usage: |
| 88 | +def my_function(x, y): |
| 89 | + return x**2 + y**2 |
| 90 | + |
| 91 | +bounds = [(-10, 10), (-10, 10)] |
| 92 | +ga = GeneticAlgorithm(func=my_function, bounds=bounds, pop_size=30, generations=100) |
| 93 | +best_solution, best_value = ga.run() |
| 94 | +print(f"Optimized Solution: {best_solution}, Function Value: {best_value}") |
0 commit comments