├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── img ├── 01_Battleship_Board_Solution.png ├── 02_Battleship_Random_Guess_51.png ├── 03_Random_Accuracy_53.png ├── 04_Generation_001.png ├── 05_Generation_002.png ├── 06_Generation_155.png ├── 07_Stats_10.png ├── 08_Stats_155.png ├── 09_Convergence.png └── 10_Performance.png ├── requirements.txt └── src ├── battleship.py ├── genetic_algorithm.py └── genetic_algorithm_battleship.ipynb /.gitattributes: -------------------------------------------------------------------------------- 1 | src/*.ipynb linguist-vendored 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # Logs and databases 107 | *.log 108 | *.sql 109 | *.sqlite 110 | 111 | # OS generated files 112 | .DS_Store 113 | .DS_Store? 114 | ._* 115 | .Spotlight-V100 116 | .Trashes 117 | ehthumbs.db 118 | Thumbs.db 119 | 120 | # config file 121 | config.py 122 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Adam C Dick 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DATA STRUCTURES AND ALGORITHMS 2 | # Understanding Genetic Algorithms 3 | SOLVING A BATTLESHIP BOARD GAME AS AN OPTIMIZATION PROBLEM 4 | 5 | A genetic algorithm is a prime example of technology imitating nature to solve complex problems, in this case, by adopting the concept of natural selection in an evolutionary algorithm. Genetic algorithms, introduced in 1960 by John Holland, extend Alan Turing’s concept of a “learning machine” and are best-suited for solving optimization problems such as the traveling salesman. 6 | 7 | To intuitively understand the practical implementation and fundamental requirements for employing genetic algorithms, we can set up a toy problem and solve the board of the classic guessing game, Battleship, first released by Milton Bradley in 1967. But rather than calling a sequence of individual shots, let’s ask our genetic algorithm to make a series of guesses of the entire board. 8 | 9 | [Continue reading the full story curated by Towards Data Science, a Medium publication...](https://towardsdatascience.com/understanding-genetic-algorithms-cd556e9089cb?source=friends_link&sk=70e5b098ef167ff2d1132396ab441030) 10 | 11 | ## Repository Contents 12 | 13 | * [Project Features](#project-features) 14 | * [Source Code](#source-code) 15 | * [Output Results](#output-results) 16 | * [Contribute](#contribute) 17 | 18 | ## Project Features 19 | ARTIFICIAL INTELLIGENCE | MACHINE LEARNING | OPTIMIZATION | EVOLUTIONARY ALGORITHMS | TOY PROBLEMS 20 | 21 |

22 | 23 |

24 | 25 | - [x] **Random Board Generator for Battleship Game**
26 | Graphical and binary representation of a Battleship board game with 5 ships occupying 17 squares 27 | - [x] **Implementation of Genetic Algorithm**
28 | Iterative solver that generates candidate solutions of an entire board of a random Battleship game, which converge to the genetic solution 29 | - [x] **Genetic Selector: Elitism**
30 | The fittest chromosomes from the former generation that are selected to be parents of all of the chromosomes in a new generation. 31 | - [x] **Genetic Operator: Crossover**
32 | Two chromosomes can be spliced at a random crossover gene and recombined to create two children, sharing gene chains from both parents. 33 | - [x] **Genetic Operator: Mutation**
34 | Random genes of one chromosome can be inverted with bit flips to create a single child that exhibits a small genetic variation from its parent. 35 | 36 | ## Source Code 37 | PYTHON | NUMPY | PANDAS 38 | 39 | **[The Random Board Generator](https://gist.github.com/acdick/11d2bc2d3c046306a143fd5a0b24b6a9)** 40 | 1. Position the head of the ship with a random two-dimensional tuple 41 | 2. Orient the heading of the ship with a random cardinal direction 42 | 3. Locate the tail of the ship based on its head position, direction and length 43 | 4. Check that the tail of the ship remains within the boundaries of the board 44 | 5. Check that the ship does not overlap with any previously positioned ships 45 | 6. Repeat the process if the ship fails either of the two assertion tests 46 | 47 | **[The Battleship Module](/src/battleship.py)** 48 | 49 | * Random board generator for a new Battleship game 50 | * Genetic representation of the board solution as binary representation 51 | * Fitness function evaluator of a candidate solution based on accuracy 52 | 53 | **[The Genetic Algorithm Module](/src/genetic_algorithm.py)** 54 | * Random solution generator for first generation 55 | * Assignment of elite chromosomes within a generation 56 | * Selection of elite parents to populate a new generation with elite rate 57 | * Creation of mutant descendents with bit flip rate 58 | * Creation of splice pair descendents with number of splice pairs 59 | * Filling of generation vacancies with random descendents 60 | * Creation of descendent generations with specified population mix and termination criterion 61 | * Solution driver to create generations until convergence to the genetic solution 62 | 63 | ## Output Results 64 | SEABORN | MATPLOTLIB 65 | 66 | **[Genetic Algorithm Demo](/src/genetic_algorithm_battleship.ipynb)** 67 | 1. Set up the board with random board generator 68 | 2. Generate candidate solution for the entire board 69 | 3. Determine fitness of candidate solution based on accuracy 70 | 4. Create descendent generations until convergence to the genetic solution 71 | 5. Describe fitness statistics of generations to inspect algorithm behavior 72 | 6. Solve 1,000 random board games to assess performance of model parameters of genetic algorithm 73 | 74 | **Random Chromosome** 75 | * 51 Occupied Squares (Solution: 1, Candidate: Red) 76 | * 49 Unoccupied Squares (Solution: 0, Candidate: Blue) 77 | 78 |

79 | 80 |

81 | 82 | **Accuracy Rate of Random Chromosome** 83 | * 52% candidate accuracy 84 | * 52 Matches (Green) 85 | * 48 Mismatches (Red) 86 | 87 |

88 | 89 |

90 | 91 | **Fitness of Generation 1**
92 | * 10 Random chromosomes 93 | * 2 top performers are 57% accurate 94 | * Median fitness of generation is 52.5% 95 | 96 |

97 | 98 |

99 | 100 | **Fitness of Generation 2** 101 | * 10 total chromosomes 102 | * 2 Elitism chromosomes (20% elite rate) 103 | * 2 Splice Pair chromosomes (20% crossover rate) 104 | * 6 Mutation chromosomes (60% mutation rate and 1% bit flip rate) 105 | * 2 top performers improve fitness to be 60% and 58% accurate 106 | 107 |

108 | 109 |

110 | 111 | **Fitness of Generation 155** 112 | * 155 generations with 10 chromosomes per generation 113 | * 1 Mutation chromosome converges to genetic solution 114 | 115 |

116 | 117 |

118 | 119 | **Fitness Distribution Statistics for Generations 1 to 10** 120 | * Natural selection gradually improves population fitness 121 | * Dispersion is highest in the first random generation 122 | 123 |

124 | 125 |

126 | 127 | **Fitness Distribution Statistics for Generations 146 to 155** 128 | * All generations are extremely fit with least fit chromosome having 95% accuracy 129 | * Improved fitness is most likely to occur through mutation of a single gene 130 | * High mutation rates and low bit flip rates give latter generations better chance to converge 131 | 132 |

133 | 134 |

135 | 136 | **Convergence of the Gene Pool Towards the Optimal Solution Over 155 Generations** 137 | * First random generation will be 50% accurate 138 | * Elitism guarantees that generational peak performance is monotonically increasing 139 | * Crossover promotes substantial improvements to fitness in initial generations 140 | * Mutation achieves convergence with genetic solution in latter generations 141 | 142 |

143 | 144 |

145 | 146 | **Performance Metric of a Genetic Algorithm with Fixed Model Parameters Over 1,000 Samples** 147 | * Genetic algoritm performance is influenced by nature of Battleship problem and the model parameters 148 | * Performance can be measured by sampling and solving 1,000 random Battleship board games 149 | * Model parameters can be tuned by using the performance distribution as an objective function 150 | 151 |

152 | 153 |

154 | 155 | ## Contribute 156 | 157 | **Contact** 158 | * [Email](mailto:adam.c.dick@gmail.com) 159 | * [LinkedIn](https://www.linkedin.com/in/adamcdick/) 160 | * [Medium](https://medium.com/@adam.c.dick) 161 | * [Scholar](https://scholar.google.com/citations?user=eMO88ogAAAAJ&hl=en) 162 | 163 | **License** 164 | * [MIT License](https://github.com/acdick/understanding_genetic_algorithms/blob/master/LICENSE) 165 | -------------------------------------------------------------------------------- /img/01_Battleship_Board_Solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acdick/understanding_genetic_algorithms/2609446dd7790287baf4e949d6c9407bca2391a3/img/01_Battleship_Board_Solution.png -------------------------------------------------------------------------------- /img/02_Battleship_Random_Guess_51.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acdick/understanding_genetic_algorithms/2609446dd7790287baf4e949d6c9407bca2391a3/img/02_Battleship_Random_Guess_51.png -------------------------------------------------------------------------------- /img/03_Random_Accuracy_53.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acdick/understanding_genetic_algorithms/2609446dd7790287baf4e949d6c9407bca2391a3/img/03_Random_Accuracy_53.png -------------------------------------------------------------------------------- /img/04_Generation_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acdick/understanding_genetic_algorithms/2609446dd7790287baf4e949d6c9407bca2391a3/img/04_Generation_001.png -------------------------------------------------------------------------------- /img/05_Generation_002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acdick/understanding_genetic_algorithms/2609446dd7790287baf4e949d6c9407bca2391a3/img/05_Generation_002.png -------------------------------------------------------------------------------- /img/06_Generation_155.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acdick/understanding_genetic_algorithms/2609446dd7790287baf4e949d6c9407bca2391a3/img/06_Generation_155.png -------------------------------------------------------------------------------- /img/07_Stats_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acdick/understanding_genetic_algorithms/2609446dd7790287baf4e949d6c9407bca2391a3/img/07_Stats_10.png -------------------------------------------------------------------------------- /img/08_Stats_155.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acdick/understanding_genetic_algorithms/2609446dd7790287baf4e949d6c9407bca2391a3/img/08_Stats_155.png -------------------------------------------------------------------------------- /img/09_Convergence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acdick/understanding_genetic_algorithms/2609446dd7790287baf4e949d6c9407bca2391a3/img/09_Convergence.png -------------------------------------------------------------------------------- /img/10_Performance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acdick/understanding_genetic_algorithms/2609446dd7790287baf4e949d6c9407bca2391a3/img/10_Performance.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | matplotlib==3.0.2 2 | numpy==1.15.4 3 | pandas==0.23.4 4 | seaborn==0.9.0 -------------------------------------------------------------------------------- /src/battleship.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | # generate battleship board and solution 4 | def new_board(): 5 | 6 | # create a clear board 7 | dim = 10 8 | board = np.zeros((dim, dim), dtype=int) 9 | 10 | # randomly place ships on the board 11 | ship_lengths = [5, 4, 3, 3, 2] 12 | 13 | # for each ship 14 | for ship_length in ship_lengths: 15 | is_ship_placed = False 16 | 17 | while not is_ship_placed: 18 | # seed a coordinate for the head of a ship 19 | head = tuple(np.random.randint(dim, size=2)) 20 | 21 | # choose a direction for the ship to be laid out 22 | # N=0, S=1, E=2, W=3 23 | heading = np.random.randint(4) 24 | 25 | # check that the ship does not hang off the edge of the board 26 | if heading == 0: 27 | tail = (head[0] - ship_length + 1, head[1]) 28 | elif heading == 1: 29 | tail = (head[0] + ship_length - 1, head[1]) 30 | elif heading == 2: 31 | tail = (head[0], head[1] + ship_length - 1) 32 | elif heading == 3: 33 | tail = (head[0], head[1] - ship_length + 1) 34 | 35 | if not ((0 <= tail[0] <= dim-1) and (0 <= tail[1] <= dim-1)): 36 | continue 37 | 38 | # check that the ship does not overlap with any others 39 | NS_min = min(head[0],tail[0]) 40 | NS_max = max(head[0],tail[0]) 41 | EW_min = min(head[1],tail[1]) 42 | EW_max = max(head[1],tail[1]) 43 | 44 | if sum(sum(board[NS_min:NS_max+1,EW_min:EW_max+1])) != 0: 45 | continue 46 | 47 | # place the ship 48 | board[NS_min:NS_max+1,EW_min:EW_max+1] = 1 49 | is_ship_placed = True 50 | 51 | #check number of pieces on the board 52 | if sum(ship_lengths) == sum(sum(board)): 53 | print('Correct number of pieces on board') 54 | else: 55 | print('Incorrect number of pieces on board') 56 | 57 | # represent board solution in genetic form 58 | genetic_solution = ''.join(str(x) for x in list(board.flatten())) 59 | 60 | return board, genetic_solution 61 | 62 | # fitness function 63 | def accuracy(solution, candidate): 64 | n_gene_matches = 0 65 | 66 | for i in range(len(solution)): 67 | if solution[i] == candidate[i]: 68 | n_gene_matches += 1 69 | 70 | return n_gene_matches / len(solution) -------------------------------------------------------------------------------- /src/genetic_algorithm.py: -------------------------------------------------------------------------------- 1 | import battleship as ship 2 | import pandas as pd 3 | import numpy as np 4 | 5 | def random_generation(generation_size, genes): 6 | 7 | # create dataframe for gene pool 8 | generation = pd.DataFrame(columns=['Sequence','Chromosome','Generation','Birth','Fitness','Parents']) 9 | 10 | # for each chromosome 11 | i = 0 12 | while i < generation_size: 13 | 14 | # create random chromosome 15 | chromosome = {} 16 | chromosome['Sequence'] = i+1 17 | chromosome['Chromosome'] = ''.join(str(x) for x in list(np.random.randint(2, size=genes))) 18 | chromosome['Generation'] = 1 19 | chromosome['Birth'] = 'Random' 20 | chromosome['Parents'] = 0 21 | 22 | # check for uniqueness and add to gene pool 23 | if chromosome['Chromosome'] not in generation['Chromosome']: 24 | generation = generation.append(chromosome, ignore_index=True) 25 | i += 1 26 | 27 | # return the generation 28 | return generation 29 | 30 | def assign_elites(generation, elite_rate): 31 | 32 | # determine number of elites 33 | generation_size = generation.shape[0] 34 | elites = elite_rate * generation_size 35 | 36 | # assign elite status to most fit chromosomes 37 | generation['Elite'] = False 38 | generation = generation.sort_values(by='Fitness', ascending=False) 39 | generation.iloc[0:int(elites),6:7] = True 40 | 41 | # return the generation 42 | return generation 43 | 44 | def select_elites(generation): 45 | 46 | # copy elites from old generation 47 | elites = generation.loc[generation['Elite'] == True].copy() 48 | 49 | # update attributes of new generation 50 | pool_size = generation['Sequence'].max() 51 | elites['Parents'] = elites['Sequence'] 52 | elites['Sequence'] = range(pool_size + 1, pool_size + elites.shape[0] + 1) 53 | elites.loc[:,'Birth'] = 'Elitism' 54 | elites['Elite'] = False 55 | elites['Generation'] = generation['Generation'].max() + 1 56 | 57 | return elites 58 | 59 | def create_mutants(generation, mutants, bit_flip_rate): 60 | 61 | # get generation attributes 62 | last_generation = generation['Generation'].max() 63 | last_sequence = generation['Sequence'].max() 64 | n_elites = generation['Birth'].value_counts()['Elitism'] 65 | 66 | # for each mutant 67 | i = 0 68 | while i < mutants: 69 | 70 | # create mutant chromosome 71 | chromosome = {} 72 | chromosome['Sequence'] = last_sequence + i + 1 73 | chromosome['Generation'] = last_generation 74 | chromosome['Birth'] = 'Mutation' 75 | chromosome['Elite'] = False 76 | 77 | # select random elite as new parent 78 | parent_index = np.random.choice(n_elites) 79 | chromosome['Parents'] = list(generation['Sequence'].values)[parent_index] 80 | parent = list(generation['Chromosome'].values)[parent_index] 81 | 82 | # create array of random bit flips 83 | bit_flip_array = np.random.choice(2, len(parent), p=[1 - bit_flip_rate, bit_flip_rate]) 84 | bits_to_flip = ''.join(str(x) for x in list(bit_flip_array.flatten())) 85 | 86 | # create mutant child from parent and flip bits from array 87 | mutant = '' 88 | for j in range(len(bits_to_flip)): 89 | if not int(bits_to_flip[j]): 90 | mutant += parent[j] 91 | else: 92 | mutant += str(abs(int(parent[j]) - 1)) 93 | 94 | # check for uniqueness and add to gene pool 95 | chromosome['Chromosome'] = mutant 96 | if chromosome['Chromosome'] not in generation['Chromosome']: 97 | generation = generation.append(chromosome, ignore_index=True) 98 | i += 1 99 | 100 | # return the generation 101 | return generation 102 | 103 | def create_splices(generation, n_splice_pairs): 104 | 105 | # get generation attributes 106 | last_generation = generation['Generation'].max() 107 | last_sequence = generation['Sequence'].max() 108 | n_elites = generation['Birth'].value_counts()['Elitism'] 109 | 110 | # for each splice pair 111 | i = 0 112 | while i < n_splice_pairs: 113 | 114 | # create splice pair chromosome 115 | chromosome = {} 116 | chromosome['Generation'] = last_generation 117 | chromosome['Birth'] = 'Splice Pair' 118 | chromosome['Elite'] = False 119 | 120 | # select random elite pair as new parents 121 | parent_indices = np.random.choice(n_elites, 2, replace=False) 122 | chromosome['Parents'] = np.array(generation['Sequence'].values)[parent_indices] 123 | parents = np.array(generation['Chromosome'].values)[parent_indices] 124 | 125 | # create random splice bit 126 | splice_bit = np.random.randint(len(parents[0])) 127 | 128 | # create splice pair children from parent and cross over bits 129 | splices = [] 130 | splices.append(parents[0][0:splice_bit] + parents[1][splice_bit:len(parents[1])]) 131 | splices.append(parents[1][0:splice_bit] + parents[0][splice_bit:len(parents[0])]) 132 | 133 | # add splices to gene pool 134 | chromosome['Chromosome'] = splices[0] 135 | chromosome['Sequence'] = last_sequence + i + 1 136 | generation = generation.append(chromosome, ignore_index=True) 137 | 138 | chromosome['Chromosome'] = splices[1] 139 | chromosome['Sequence'] = last_sequence + i + 2 140 | generation = generation.append(chromosome, ignore_index=True) 141 | 142 | i += 1 143 | 144 | # return the generation 145 | return generation 146 | 147 | def fill_random(generation, generation_size, genes): 148 | 149 | # get generation attributes 150 | last_generation = generation['Generation'].max() 151 | last_sequence = generation['Sequence'].max() 152 | 153 | # for each random chromosome 154 | i = generation.shape[0] 155 | while i < generation_size: 156 | 157 | # create random chromosome 158 | chromosome = {} 159 | chromosome['Sequence'] = last_sequence + i + 1 160 | chromosome['Chromosome'] = ''.join(str(x) for x in list(np.random.randint(2, size=genes))) 161 | chromosome['Generation'] = last_generation 162 | chromosome['Birth'] = 'Random' 163 | chromosome['Parents'] = 0 164 | chromosome['Elite'] = False 165 | 166 | # check for uniqueness and add to gene pool 167 | if chromosome['Chromosome'] not in generation['Chromosome']: 168 | generation = generation.append(chromosome, ignore_index=True) 169 | i += 1 170 | 171 | # return the generation 172 | return generation 173 | 174 | def create_descendents(gene_pool, elite_rate, solution, stop_limit): 175 | 176 | # copy initial generation 177 | next_generation = gene_pool.copy() 178 | generation_size = next_generation.shape[0] 179 | 180 | # create generations until fitness criteria is achieved 181 | while gene_pool['Fitness'].max() < stop_limit: 182 | 183 | # print current generation 184 | # print(str(gene_pool['Generation'].max()) + ': ' + str(gene_pool['Fitness'].max())) 185 | 186 | # select elites with elite rate 187 | next_generation = select_elites(next_generation) 188 | 189 | # add splice pairs to generation 190 | splice_pair_rate = elite_rate / 2 191 | n_splice_pairs = int(splice_pair_rate * generation_size) 192 | next_generation = create_splices(next_generation, n_splice_pairs) 193 | 194 | # add mutants to generation 195 | mutant_rate = 0.60 196 | bit_flip_rate = 0.01 197 | n_mutants = int(mutant_rate * generation_size) 198 | next_generation = create_mutants(next_generation, n_mutants, bit_flip_rate) 199 | 200 | # fill the rest of the generation with random chromosomes for diversity 201 | next_generation = fill_random(next_generation, generation_size, 100) 202 | 203 | # compare fitness 204 | next_generation['Fitness'] = next_generation.apply(lambda row: ship.accuracy(row.Chromosome, solution), axis=1) 205 | 206 | # assign elites with elite rate 207 | elite_rate = 0.20 208 | next_generation = assign_elites(next_generation, elite_rate) 209 | next_generation 210 | 211 | # add generation to gene pool 212 | gene_pool = gene_pool.append(next_generation) 213 | 214 | return gene_pool 215 | 216 | def solve(solution, generation_size): 217 | # initialize the first random generation 218 | gene_pool = random_generation(generation_size, 100) 219 | 220 | # compare fitness 221 | gene_pool['Fitness'] = gene_pool.apply(lambda row: ship.accuracy(row.Chromosome, solution), axis=1) 222 | 223 | # assign elites with elite rate 224 | elite_rate = 0.20 225 | gene_pool = assign_elites(gene_pool, elite_rate) 226 | 227 | # create successive generations until termination criteria is met 228 | gene_pool = create_descendents(gene_pool, elite_rate, solution, 1.0) 229 | gene_pool = gene_pool.set_index('Sequence') 230 | 231 | return gene_pool --------------------------------------------------------------------------------