Поиск минимума

This commit is contained in:
2025-09-11 11:40:37 +03:00
parent 1732a1dbb6
commit 4eb3031869
3 changed files with 30 additions and 14 deletions

View File

@@ -27,9 +27,10 @@ BASE_CONFIG = {
"precision_digits": 3,
"max_generations": 200,
"seed": 17,
"min_fitness_avg": 0.015, # критерий остановки
"minimize": True,
"fitness_avg_threshold": -0.048, # критерий остановки
# при включенном сохранении графиков на время смотреть бессмысленно
# "save_generations": [0, 50, 199],
"save_generations": [0, 50, 199],
}
@@ -83,7 +84,9 @@ def main():
print(f"Размеры популяции: {POPULATION_SIZES}")
print(f"Значения Pc: {PC_VALUES}")
print(f"Значения Pm: {PM_VALUES}")
print(f"Критерий остановки: среднее значение > {BASE_CONFIG['min_fitness_avg']}")
print(
f"Критерий остановки: среднее значение > {BASE_CONFIG['fitness_avg_threshold']}"
)
print("=" * 60)
# Создаем базовую папку

View File

@@ -137,6 +137,7 @@ class GARunConfig:
pm: float # вероятность мутации
max_generations: int # максимальное количество поколений
seed: int | None = None # seed для генератора случайных чисел
minimize: bool = False # если True, ищем минимум вместо максимума
save_generations: list[int] | None = (
None # индексы поколений для сохранения графиков
)
@@ -144,7 +145,7 @@ class GARunConfig:
variance_threshold: float | None = (
None # порог дисперсии для остановки (если None - не используется)
)
min_fitness_avg: float | None = (
fitness_avg_threshold: float | None = (
None # порог среднего значения фитнес функции для остановки
)
@@ -177,7 +178,7 @@ def genetic_algorithm(config: GARunConfig) -> GARunResult:
start = time.perf_counter()
history_best_x, history_best_f = [], []
history_populations_x, history_populations_f = [], []
best_x, best_f = 0, -float("inf")
best_x, best_f = 0, (float("inf") if config.minimize else -float("inf"))
for generation in range(config.max_generations):
xs, fits = eval_population(
@@ -185,9 +186,11 @@ def genetic_algorithm(config: GARunConfig) -> GARunResult:
)
# лучший в поколении + глобально лучший
gi = int(np.argmax(fits))
gi = int(np.argmin(fits)) if config.minimize else int(np.argmax(fits))
gen_best_x, gen_best_f = xs[gi], fits[gi]
if gen_best_f > best_f:
if (config.minimize and gen_best_f < best_f) or (
not config.minimize and gen_best_f > best_f
):
best_x, best_f = gen_best_x, gen_best_f
history_best_x.append(gen_best_x)
@@ -208,11 +211,14 @@ def genetic_algorithm(config: GARunConfig) -> GARunResult:
stop_algorithm = True
# Критерий остановки по среднему значению фитнес функции
if config.min_fitness_avg is not None:
if config.fitness_avg_threshold is not None:
mean_fitness = np.mean(fits)
if mean_fitness > config.min_fitness_avg:
if (config.minimize and mean_fitness < config.fitness_avg_threshold) or (
not config.minimize and mean_fitness > config.fitness_avg_threshold
):
comparator = "<" if config.minimize else ">"
print(
f"Остановка на поколении {generation}: среднее значение {mean_fitness:.6f} > {config.min_fitness_avg}"
f"Остановка на поколении {generation}: среднее значение {mean_fitness:.6f} {comparator} {config.fitness_avg_threshold}"
)
stop_algorithm = True
@@ -229,6 +235,7 @@ def genetic_algorithm(config: GARunConfig) -> GARunResult:
config.x_max,
config.results_dir,
config.fitness_func,
minimize=config.minimize,
)
break
@@ -244,10 +251,12 @@ def genetic_algorithm(config: GARunConfig) -> GARunResult:
config.x_max,
config.results_dir,
config.fitness_func,
minimize=config.minimize,
)
# селекция
parents = reproduction(population, fits)
# селекция (для минимума инвертируем знак, чтобы минимальные значения становились максимальными)
fitnesses_for_selection = fits if not config.minimize else [-f for f in fits]
parents = reproduction(population, fitnesses_for_selection)
# кроссинговер попарно
next_population = crossover(parents, config.pc)
@@ -283,6 +292,7 @@ def plot_generation_snapshot(
x_max: float,
results_dir: str,
fitness_func: Callable[[float], float],
minimize: bool = False,
) -> str:
"""
График для конкретного поколения с отображением всей популяции.
@@ -311,7 +321,9 @@ def plot_generation_snapshot(
)
# Лучшая особь красной точкой
best_idx = np.argmax(current_pop_f)
best_idx = (
int(np.argmin(current_pop_f)) if minimize else int(np.argmax(current_pop_f))
)
plt.scatter(
[current_pop_x[best_idx]],
[current_pop_f[best_idx]],

View File

@@ -20,6 +20,7 @@ config = GARunConfig(
pm=0.01,
max_generations=200,
seed=17,
minimize=True,
save_generations=[
0,
1,
@@ -34,7 +35,7 @@ config = GARunConfig(
], # поколения для сохранения графиков
results_dir="results",
# variance_threshold=1e-6, # порог дисперсии для остановки
min_fitness_avg=0.015, # порог среднего значения для остановки
fitness_avg_threshold=-0.048, # порог среднего значения для остановки
)
# Запускаем генетический алгоритм