Генетические алгоритмы представляют собой эволюционный метод оптимизации, вдохновлённый принципами естественного отбора. В этом подходе возможные решения задачи рассматриваются как особи в популяции, каждая из которых обладает определёнными характеристиками. Качество решения определяется функцией приспособленности, и чем выше это значение, тем больше вероятность, что данная особь будет выбрана для генерации следующего поколения. В процессе эволюции происходит отбор лучших индивидов, их скрещивание для получения новых комбинаций признаков и случайные мутации, обеспечивающие разнообразие. Со временем популяция всё лучше адаптируется к задаче, постепенно улучшая качество решений.
Чтобы продемонстрировать минимальную структуру генетического алгоритма в DEAP, обычно используют классическую тестовую задачу OneMax. Её суть заключается в поиске бинарной строки максимальной приспособленности, где приспособленность определяется простой суммой единиц. Индивид представляет собой список из нулей и единичных значений фиксированной длины, а целью алгоритма является получение строки, состоящей из одних единиц. Благодаря простоте этой задачи можно сосредоточиться на механике работы генетического алгоритма: создании популяции, определении функции фитнеса, выборе операторов селекции, скрещивания и мутации. Это делает OneMax удобным примером для минимального рабочего кода.
import random
from deap import base, creator, tools, algorithms
# Определяем типы
creator.create("FitnessMax", base.Fitness, weights=(1.0,))
creator.create("Individual", list, fitness=creator.FitnessMax)
toolbox = base.Toolbox()
# Ген битов
toolbox.register("attr_bool", random.randint, 0, 1)
# Индивид и популяция
toolbox.register("individual", tools.initRepeat, creator.Individual,
toolbox.attr_bool, 20)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
# Фитнес-функция
def eval_one_max(individual):
return (sum(individual),)
toolbox.register("evaluate", eval_one_max)
# Операторы ГА
toolbox.register("mate", tools.cxTwoPoint)
toolbox.register("mutate", tools.mutFlipBit, indpb=0.05)
toolbox.register("select", tools.selTournament, tournsize=3)
def main():
random.seed(42)
pop = toolbox.population(n=50)
# Начальная оценка
fitnesses = list(map(toolbox.evaluate, pop))
for ind, fit in zip(pop, fitnesses):
ind.fitness.values = fit
for gen in range(20):
offspring = toolbox.select(pop, len(pop))
offspring = list(map(toolbox.clone, offspring))
# Cкрещивание
for c1, c2 in zip(offspring[::2], offspring[1::2]):
if random.random() < 0.5:
toolbox.mate(c1, c2)
del c1.fitness.values
del c2.fitness.values
# Мутации
for mutant in offspring:
if random.random() < 0.2:
toolbox.mutate(mutant)
del mutant.fitness.values
# Переоценка изменённых индивидов
invalid = [ind for ind in offspring if not ind.fitness.valid]
fits = map(toolbox.evaluate, invalid)
for ind, fit in zip(invalid, fits):
ind.fitness.values = fit
pop[:] = offspring
best = tools.selBest(pop, 1)[0]
print(f"Поколение {gen}: фитнес = {best.fitness.values[0]}")
print("\nЛучший найденный индивид:", best)
print("Сумма единиц:", sum(best))
if __name__ == "__main__":
main()
В итоге задача OneMax служит простым, но показательно эффективным примером применения генетических алгоритмов в DEAP. Она позволяет без лишних деталей понять структуру эволюционного поиска и увидеть, как популяция шаг за шагом улучшает решения за счёт отбора, скрещивания и мутаций. Отработав данный минимальный пример, становится проще переходить к более сложным задачам оптимизации, где фитнес-функция учитывает реальные параметры и имеет куда более сложную структуру.