遗传算法优化中出现IndexError索引越界问题的解决咨询
Hey there! Let's break down why you're hitting this IndexError and fix it step by step.
What's Causing the Error?
The root issue is that your fitness_array function hardcodes the loop to run population_size times (which is 5), but as your algorithm runs, the new_population array can end up smaller than 5 elements.
Here's the breakdown: In your generation loop, when return_crossovered_child returns 0 (which happens when the random number is >= crossover_rate), you use continue and don't add anything to last_population. Over time, this can lead to new_population having fewer than 5 rows, but fitness_array still tries to access indices 0-4 regardless. When new_population only has 4 elements, accessing index 4 triggers the error you see.
Fix Steps
Let's address this with three key fixes:
Make fitness_array use the actual population size instead of the constant
Replace the hardcodedpopulation_sizein the loop withpopulation.shape[0](the actual number of rows in the population array). This ensures you only iterate over existing elements.Modified
fitness_arrayfunction:def fitness_array(population): fitness_array = [] # Use the actual length of the population, not the constant for i in range(population.shape[0]): fitness_array.append(summation(population[i,0], population[i,1])) return np.array(fitness_array)Ensure
last_populationalways reaches the desired population size
Right now, if some crossover attempts return 0, you're missing elements inlast_population. We need to keep generating children until we have exactlypopulation_sizeelements.Update the generation loop's inner part:
for i in range(generation): mate_pool = return_ranked_selected_population(new_population) last_population = [] # Keep adding children until we reach the desired population size while len(last_population) < population_size: crossed_child = return_crossovered_child(mate_pool) if crossed_child != 0: mutated_child = return_mutated_child(crossed_child) last_population.append(mutated_child) new_population = np.array(last_population) a, b = return_best_worst_population(new_population) print('generation', i, "best", a, "fitness", b)Clean up
return_best_worst_populationfor robustness
The original sorting logic works, but we can make it more readable and ensure it handles variable population sizes correctly:def return_best_worst_population(population): fitness_array1 = fitness_array(population) new_population = np.zeros(population.shape) new_fitness_array = np.zeros(fitness_array1.shape) # Sort indices from worst to best, then reverse for best to worst order best_indices = np.argsort(fitness_array1)[::-1] for row_counter, idx in enumerate(best_indices): new_population[row_counter, :] = population[idx, :] new_fitness_array[row_counter] = fitness_array1[idx] return new_population[0], new_fitness_array[0]
Why These Fixes Work
- The first fix ensures your fitness calculation never tries to access indices that don't exist, since it uses the actual current population size.
- The second fix guarantees that
new_populationalways has exactlypopulation_sizeelements, so you never end up with a smaller array that causes index issues. - The third fix cleans up the sorting logic to be more maintainable while keeping its original purpose of returning the best individual.
Full Modified Code (Key Sections Updated)
Here's the full code with all fixes applied:
import numpy as np import itertools import random population_size = 5 chromosome_size = 2 geneset1 = [7,9,19,23,25,27,31] geneset2 = [2,78,46,68,34,24,12] mutation_rate = 0.1 crossover_rate = 0.95 generation = 24 def summation(a,b): return a+b # Fixed fitness function using actual population size def fitness_array(population): fitness_array = [] for i in range(population.shape[0]): fitness_array.append(summation(population[i,0], population[i,1])) return np.array(fitness_array) def return_random_population(geneset1, geneset2, population_size): population = random.sample(set(itertools.product(geneset1, geneset2)), population_size) return np.array(population) # Cleaned up best/worst sorting def return_best_worst_population(population): fitness_array1 = fitness_array(population) new_population = np.zeros(population.shape) new_fitness_array = np.zeros(fitness_array1.shape) best_indices = np.argsort(fitness_array1)[::-1] for row_counter, idx in enumerate(best_indices): new_population[row_counter, :] = population[idx, :] new_fitness_array[row_counter] = fitness_array1[idx] return new_population[0], new_fitness_array[0] def return_ranked_selected_population(population): ranked_population = [] fitness_of_given_population = fitness_array(population) sort = np.argsort(fitness_of_given_population) rank_population = np.zeros(fitness_of_given_population.shape) x = 1 for i in sort: rank_population[i] = x x += 1 fitness_score = [(x/sum(rank_population)) for x in rank_population] for i in range(len(fitness_score)): n = int(fitness_score[i]*100) for j in range(n): ranked_population.append(population[i]) return ranked_population def return_crossovered_child(ranked_selected_population): if np.random.random() < crossover_rate: a = np.random.randint(0, len(ranked_selected_population)) b = np.random.randint(0, len(ranked_selected_population)) parent1 = ranked_selected_population[a] parent2 = ranked_selected_population[b] slicing_point = np.random.randint(0, chromosome_size) child = list(parent1[:slicing_point]) + list(parent2[slicing_point:]) return child return 0 def return_mutated_child(crossovered_child): for i in range(chromosome_size): if np.random.random() < mutation_rate: if i == 0: crossovered_child[i] = np.random.choice(geneset1) else: crossovered_child[i] = np.random.choice(geneset2) return crossovered_child # Initial population setup new_population = return_random_population(geneset1, geneset2, population_size) a, b = return_best_worst_population(new_population) print("best", a, "fitness", b) # Fixed generation loop ensuring population size stays consistent for i in range(generation): mate_pool = return_ranked_selected_population(new_population) last_population = [] while len(last_population) < population_size: crossed_child = return_crossovered_child(mate_pool) if crossed_child != 0: mutated_child = return_mutated_child(crossed_child) last_population.append(mutated_child) new_population = np.array(last_population) a, b = return_best_worst_population(new_population) print('generation', i, "best", a, "fitness", b)
Give this a run, and you should no longer hit that IndexError. The algorithm will maintain a consistent population size through all generations, and the fitness calculation will always work with the actual number of individuals present.
内容的提问来源于stack exchange,提问作者rafe zayed

