Genetic fertig

This commit is contained in:
2025-06-19 16:38:55 +02:00
parent f94c6d9fe6
commit 608faa4c93

168
P2.py
View File

@@ -1,52 +1,170 @@
import random
class Field:
def __init__(self, init_state=None):
self.state = []
if init_state is None:
for i in range(8):
self.state.append(random.randint(1,8)) # row number (0:8] => [1:8]
self.state.append(random.randint(1, 8)) # row number [1:8]
else:
self.state = init_state.copy()
def print_field(self):
print(" ┌───┬───┬───┬───┬───┬───┬───┬───┐")
for row in range(8,0,-1): # (0:8]
row_string = ""
for line in range(8):
if row is self.state[line]: # is there a Queen in this line (spalte) in this row
row_string += "Q │ "
else:
row_string += ""
print(f"{row} | {row_string}")
if row > 1 : print(" ├───┼───┼───┼───┼───┼───┼───┼───┤")
self.threats = self.collisions(self.state)
self.fitness = 28 - self.threats
print(" └───┴───┴───┴───┴───┴───┴───┴───┘")
print(" A B C D E F G H ")
print(self.state)
def get_fitness(self):
return self.fitness
def get_state(self):
return self.state
def collisions(self, current_state):
# wagerechte haben die gleiche zahl stehe
# diagonale haben einen wert der um den abstand gemindert ist => gleichseitiges rechtwinkliges Dreieck
# Actions
def set_state(self, column, row=None):
if row is None:
self.state[column] = random.randint(1, 8)
if 0 < row and row < 9:
self.state[column] = row
def move_queen(self, column, new_row=None):
self.set_state(column, new_row)
# Update
self.threats = self.collisions()
self.fitness = 28 - self.threats
def move_all_queens(self, new_state=None):
if new_state is None:
for i in range(8):
self.move_queen(i)
else:
for i, new_row in enumerate(new_state):
self.move_queen(i, new_row)
# heuristics functions
def collisions(self, current_state=None):
# wagerechte haben die gleiche row zahl stehe
# diagonale haben einen wert der um den spalten-abstand gemindert ist => gleichseitiges rechtwinkliges Dreieck
# Beachte die Spalten/ Linien Nr ist um eins verringert [0, 1, ...,7]
if current_state is None:
current_state = self.get_state()
collisions = 0
for i, row_i in enumerate(current_state):
for j, row_j in enumerate(current_state):
if j is not i:
# horizontal diagonal in both directions and counting twice
if row_i == row_j or row_j == (row_i + (j-i)) or row_j == (row_i - (j-i)):
# horizontal diagonal in both sides up and down and counting "twice"
if row_i == row_j or row_j == (row_i + abs(j - i)) or row_j == (row_i - abs(j - i)):
collisions += 1
return collisions
# print(f"{i+1}-{row_i} <=> {j+1}-{row_j}") # Debugging
return collisions / 2
def print_field(self):
print("\n ┌───┬───┬───┬───┬───┬───┬───┬───┐")
for row in range(8, 0, -1): # (0:8]
row_string = ""
for line in range(8):
if row is self.state[line]: # is there a Queen in this line (spalte) in this row
if (row + line) % 2 == 0:
row_string += "▌Q▐│"
else:
row_string += " Q │"
elif (row + line) % 2 == 0:
row_string += "███│"
else:
row_string += ""
print(f"{row} |{row_string}")
if row > 1: print(" ├───┼───┼───┼───┼───┼───┼───┼───┤")
print(" └───┴───┴───┴───┴───┴───┴───┴───┘")
print(" A B C D E F G H \n")
class Genetic:
def __init__(self):
self.initial_population = []
self.p_mutation = 0.1
for i in range(100):
self.initial_population.append(Field())
def random_selection(self, population):
"""
input:
population: a set of individuals
Fitness-FN: # of non-attacking queens (max 28)
returns:
Basierend auf der Verteilung der heuristischen Werte (Fitness) soll zufällig ein Eintrag (Field) gewählt werden, d.h. je höher der heuritische Wert (Fitness) ist, umso höher soll die Wahrscheinlichkeit sein, dass ein Field ausgewählt wird
"""
fitness = []
for field in population:
fitness.append(field.get_fitness())
chosen = random.choices(population, weights=fitness, k=1)[0]
return chosen
def mutation(self, field):
"""
input:
state: a single individuals
returns:
randomly mutated version of it
"""
field.move_queen(random.randint(0, 7), random.randint(1, 8))
def reproduce(self, x, y):
child = []
n = len(x.get_state())
c = random.randint(1, n)
child.extend(x.get_state()[:c]) # Slice operator Syntax [a:b[
child.extend(y.get_state()[c:])
return Field(child)
def genetic_algorithm(self, n):
"""
population: a set of individuals
Fitness-FN: # of non-attacking queens (max 28)
"""
current_population = self.initial_population
new_population = []
best_field = self.initial_population[0]
for i in range(n):
for j in range(len(self.initial_population)):
x = self.random_selection(current_population)
y = self.random_selection(current_population)
child = self.reproduce(x, y)
if random.random() < self.p_mutation:
self.mutation(child)
new_population.append(child)
if child.get_fitness() > best_field.get_fitness():
best_field = child
current_population = new_population
new_population = []
if best_field.get_fitness() == 28:
break
return best_field
def main():
new_field = Field()
new_field = Field(
init_state=[6, 3, 5, 7, 1, 4, 2, 8]) # [8, 4, 5, 4, 4, 3, 7, 6] [5, 5, 5, 5, 1, 2, 8, 5] [6,3,5,7,1,4,2,8]
new_field.print_field()
print(new_field.collisions(new_field.get_state()))
print(new_field.collisions())
main()
genetic = Genetic()
best_genetic_field = genetic.genetic_algorithm(500)
best_genetic_field.print_field()
print(best_genetic_field.get_fitness())
main()