Skip to content

Commit 6ef905b

Browse files
Genetic Algorithm for Function Optimization TheAlgorithms#11578 updated
1 parent 04c83ef commit 6ef905b

File tree

1 file changed

+94
-0
lines changed

1 file changed

+94
-0
lines changed

genetic_algorithm/ga_optimisation.py

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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

Comments
 (0)