Files
School/Anul 2/Semestrul 2/AI/Lab_9_2024.ipynb
T
2024-08-31 12:07:21 +03:00

199 lines
8.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Lab. 12\n",
"\n",
"### Solve the following problem using Genetic Algorithms:\n",
"\n",
"\n",
"Problem: Weighted N-Queen Problem\n",
"\n",
"\n",
"You are given an N×N chessboard, and each cell of the board has an associated weight. Your task is to find a valid placement of N queens such that the total weight of the queens is maximized, and no two queens threaten each other.\n",
"\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"\n",
"In the traditional N-Queen Problem, the goal is to place N queens on an N×N chessboard in such a way that no two queens threaten each other. In this variation, we introduce weights to the queens and aim to find a placement that maximizes the total weight of the queens while satisfying the constraint of non-threatening positions.\n",
"\n",
"\n",
"Constraints:\n",
"\n",
"1. There should be exactly one queen in each row and each column.\n",
"2. No two queens should be placed in the same diagonal, i.e., they should not threaten each other.\n",
"3. The placement should maximize the total weight of the queens.\n",
"\n",
"\n",
"Representation:\n",
"\n",
"Use a permutation-based representation. Each permutation represents the column position of the queen for each row. \n",
"\n",
"For example, if N=4, a valid permutation [2, 4, 1, 3] indicates that the queen in the first row is placed in column 2, the queen in the second row is placed in column 4, and so on.\n",
"\n",
"\n",
"Genetic Algorithm Steps:\n",
"\n",
"1. *Initialization*: Generate an initial population of permutations randomly.\n",
"\n",
"2. *Fitness Evaluation*: Evaluate the fitness of each permutation by calculating the total weight of the queens while considering the non-threatening positions.\n",
"\n",
"3. *Selection*: Select a subset of permutations from the population based on their fitness, using selection techniques like tournament selection or roulette wheel selection.\n",
"\n",
"4. *Crossover*: Perform crossover (recombination) on the selected permutations to create new offspring permutations.\n",
"\n",
"5. *Mutation*: Introduce random changes (mutations) in the offspring permutations to maintain diversity in the population.\n",
"\n",
"6. *Fitness Evaluation for the new individuals*: Evaluate the fitness of the new population.\n",
"\n",
"7. *Form the new population*: Select the surviving individuals based on scores, with chances direct proportional with their performance.\n",
"\n",
"8. Repeat steps 3-7 for a certain number of generations or until a termination condition is met (e.g., a maximum number of iterations or a satisfactory solution is found).\n",
"\n",
"\n",
"9. *Termination*: Return the best-performing individual (permutation) found as the solution to the problem.\n",
"\n",
"Note: The fitness function used in this problem should calculate the total weight of the queens based on the positions specified by the permutation. Additionally, the fitness function should penalize solutions that violate the non-threatening constraint by assigning a lower fitness score to such permutations."
]
},
{
"cell_type": "code",
"execution_count": 223,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[[0 1 2 3]\n",
" [1 0 4 5]\n",
" [2 4 0 6]\n",
" [3 5 6 0]]\n",
"[[3 0 1 2]\n",
" [3 1 2 0]\n",
" [3 1 2 0]\n",
" [0 1 2 3]\n",
" [2 0 1 3]]\n",
"[2 0 3 1]\n",
"14\n"
]
}
],
"source": [
"import numpy as np\n",
"import random\n",
"\n",
"np.random.seed(40)\n",
"\n",
"n = 4\n",
"weights = np.array([[0, 1, 2, 3], [1, 0, 4, 5], [2, 4, 0, 6], [3, 5, 6, 0]])\n",
"\n",
"print(weights)\n",
"\n",
"def initialization(chromosome_size: int, population_size: int) -> np.ndarray:\n",
" return np.array([np.random.permutation(chromosome_size) for _ in range(population_size)])\n",
"\n",
"def fitness(chromosome: np.ndarray, weights: np.ndarray) -> int:\n",
" score = 0\n",
" for i in range(len(chromosome)):\n",
" if not is_attacked(chromosome, [i, chromosome[i]]):\n",
" score += weights[i, chromosome[i]]\n",
" return score\n",
"\n",
"def is_attacked(chromosome: np.ndarray, queen: list) -> bool:\n",
" for i in range(len(chromosome)):\n",
" if i == queen[0]:\n",
" continue\n",
" if abs(i - queen[0]) == abs(chromosome[i] - queen[1]):\n",
" return True\n",
" return False\n",
"\n",
"def selection(population: np.ndarray, weights: np.ndarray) -> np.ndarray:\n",
" return random.choices(population, weights=[fitness(chromosome, weights)+1 for chromosome in population], k=2)\n",
"\n",
"def crossover(parent1: np.ndarray, parent2: np.ndarray) -> np.ndarray:\n",
" point1, point2 = sorted(np.random.choice(len(parent1)+1, 2, replace=False))\n",
" offspring = np.full_like(parent1, -1)\n",
" offspring[point1:point2] = parent1[point1:point2]\n",
"\n",
" idx = 0\n",
" while idx != len(offspring) and offspring[idx] != -1:\n",
" idx += 1\n",
" for gene in parent2:\n",
" if gene not in offspring:\n",
" offspring[idx] = gene\n",
" while idx != len(offspring) and offspring[idx] != -1:\n",
" idx += 1\n",
" return offspring\n",
"\n",
"def mutation(chromosome: np.ndarray, probability: np.float32) -> np.ndarray:\n",
" if random.random() < probability:\n",
" point1, point2 = np.random.choice(len(chromosome), 2, replace=False)\n",
" chromosome[point1], chromosome[point2] = chromosome[point2], chromosome[point1]\n",
" return chromosome\n",
"\n",
"def genetic_algorithm(n: int, weights: np.ndarray, population_size: int, generations: int, mutation_probability: np.float32) -> np.ndarray:\n",
" population = initialization(n, population_size)\n",
" print(population)\n",
" for _ in range(generations):\n",
" parent1, parent2 = selection(population, weights)\n",
" offspring = crossover(parent1, parent2)\n",
" offspring = mutation(offspring, mutation_probability)\n",
" if fitness(offspring, weights) > fitness(parent1, weights):\n",
" population[np.where((population == parent1).all(axis=1))[0][0]] = offspring\n",
" elif fitness(offspring, weights) > fitness(parent2, weights):\n",
" population[np.where((population == parent2).all(axis=1))[0][0]] = offspring\n",
" return max(population, key=lambda x: fitness(x, weights))\n",
" \n",
"\n",
"# print(initialization(n, 5))\n",
"# print(fitness(np.array([2, 0, 3, 1]), weights))\n",
"# print(crossover(np.array([1, 3, 0, 2]), np.array([1, 3, 0, 2])))\n",
"\n",
"champion = genetic_algorithm(n, weights, 5, 100, 0.6)\n",
"print(champion)\n",
"print(fitness(champion, weights))\n",
"\n",
"\n"
]
}
],
"metadata": {
"kernel_info": {
"name": "python"
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.2"
},
"nteract": {
"version": "nteract-front-end@1.0.0"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}