diff --git a/lab1/expirements.py b/lab1/expirements.py index 112f6b8..c3e8148 100644 --- a/lab1/expirements.py +++ b/lab1/expirements.py @@ -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) # Создаем базовую папку diff --git a/lab1/gen.py b/lab1/gen.py index 32b207d..25a4f71 100644 --- a/lab1/gen.py +++ b/lab1/gen.py @@ -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]], diff --git a/lab1/main.py b/lab1/main.py index 13576c5..35c06f1 100644 --- a/lab1/main.py +++ b/lab1/main.py @@ -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, # порог среднего значения для остановки ) # Запускаем генетический алгоритм