89 lines
3.1 KiB
Python
89 lines
3.1 KiB
Python
import numpy as np
|
||
|
||
from .types import Fitnesses, Population
|
||
|
||
|
||
def roulette_selection(population: Population, fitnesses: Fitnesses) -> Population:
|
||
"""Селекция методом рулетки.
|
||
|
||
Чем больше значение фитнеса, тем больше вероятность выбора особи. Для минимизации
|
||
значения фитнеса нужно предварительно инвертировать.
|
||
"""
|
||
# Чтобы работать с отрицательными f, сдвигаем значения фитнес функции на минимальное
|
||
# значение в популяции. Вычитаем min_fit, т. к. min_fit может быть отрицательным.
|
||
min_fit = np.min(fitnesses)
|
||
shifted_fitnesses = fitnesses - min_fit + 1e-12
|
||
|
||
# Получаем вероятности для каждой особи
|
||
probs = shifted_fitnesses / np.sum(shifted_fitnesses)
|
||
cum = np.cumsum(probs)
|
||
|
||
# Выбираем особей методом рулетки
|
||
selected = []
|
||
for _ in population:
|
||
r = np.random.random()
|
||
idx = int(np.searchsorted(cum, r, side="left"))
|
||
selected.append(population[idx])
|
||
|
||
return selected
|
||
|
||
|
||
def tournament_selection(
|
||
population: Population,
|
||
fitnesses: Fitnesses,
|
||
k: int = 3,
|
||
) -> Population:
|
||
"""Турнирная селекция.
|
||
|
||
В каждом турнире случайно выбирается k особей, и побеждает та,
|
||
у которой лучшее (наибольшее) значение фитнеса. Для минимизации
|
||
значения фитнеса нужно предварительно инвертировать.
|
||
|
||
Args:
|
||
population: список особей (Population)
|
||
fitnesses: список или массив фитнесов (Fitnesses)
|
||
k: размер турнира
|
||
|
||
Returns:
|
||
Новая популяция того же размера
|
||
"""
|
||
size = len(population)
|
||
selected = []
|
||
for _ in range(size):
|
||
idxs = np.random.choice(size, size=k, replace=False)
|
||
|
||
fits = fitnesses[idxs]
|
||
|
||
winner_idx = idxs[np.argmax(fits)]
|
||
selected.append(population[winner_idx])
|
||
return selected
|
||
|
||
|
||
def stochastic_tournament_selection(
|
||
population: Population,
|
||
fitnesses: Fitnesses,
|
||
k: int = 3,
|
||
p_best: float = 0.75,
|
||
) -> Population:
|
||
"""Стохастическая турнирная селекция.
|
||
|
||
Побеждает лучший в турнире с вероятностью p_best, иначе выбирается
|
||
случайный участник турнира.
|
||
"""
|
||
size = len(population)
|
||
selected = []
|
||
|
||
for _ in range(size):
|
||
idxs = np.random.choice(size, size=k, replace=False)
|
||
fits = fitnesses[idxs]
|
||
order = np.argsort(-fits)
|
||
|
||
if np.random.random() < p_best:
|
||
winner_idx = idxs[order[0]]
|
||
else:
|
||
winner_idx = np.random.choice(idxs[1:]) if k > 1 else idxs[0]
|
||
|
||
selected.append(population[winner_idx])
|
||
|
||
return selected
|