Таблицы
This commit is contained in:
327
lab5/csv_to_tex.py
Normal file
327
lab5/csv_to_tex.py
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
"""
|
||||||
|
Скрипт для конвертации результатов экспериментов из CSV в LaTeX таблицы для lab5.
|
||||||
|
|
||||||
|
Адаптирован из lab2/csv_to_tex.py для работы с форматом эволюционных стратегий.
|
||||||
|
Формат входных данных: "время±стд (поколения±стд) фитнес"
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Настройка цвета для выделения лучших результатов
|
||||||
|
# None - только жирным, строка (например "magenta") - жирным и цветом
|
||||||
|
HIGHLIGHT_COLOR = "magenta"
|
||||||
|
|
||||||
|
|
||||||
|
def parse_csv_file(csv_path: str) -> tuple[str, list[list[str]]]:
|
||||||
|
"""
|
||||||
|
Парсит CSV файл с результатами эксперимента.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
csv_path: Путь к CSV файлу
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple с заголовком и данными таблицы
|
||||||
|
"""
|
||||||
|
with open(csv_path, "r", encoding="utf-8") as file:
|
||||||
|
lines = file.readlines()
|
||||||
|
|
||||||
|
# Удаляем пустые строки и берём только строки с данными
|
||||||
|
clean_lines = [line.strip() for line in lines if line.strip()]
|
||||||
|
|
||||||
|
# Первая строка - заголовки
|
||||||
|
header = clean_lines[0]
|
||||||
|
|
||||||
|
# Остальные строки - данные
|
||||||
|
data_lines = clean_lines[1:]
|
||||||
|
|
||||||
|
# Парсим данные
|
||||||
|
data_rows = []
|
||||||
|
for line in data_lines:
|
||||||
|
parts = line.split(",")
|
||||||
|
if len(parts) >= 2: # mu + хотя бы одно значение
|
||||||
|
data_rows.append(parts)
|
||||||
|
|
||||||
|
return header, data_rows
|
||||||
|
|
||||||
|
|
||||||
|
def extract_time_value(value: str) -> float | None:
|
||||||
|
"""
|
||||||
|
Извлекает значение времени из строки формата "X.Y±Z.W (...)".
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: Строка с результатом
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Время выполнения как float или None если значение пустое
|
||||||
|
"""
|
||||||
|
value = value.strip()
|
||||||
|
if value == "—" or value == "" or value == "–":
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Ищем паттерн "число.число±число"
|
||||||
|
match = re.match(r"(\d+\.?\d*)±", value)
|
||||||
|
if match:
|
||||||
|
return float(match.group(1))
|
||||||
|
|
||||||
|
# Если нет ±, пробуем просто число перед скобкой
|
||||||
|
match = re.match(r"(\d+\.?\d*)\s*\(", value)
|
||||||
|
if match:
|
||||||
|
return float(match.group(1))
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def extract_generations_value(value: str) -> float | None:
|
||||||
|
"""
|
||||||
|
Извлекает среднее число поколений из строки формата "... (X±Y) ...".
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: Строка с результатом
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Среднее число поколений как float или None если значение пустое
|
||||||
|
"""
|
||||||
|
value = value.strip()
|
||||||
|
if value == "—" or value == "" or value == "–":
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Ищем паттерн "(число±число)" и берём первое число
|
||||||
|
match = re.search(r"\((\d+\.?\d*)±", value)
|
||||||
|
if match:
|
||||||
|
return float(match.group(1))
|
||||||
|
|
||||||
|
# Если нет ±, пробуем просто число в скобках
|
||||||
|
match = re.search(r"\((\d+\.?\d*)\)", value)
|
||||||
|
if match:
|
||||||
|
return float(match.group(1))
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def find_best_time(data_rows: list[list[str]]) -> float | None:
|
||||||
|
"""
|
||||||
|
Находит минимальное время выполнения среди всех значений в таблице.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data_rows: Строки данных таблицы
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Минимальное время или None если нет валидных значений
|
||||||
|
"""
|
||||||
|
min_time = None
|
||||||
|
|
||||||
|
for row in data_rows:
|
||||||
|
for i in range(1, len(row)): # Пропускаем первую колонку (mu)
|
||||||
|
time_value = extract_time_value(row[i])
|
||||||
|
if time_value is not None:
|
||||||
|
if min_time is None or time_value < min_time:
|
||||||
|
min_time = time_value
|
||||||
|
|
||||||
|
return min_time
|
||||||
|
|
||||||
|
|
||||||
|
def find_best_generations(data_rows: list[list[str]]) -> float | None:
|
||||||
|
"""
|
||||||
|
Находит минимальное число поколений среди всех значений в таблице.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data_rows: Строки данных таблицы
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Минимальное число поколений или None если нет валидных значений
|
||||||
|
"""
|
||||||
|
min_gens = None
|
||||||
|
|
||||||
|
for row in data_rows:
|
||||||
|
for i in range(1, len(row)): # Пропускаем первую колонку (mu)
|
||||||
|
gens_value = extract_generations_value(row[i])
|
||||||
|
if gens_value is not None:
|
||||||
|
if min_gens is None or gens_value < min_gens:
|
||||||
|
min_gens = gens_value
|
||||||
|
|
||||||
|
return min_gens
|
||||||
|
|
||||||
|
|
||||||
|
def format_value(
|
||||||
|
value: str, best_time: float | None = None, best_gens: float | None = None
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
Форматирует значение для LaTeX таблицы, выделяя лучшие результаты жирным.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: Строковое значение из CSV
|
||||||
|
best_time: Лучшее время в таблице для сравнения
|
||||||
|
best_gens: Лучшее число поколений для сравнения
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Отформатированное значение для LaTeX
|
||||||
|
"""
|
||||||
|
value = value.strip()
|
||||||
|
if value == "—" or value == "" or value == "–":
|
||||||
|
return "—"
|
||||||
|
|
||||||
|
# Парсим значение: "время±стд (поколения±стд) фитнес"
|
||||||
|
# Пример: "60.6±47.9 (37±29) 0.0000"
|
||||||
|
pattern = r"(\d+\.?\d*)±(\d+\.?\d*)\s*\((\d+\.?\d*)±(\d+\.?\d*)\)\s+(\d+\.?\d+)"
|
||||||
|
match = re.match(pattern, value)
|
||||||
|
|
||||||
|
if not match:
|
||||||
|
# Если не удалось распарсить, возвращаем как есть
|
||||||
|
return value
|
||||||
|
|
||||||
|
time_avg = float(match.group(1))
|
||||||
|
time_std = float(match.group(2))
|
||||||
|
gens_avg = float(match.group(3))
|
||||||
|
gens_std = float(match.group(4))
|
||||||
|
fitness = match.group(5)
|
||||||
|
|
||||||
|
# Формируем части БЕЗ стандартных отклонений
|
||||||
|
time_part = f"{time_avg:.1f}"
|
||||||
|
gens_part = f"{gens_avg:.0f}"
|
||||||
|
|
||||||
|
# Проверяем, является ли время лучшим
|
||||||
|
is_best_time = best_time is not None and abs(time_avg - best_time) < 0.1
|
||||||
|
is_best_gens = best_gens is not None and abs(gens_avg - best_gens) < 0.1
|
||||||
|
|
||||||
|
# Выделяем лучшее время
|
||||||
|
if is_best_time:
|
||||||
|
if HIGHLIGHT_COLOR is not None:
|
||||||
|
time_part = f"\\textcolor{{{HIGHLIGHT_COLOR}}}{{\\textbf{{{time_part}}}}}"
|
||||||
|
else:
|
||||||
|
time_part = f"\\textbf{{{time_part}}}"
|
||||||
|
|
||||||
|
# Выделяем лучшее число поколений
|
||||||
|
if is_best_gens:
|
||||||
|
if HIGHLIGHT_COLOR is not None:
|
||||||
|
gens_part = f"\\textcolor{{{HIGHLIGHT_COLOR}}}{{\\textbf{{{gens_part}}}}}"
|
||||||
|
else:
|
||||||
|
gens_part = f"\\textbf{{{gens_part}}}"
|
||||||
|
|
||||||
|
# Не показываем фитнес в таблице, т.к. он всегда близок к нулю
|
||||||
|
return f"{time_part} ({gens_part})"
|
||||||
|
|
||||||
|
|
||||||
|
def generate_latex_table(dimension: str, header: str, data_rows: list[list[str]]) -> str:
|
||||||
|
"""
|
||||||
|
Генерирует LaTeX код таблицы.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dimension: Размерность задачи (2 или 3)
|
||||||
|
header: Заголовок таблицы
|
||||||
|
data_rows: Строки данных
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
LaTeX код таблицы
|
||||||
|
"""
|
||||||
|
# Находим лучшее время и лучшее число поколений в таблице
|
||||||
|
best_time = find_best_time(data_rows)
|
||||||
|
best_gens = find_best_generations(data_rows)
|
||||||
|
|
||||||
|
# Извлекаем заголовки колонок из header
|
||||||
|
header_parts = header.split(",")
|
||||||
|
p_mut_values = header_parts[1:] # Пропускаем "mu \ p_mut"
|
||||||
|
|
||||||
|
num_cols = len(p_mut_values)
|
||||||
|
|
||||||
|
latex_code = f""" \\begin{{table}}[h!]
|
||||||
|
\\centering
|
||||||
|
\\small
|
||||||
|
\\caption{{Результаты для $n = {dimension}$. Формат: время в мс (число поколений)}}
|
||||||
|
\\begin{{tabularx}}{{{0.95 if num_cols <= 5 else 1.0}\\linewidth}}{{l *{{{num_cols}}}{{Y}}}}
|
||||||
|
\\toprule
|
||||||
|
$\\mathbf{{\\mu \\;\\backslash\\; p_{{mut}}}}$"""
|
||||||
|
|
||||||
|
# Добавляем заголовки p_mut
|
||||||
|
for p_mut in p_mut_values:
|
||||||
|
latex_code += f" & \\textbf{{{p_mut.strip()}}}"
|
||||||
|
|
||||||
|
latex_code += " \\\\\n \\midrule\n"
|
||||||
|
|
||||||
|
# Добавляем строки данных
|
||||||
|
for row in data_rows:
|
||||||
|
mu_value = row[0].strip()
|
||||||
|
latex_code += f" \\textbf{{{mu_value}}}"
|
||||||
|
|
||||||
|
# Добавляем значения для каждого p_mut
|
||||||
|
for i in range(1, len(row)):
|
||||||
|
value = format_value(row[i], best_time, best_gens)
|
||||||
|
latex_code += f" & {value}"
|
||||||
|
|
||||||
|
# Заполняем недостающие колонки если их меньше чем в заголовке
|
||||||
|
for i in range(len(row) - 1, num_cols):
|
||||||
|
latex_code += " & —"
|
||||||
|
|
||||||
|
latex_code += " \\\\\n"
|
||||||
|
|
||||||
|
latex_code += f""" \\bottomrule
|
||||||
|
\\end{{tabularx}}
|
||||||
|
\\label{{tab:es_results_{dimension}}}
|
||||||
|
\\end{{table}}"""
|
||||||
|
|
||||||
|
return latex_code
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Основная функция скрипта."""
|
||||||
|
experiments_path = Path("lab5_experiments")
|
||||||
|
|
||||||
|
if not experiments_path.exists():
|
||||||
|
print("Папка lab5_experiments не найдена!")
|
||||||
|
return
|
||||||
|
|
||||||
|
tables = []
|
||||||
|
|
||||||
|
# Обрабатываем файлы dimension_2.csv и dimension_3.csv
|
||||||
|
for dimension in [2, 3]:
|
||||||
|
csv_file = experiments_path / f"dimension_{dimension}.csv"
|
||||||
|
|
||||||
|
if csv_file.exists():
|
||||||
|
print(f"Обрабатываем {csv_file}...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
header, data_rows = parse_csv_file(str(csv_file))
|
||||||
|
best_time = find_best_time(data_rows)
|
||||||
|
best_gens = find_best_generations(data_rows)
|
||||||
|
latex_table = generate_latex_table(str(dimension), header, data_rows)
|
||||||
|
tables.append(latex_table)
|
||||||
|
print(
|
||||||
|
f"[OK] Таблица для n={dimension} готова (лучшее время: {best_time:.1f} мс, лучшее число поколений: {best_gens:.0f})"
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[ERROR] Ошибка при обработке {csv_file}: {e}")
|
||||||
|
else:
|
||||||
|
print(f"[ERROR] Файл {csv_file} не найден")
|
||||||
|
|
||||||
|
# Сохраняем все таблицы в файл
|
||||||
|
if tables:
|
||||||
|
output_file = experiments_path / "tables.tex"
|
||||||
|
with open(output_file, "w", encoding="utf-8") as f:
|
||||||
|
f.write("% Автоматически сгенерированные LaTeX таблицы\n")
|
||||||
|
f.write(
|
||||||
|
"% Лучший результат по времени и по числу поколений выделены жирным отдельно\n"
|
||||||
|
)
|
||||||
|
f.write("% Убедитесь, что подключен \\usepackage{tabularx}\n")
|
||||||
|
if HIGHLIGHT_COLOR is not None:
|
||||||
|
f.write(
|
||||||
|
"% ВНИМАНИЕ: Убедитесь, что подключен \\usepackage{xcolor} для цветового выделения\n"
|
||||||
|
)
|
||||||
|
f.write(
|
||||||
|
"% Используйте \\newcolumntype{Y}{>{\\centering\\arraybackslash}X} перед таблицами\n\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, table in enumerate(tables):
|
||||||
|
if i > 0:
|
||||||
|
f.write("\n \n")
|
||||||
|
f.write(table + "\n")
|
||||||
|
|
||||||
|
print(f"\n[OK] Все таблицы сохранены в файл '{output_file}'")
|
||||||
|
print(f"Сгенерировано таблиц: {len(tables)}")
|
||||||
|
else:
|
||||||
|
print("Не найдено данных для генерации таблиц!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
||||||
@@ -228,38 +228,20 @@
|
|||||||
\newpage
|
\newpage
|
||||||
\section{Результаты работы}
|
\section{Результаты работы}
|
||||||
|
|
||||||
Для анализа параметров стратегии подготовлен набор серийных экспериментов. В таблице~\ref{tab:configs} представлены базовые комбинации, используемые для минимизации функции при $n=2$ и $n=3$.
|
Для демонстрации работы алгоритма была выполнена визуализация процесса оптимизации двумерной функции ($n=2$) со следующими параметрами:
|
||||||
|
|
||||||
\newcolumntype{Y}{>{\centering\arraybackslash}X}
|
\begin{itemize}
|
||||||
\begin{table}[h!]
|
\item $\mu = 20$ -- размер популяции родителей.
|
||||||
\centering
|
\item $\lambda = 80$ -- число потомков ($\lambda = 4\mu$).
|
||||||
\small
|
\item $p_{mut} = 0.7$ -- вероятность мутации каждой координаты.
|
||||||
\caption{Экспериментальные конфигурации}
|
\item Промежуточная рекомбинация двух родителей.
|
||||||
\begin{tabularx}{0.9\linewidth}{l *{4}{Y}}
|
\item $(\mu, \lambda)$-селекция: родители полностью заменяются.
|
||||||
\toprule
|
\item Адаптивное масштабирование шага мутации по правилу успеха $1/5$.
|
||||||
\textbf{ID} & $\mu$ & $\lambda$ & $\sigma_0$ & Режим адаптации \\
|
\item Начальное стандартное отклонение $\sigma_0 = 0.15 \cdot (x_{max} - x_{min})$.
|
||||||
\midrule
|
\end{itemize}
|
||||||
A & 1 & 1 & 0.5 & правило успеха $1/5$ \\
|
|
||||||
B & 5 & 25 & 0.3 & логнормальная самоадаптация \\
|
|
||||||
C & 10 & 70 & 0.2 & фиксированное $\sigma$ \\
|
|
||||||
D & 15 & 105 & 0.2 & смешанный: рекомбинация $+$ правило $1/5$ \\
|
|
||||||
\bottomrule
|
|
||||||
\end{tabularx}
|
|
||||||
\label{tab:configs}
|
|
||||||
\end{table}
|
|
||||||
|
|
||||||
Визуализация для двумерного случая воспроизводит поверхность целевой функции и положение популяции на каждом шаге. Пошаговый режим позволяет наблюдать влияние изменения дисперсий: при успешных мутациях облако точек расширяется, при неудачах сжимается вокруг текущего минимума. Для трёхмерного случая графически отображается последовательность лучших точек и динамика величины функции во времени.
|
Визуализация воспроизводит поверхность целевой функции и положение популяции на каждом шаге. Пошаговый режим позволяет наблюдать влияние изменения дисперсий: при успешных мутациях облако точек расширяется, при неудачах сжимается вокруг текущего минимума. Популяция постепенно консолидируется вокруг глобального минимума в точке $(0, 0)$.
|
||||||
|
|
||||||
\subsection{Пошаговая визуализация процесса оптимизации}
|
|
||||||
|
|
||||||
Чтобы получить в отчёт те же трёхмерные графики, что присутствовали во второй лабораторной работе, подготовлен отдельный скрипт \texttt{lab5/generate\_report\_figures.py}. Он переиспользует функцию визуализации из модуля \texttt{main.py}, на каждом указанном поколении строит контурный и два трёхмерных вида поверхности и сохраняет кадры в каталог \texttt{lab5/report/img/results}. Команды следует выполнять из корня репозитория, предварительно установив зависимости:
|
|
||||||
|
|
||||||
\begin{verbatim}
|
|
||||||
pip install numpy matplotlib
|
|
||||||
python lab5/generate_report_figures.py
|
|
||||||
\end{verbatim}
|
|
||||||
|
|
||||||
После выполнения команды изображения автоматически появятся в каталоге отчёта и будут подхвачены при компиляции \LaTeX-документа.
|
|
||||||
|
|
||||||
\begin{figure}[H]
|
\begin{figure}[H]
|
||||||
\centering
|
\centering
|
||||||
@@ -310,16 +292,73 @@
|
|||||||
\end{figure}
|
\end{figure}
|
||||||
|
|
||||||
|
|
||||||
При запуске экспериментов собираются следующие показатели:
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section{Исследование параметров}
|
||||||
|
|
||||||
|
В рамках лабораторной работы было проведено исследование влияния размера популяции $\mu$ и вероятности мутации $p_{mut}$ на эффективность алгоритма. Для экспериментов использовалась $(\mu, \lambda)$-стратегия с $\lambda = 5\mu$, промежуточной рекомбинацией и адаптивным масштабированием шага мутации по правилу успеха $1/5$.
|
||||||
|
|
||||||
|
\subsection{Проведение измерений}
|
||||||
|
|
||||||
|
Для исследования были выбраны следующие значения параметров:
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item число поколений до достижения целевого порога $f(\mathbf{x}) < 10^{-6}$ либо исчерпания лимита поколений;
|
\item $\mu = 5, 10, 20, 40$ -- размер популяции родителей.
|
||||||
\item итоговая точность (значение функции и евклидово расстояние до нулевого вектора);
|
\item $p_{mut} = 0.3, 0.5, 0.7, 0.9, 1.0$ -- вероятность мутации каждой координаты.
|
||||||
\item суммарное процессорное время на серию запусков (возвращается в миллисекундах);
|
\item Количество независимых запусков для усреднения результатов: 5.
|
||||||
\item статистика успехов для правила $1/5$ и распределение актуальных $\sigma_i$.
|
\item Критерий остановки: достижение порога $f(\mathbf{x}) < 10^{-6}$ или исчерпание лимита 300 поколений.
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
На практике $(1+1)$-стратегия показывает самую быструю сходимость на гладком рельефе, однако чувствительна к выбору начального $\sigma_0$. Популяционные режимы требовательнее по времени, но надёжнее удерживаются в окрестности минимума и легче масштабируются на $n=3$.
|
Результаты измерений представлены в таблицах~\ref{tab:es_results_2} и~\ref{tab:es_results_3}. В ячейках указано среднее время выполнения в миллисекундах и среднее число поколений до достижения критерия остановки. Лучшие результаты по времени выполнения и по числу поколений выделены жирным цветом.
|
||||||
|
|
||||||
|
\newcolumntype{Y}{>{\centering\arraybackslash}X}
|
||||||
|
|
||||||
|
\begin{table}[h!]
|
||||||
|
\centering
|
||||||
|
\small
|
||||||
|
\caption{Результаты для $n = 2$. Формат: время в мс (число поколений)}
|
||||||
|
\begin{tabularx}{0.95\linewidth}{l *{5}{Y}}
|
||||||
|
\toprule
|
||||||
|
$\mathbf{\mu \;\backslash\; p_{mut}}$ & \textbf{0.30} & \textbf{0.50} & \textbf{0.70} & \textbf{0.90} & \textbf{1.00} \\
|
||||||
|
\midrule
|
||||||
|
\textbf{5} & 60.6 (37) & 35.1 (23) & 37.9 (25) & 29.2 (20) & \textcolor{magenta}{\textbf{20.4}} (17) \\
|
||||||
|
\textbf{10} & 69.5 (22) & 84.1 (28) & 61.1 (21) & 48.2 (17) & 38.1 (16) \\
|
||||||
|
\textbf{20} & 109.6 (18) & 120.4 (20) & 107.0 (18) & 100.2 (17) & 69.4 (15) \\
|
||||||
|
\textbf{40} & 239.8 (19) & 225.9 (19) & 199.9 (17) & 180.6 (16) & 121.4 (\textcolor{magenta}{\textbf{13}}) \\
|
||||||
|
\bottomrule
|
||||||
|
\end{tabularx}
|
||||||
|
\label{tab:es_results_2}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\begin{table}[h!]
|
||||||
|
\centering
|
||||||
|
\small
|
||||||
|
\caption{Результаты для $n = 3$. Формат: время в мс (число поколений)}
|
||||||
|
\begin{tabularx}{0.95\linewidth}{l *{5}{Y}}
|
||||||
|
\toprule
|
||||||
|
$\mathbf{\mu \;\backslash\; p_{mut}}$ & \textbf{0.30} & \textbf{0.50} & \textbf{0.70} & \textbf{0.90} & \textbf{1.00} \\
|
||||||
|
\midrule
|
||||||
|
\textbf{5} & 146.0 (88) & 212.2 (126) & 93.7 (60) & 44.8 (29) & \textcolor{magenta}{\textbf{30.3}} (25) \\
|
||||||
|
\textbf{10} & 155.9 (49) & 149.3 (48) & 88.7 (30) & 69.8 (24) & 55.7 (23) \\
|
||||||
|
\textbf{20} & 235.5 (38) & 199.0 (32) & 157.7 (26) & 125.8 (21) & 105.9 (21) \\
|
||||||
|
\textbf{40} & 670.3 (53) & 374.2 (31) & 311.8 (26) & 258.2 (22) & 194.0 (\textcolor{magenta}{\textbf{20}}) \\
|
||||||
|
\bottomrule
|
||||||
|
\end{tabularx}
|
||||||
|
\label{tab:es_results_3}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\subsection{Анализ результатов}
|
||||||
|
|
||||||
|
Анализ экспериментальных данных выявляет следующие закономерности:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{Влияние вероятности мутации:} Увеличение $p_{mut}$ от 0.3 до 1.0 последовательно улучшает результаты как по времени, так и по числу поколений. Это объясняется тем, что более частая мутация всех координат ускоряет исследование пространства и адаптацию популяции. Лучшие результаты достигаются при $p_{mut} = 1.0$ (мутация всех координат на каждом шаге).
|
||||||
|
|
||||||
|
\item \textbf{Влияние размера популяции:} При малых $\mu$ (5-10) алгоритм демонстрирует наименьшее время выполнения и умеренное число поколений. С ростом $\mu$ до 40 время увеличивается пропорционально размеру популяции, но число поколений снижается благодаря более широкому охвату пространства поиска. Для двумерной задачи оптимальным является $\mu=5$, $p_{mut}=1.0$ (20.4 мс, 17 поколений).
|
||||||
|
|
||||||
|
\item \textbf{Масштабирование на размерность:} При переходе от $n=2$ к $n=3$ время выполнения изменяется незначительно (30.3 мс против 20.4 мс для лучшей конфигурации), однако требуется больше поколений (25 против 17). Это связано с усложнением ландшафта целевой функции и необходимостью большего числа итераций для достижения порога $10^{-6}$.
|
||||||
|
|
||||||
|
\item \textbf{Эффективность адаптации:} Правило успеха $1/5$ обеспечивает автоматическую подстройку масштаба мутации, что позволяет алгоритму быстро сходиться без ручной настройки начального $\sigma$. Минимальное число поколений (13 и 20 для $n=2$ и $n=3$ соответственно) достигается при больших популяциях ($\mu=40$) и высокой вероятности мутации ($p_{mut}=1.0$).
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
\newpage
|
\newpage
|
||||||
\section{Ответ на контрольный вопрос}
|
\section{Ответ на контрольный вопрос}
|
||||||
|
|||||||
Reference in New Issue
Block a user