diff --git a/lab2/gen.py b/lab2/gen.py index 79f38f2..c64d8e0 100644 --- a/lab2/gen.py +++ b/lab2/gen.py @@ -30,6 +30,9 @@ class GARunConfig: pc: float # вероятность кроссинговера pm: float # вероятность мутации max_generations: int # максимальное количество поколений + max_best_repetitions: int | None = ( + None # остановка при повторении лучшего результата + ) seed: int | None = None # seed для генератора случайных чисел minimize: bool = False # если True, ищем минимум вместо максимума save_generations: list[int] | None = ( @@ -39,6 +42,7 @@ class GARunConfig: fitness_avg_threshold: float | None = ( None # порог среднего значения фитнес функции для остановки ) + log_every_generation: bool = False # логировать каждое поколение @dataclass(frozen=True) @@ -337,6 +341,7 @@ def genetic_algorithm(config: GARunConfig) -> GARunResult: best: Generation | None = None generation_number = 1 + best_repetitions = 0 while True: # Вычисляем фитнес для всех особей в популяции @@ -357,6 +362,12 @@ def genetic_algorithm(config: GARunConfig) -> GARunResult: ) history.append(current) + if config.log_every_generation: + print( + f"Generation #{generation_number} best: {current.best_fitness}," + f" avg: {np.mean(current.fitnesses)}" + ) + # Обновляем лучшую эпоху if ( best is None @@ -371,6 +382,15 @@ def genetic_algorithm(config: GARunConfig) -> GARunResult: if generation_number >= config.max_generations: stop_algorithm = True + if config.max_best_repetitions is not None and generation_number > 1: + if history[-2].best_fitness == current.best_fitness: + best_repetitions += 1 + + if best_repetitions == config.max_best_repetitions: + stop_algorithm = True + else: + best_repetitions = 0 + # if config.variance_threshold is not None: # fitness_variance = np.var(fitnesses) # if fitness_variance < config.variance_threshold: diff --git a/lab2/main.py b/lab2/main.py index fc1ebd6..0f3432e 100644 --- a/lab2/main.py +++ b/lab2/main.py @@ -11,14 +11,15 @@ config = GARunConfig( x_min=np.array([-5.12, -5.12]), x_max=np.array([5.12, 5.12]), fitness_func=fitness_function, - max_generations=200, pop_size=25, pc=0.5, pm=0.01, + max_generations=200, + max_best_repetitions=10, minimize=True, seed=17, - fitness_avg_threshold=0.05, - save_generations=[1, 2, 3, 5, 7, 10, 15], + save_generations=[1, 2, 3, 5, 7, 9, 10, 15, 19], + log_every_generation=True, ) result = genetic_algorithm(config) diff --git a/lab2/report/img/results/generation_009.png b/lab2/report/img/results/generation_009.png new file mode 100644 index 0000000..4b53281 Binary files /dev/null and b/lab2/report/img/results/generation_009.png differ diff --git a/lab2/report/img/results/generation_013.png b/lab2/report/img/results/generation_013.png deleted file mode 100644 index 7d217be..0000000 Binary files a/lab2/report/img/results/generation_013.png and /dev/null differ diff --git a/lab2/report/img/results/generation_015.png b/lab2/report/img/results/generation_015.png new file mode 100644 index 0000000..09fa5ef Binary files /dev/null and b/lab2/report/img/results/generation_015.png differ diff --git a/lab2/report/img/results/generation_019.png b/lab2/report/img/results/generation_019.png new file mode 100644 index 0000000..f7028a8 Binary files /dev/null and b/lab2/report/img/results/generation_019.png differ diff --git a/lab2/report/report.tex b/lab2/report/report.tex index 4b418a5..4aaa1a6 100644 --- a/lab2/report/report.tex +++ b/lab2/report/report.tex @@ -393,11 +393,11 @@ \item $N = 25$ -- размер популяции. \item $p_c = 0.5$ -- вероятность кроссинговера. \item $p_m = 0.01$ -- вероятность мутации. - \item $0.05$ -- минимальное среднее значение фитнесс функции по популяции для остановки алгоритма. Глобальный минимум функции равен $f(0, 0) = 0$. + \item Алгоритм останавливался, если лучшее значение фитнеса не изменялось $10$ поколений подряд. \item Использован арифметический кроссовер для real-coded хромосом. \end{itemize} - С каждым поколением точность найденного минимума становится выше. Популяция постепенно сходится к глобальному минимуму в точке $(0, 0)$. На графиках показаны 2D-контурный график (a) и 3D-поверхность целевой функции с точками популяции текущего поколения (b) и (c). + Популяция постепенно консолидируется вокруг глобального минимума в точке $(0, 0)$. Лучшая особь была найдена на поколнении №9 (см. Рис.~\ref{fig:gen9}), но судя по всему она подверглась мутации или кроссинговеру, поэтому алгоритм не остановился. На поколении №19 (см. Рис.~\ref{fig:lastgen}) было получено значение фитнеса $0.0201$, которое затем повторялось в следующих 10 поколениях. Алгоритм остановился на поколлении №29. На графиках показаны 2D-контурный график (a) и 3D-поверхность целевой функции с точками популяции текущего поколения (b) и (c). \begin{figure}[h!] \centering @@ -435,6 +435,13 @@ \label{fig:gen7} \end{figure} + \begin{figure}[h!] + \centering + \includegraphics[width=1\linewidth]{img/results/generation_009.png} + \caption{График целевой функции и популяции поколения №9} + \label{fig:gen9} + \end{figure} + \begin{figure}[h!] \centering \includegraphics[width=1\linewidth]{img/results/generation_010.png} @@ -444,8 +451,15 @@ \begin{figure}[h!] \centering - \includegraphics[width=1\linewidth]{img/results/generation_013.png} - \caption{График целевой функции и популяции поколения №13} + \includegraphics[width=1\linewidth]{img/results/generation_015.png} + \caption{График целевой функции и популяции поколения №15} + \label{fig:gen15} + \end{figure} + + \begin{figure}[h!] + \centering + \includegraphics[width=1\linewidth]{img/results/generation_019.png} + \caption{График целевой функции и популяции поколения №19} \label{fig:lastgen} \end{figure} @@ -454,6 +468,8 @@ \phantom{text} \newpage \phantom{text} + \newpage + \phantom{text} \newpage \section{Исследование реализации}