Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3436f94b61 | |||
| f79a6abf1b | |||
| ee79d6ad41 | |||
| 745cfea282 | |||
| b7f2234bff | |||
| 1e9e52341a | |||
| 7c54d08b71 | |||
| c15867f027 | |||
| 12276dc54c | |||
| 42387c70cb | |||
| 27f4a8f2d8 | |||
| 9c2e13e90b |
@@ -448,6 +448,7 @@
|
||||
\begin{table}[h!]
|
||||
\centering
|
||||
\small
|
||||
\caption{Результаты для $N = 10$}
|
||||
\begin{tabularx}{\linewidth}{l *{5}{Y}}
|
||||
\toprule
|
||||
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
||||
@@ -460,13 +461,13 @@
|
||||
\textbf{0.8} & 8.7 (126) & 8.3 (119) & 3.9 (57) & 7.9 (113)& 4.4 (61) \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
\caption{Результаты для $N = 10$}
|
||||
\label{tab:pc_pm_results}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[h!]
|
||||
\centering
|
||||
\small
|
||||
\caption{Результаты для $N = 25$}
|
||||
\begin{tabularx}{\linewidth}{l *{5}{Y}}
|
||||
\toprule
|
||||
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
||||
@@ -479,13 +480,13 @@
|
||||
\textbf{0.8} & 9.2 (64) & 5.8 (41) & 2.5 (18) & 3.0 (22) & 11.2 (78) \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
\caption{Результаты для $N = 25$}
|
||||
\label{tab:pc_pm_results2}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[h!]
|
||||
\centering
|
||||
\small
|
||||
\caption{Результаты для $N = 50$}
|
||||
\begin{tabularx}{\linewidth}{l *{5}{Y}}
|
||||
\toprule
|
||||
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
||||
@@ -498,12 +499,12 @@
|
||||
\textbf{0.8} & 10.8 (44) & 5.0 (21) & 6.1 (24) & 13.9 (56) & 36.5 (145) \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
\caption{Результаты для $N = 50$}
|
||||
\label{tab:pc_pm_results3}
|
||||
\end{table}
|
||||
\begin{table}[h!]
|
||||
\centering
|
||||
\small
|
||||
\caption{Результаты для $N = 100$}
|
||||
\begin{tabularx}{\linewidth}{l *{5}{Y}}
|
||||
\toprule
|
||||
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
||||
@@ -516,7 +517,6 @@
|
||||
\textbf{0.8} & 16.4 (34) & 12.1 (25) & 31.4 (64) & 54.9 (114) & -- \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
\caption{Результаты для $N = 100$}
|
||||
\label{tab:pc_pm_results4}
|
||||
\end{table}
|
||||
|
||||
|
||||
339
lab2/csv_to_tex.py
Normal file
@@ -0,0 +1,339 @@
|
||||
"""
|
||||
Скрипт для конвертации результатов экспериментов из CSV в LaTeX таблицы.
|
||||
|
||||
Этот скрипт автоматически сканирует папку experiments/, находит все подпапки
|
||||
с файлами results.csv, парсит данные экспериментов и генерирует LaTeX код
|
||||
таблиц в формате, готовом для вставки в отчёт.
|
||||
|
||||
Структура входных данных:
|
||||
- experiments/N/results.csv, где N - размер популяции
|
||||
- CSV содержит результаты экспериментов с различными параметрами Pc и Pm
|
||||
- Значения в формате "X.Y (Z)" где X.Y - время выполнения, Z - количество итераций
|
||||
- "—" для отсутствующих данных
|
||||
|
||||
Выходной файл: tables.tex с готовым LaTeX кодом всех таблиц.
|
||||
Лучшие результаты по времени и фитнесу выделяются жирным (и цветом, если задан HIGHLIGHT_COLOR).
|
||||
"""
|
||||
|
||||
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) >= 6: # Pc + 5 значений Pm
|
||||
data_rows.append(parts)
|
||||
|
||||
return header, data_rows
|
||||
|
||||
|
||||
def extract_time_value(value: str) -> float | None:
|
||||
"""
|
||||
Извлекает значение времени из строки формата "X.Y (Z)" или "X.Y (Z) W.V".
|
||||
|
||||
Args:
|
||||
value: Строка с результатом
|
||||
|
||||
Returns:
|
||||
Время выполнения как float или None если значение пустое
|
||||
"""
|
||||
value = value.strip()
|
||||
if value == "—" or value == "" or value == "–":
|
||||
return None
|
||||
|
||||
# Ищем паттерн "число.число (число)"
|
||||
match = re.match(r"(\d+\.?\d*)\s*\(", value)
|
||||
if match:
|
||||
return float(match.group(1))
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def extract_fitness_value(value: str) -> float | None:
|
||||
"""
|
||||
Извлекает значение фитнеса из строки формата "X.Y (Z) W.V".
|
||||
|
||||
Args:
|
||||
value: Строка с результатом
|
||||
|
||||
Returns:
|
||||
Значение фитнеса как float или None если значение пустое
|
||||
"""
|
||||
value = value.strip()
|
||||
if value == "—" or value == "" or value == "–":
|
||||
return None
|
||||
|
||||
# Ищем паттерн "число.число (число) число.число"
|
||||
# Фитнес - это последнее число в строке
|
||||
match = re.search(r"\)\s+(\d+\.?\d*)\s*$", 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, min(6, len(row))): # Пропускаем первую колонку (Pc)
|
||||
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_fitness(data_rows: list[list[str]]) -> float | None:
|
||||
"""
|
||||
Находит минимальное значение фитнеса среди всех значений в таблице.
|
||||
|
||||
Args:
|
||||
data_rows: Строки данных таблицы
|
||||
|
||||
Returns:
|
||||
Минимальное значение фитнеса или None если нет валидных значений
|
||||
"""
|
||||
min_fitness = None
|
||||
|
||||
for row in data_rows:
|
||||
for i in range(1, min(6, len(row))): # Пропускаем первую колонку (Pc)
|
||||
fitness_value = extract_fitness_value(row[i])
|
||||
if fitness_value is not None:
|
||||
if min_fitness is None or fitness_value < min_fitness:
|
||||
min_fitness = fitness_value
|
||||
|
||||
return min_fitness
|
||||
|
||||
|
||||
def format_value(
|
||||
value: str, best_time: float | None = None, best_fitness: float | None = None
|
||||
) -> str:
|
||||
"""
|
||||
Форматирует значение для LaTeX таблицы, выделяя лучшие результаты жирным.
|
||||
|
||||
Args:
|
||||
value: Строковое значение из CSV
|
||||
best_time: Лучшее время в таблице для сравнения
|
||||
best_fitness: Лучший фитнес в таблице для сравнения
|
||||
|
||||
Returns:
|
||||
Отформатированное значение для LaTeX
|
||||
"""
|
||||
value = value.strip()
|
||||
if value == "—" or value == "" or value == "–":
|
||||
return "—"
|
||||
|
||||
# Проверяем есть ли фитнес в строке
|
||||
fitness_match = re.search(r"(\d+\.?\d*)\s*\((\d+)\)\s+(\d+\.?\d*)\s*$", value)
|
||||
|
||||
if fitness_match:
|
||||
# Есть фитнес: "время (поколения) фитнес"
|
||||
time_str = fitness_match.group(1)
|
||||
generations_str = fitness_match.group(2)
|
||||
fitness_str = fitness_match.group(3)
|
||||
|
||||
current_time = float(time_str)
|
||||
current_fitness = float(fitness_str)
|
||||
|
||||
# Проверяем, является ли время лучшим
|
||||
time_part = f"{time_str} ({generations_str})"
|
||||
if best_time is not None and abs(current_time - best_time) < 0.001:
|
||||
if HIGHLIGHT_COLOR is not None:
|
||||
time_part = (
|
||||
f"\\textcolor{{{HIGHLIGHT_COLOR}}}{{\\textbf{{{time_part}}}}}"
|
||||
)
|
||||
else:
|
||||
time_part = f"\\textbf{{{time_part}}}"
|
||||
|
||||
# Проверяем, является ли фитнес лучшим
|
||||
fitness_part = fitness_str
|
||||
if best_fitness is not None and abs(current_fitness - best_fitness) < 0.00001:
|
||||
if HIGHLIGHT_COLOR is not None:
|
||||
fitness_part = (
|
||||
f"\\textcolor{{{HIGHLIGHT_COLOR}}}{{\\textbf{{{fitness_part}}}}}"
|
||||
)
|
||||
else:
|
||||
fitness_part = f"\\textbf{{{fitness_part}}}"
|
||||
|
||||
return f"{time_part} {fitness_part}"
|
||||
|
||||
else:
|
||||
# Нет фитнеса: только "время (поколения)"
|
||||
time_match = re.match(r"(\d+\.?\d*)\s*\((\d+)\)", value)
|
||||
if time_match:
|
||||
current_time = float(time_match.group(1))
|
||||
if best_time is not None and abs(current_time - best_time) < 0.001:
|
||||
if HIGHLIGHT_COLOR is not None:
|
||||
return f"\\textcolor{{{HIGHLIGHT_COLOR}}}{{\\textbf{{{value}}}}}"
|
||||
else:
|
||||
return f"\\textbf{{{value}}}"
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def generate_latex_table(n: str, header: str, data_rows: list[list[str]]) -> str:
|
||||
"""
|
||||
Генерирует LaTeX код таблицы.
|
||||
|
||||
Args:
|
||||
n: Размер популяции
|
||||
header: Заголовок таблицы
|
||||
data_rows: Строки данных
|
||||
|
||||
Returns:
|
||||
LaTeX код таблицы
|
||||
"""
|
||||
# Находим лучшее время и лучший фитнес в таблице
|
||||
best_time = find_best_time(data_rows)
|
||||
best_fitness = find_best_fitness(data_rows)
|
||||
|
||||
# Извлекаем заголовки колонок из header
|
||||
header_parts = header.split(",")
|
||||
pm_values = header_parts[1:] # Пропускаем "Pc \ Pm"
|
||||
|
||||
latex_code = f""" \\begin{{table}}[h!]
|
||||
\\centering
|
||||
\\small
|
||||
\\caption{{Результаты для $N = {n}$}}
|
||||
\\begin{{tabularx}}{{\\linewidth}}{{l *{{5}}{{Y}}}}
|
||||
\\toprule
|
||||
$\\mathbf{{P_c \\;\\backslash\\; P_m}}$"""
|
||||
|
||||
# Добавляем заголовки Pm
|
||||
for pm in pm_values:
|
||||
latex_code += f" & \\textbf{{{pm.strip()}}}"
|
||||
|
||||
latex_code += " \\\\\n \\midrule\n"
|
||||
|
||||
# Добавляем строки данных
|
||||
for row in data_rows:
|
||||
pc_value = row[0].strip()
|
||||
latex_code += f" \\textbf{{{pc_value}}}"
|
||||
|
||||
# Добавляем значения для каждого Pm
|
||||
for i in range(1, min(6, len(row))): # Максимум 5 колонок Pm
|
||||
value = format_value(row[i], best_time, best_fitness)
|
||||
latex_code += f" & {value}"
|
||||
|
||||
# Заполняем недостающие колонки если их меньше 5
|
||||
for i in range(len(row) - 1, 5):
|
||||
latex_code += " & —"
|
||||
|
||||
latex_code += " \\\\\n"
|
||||
|
||||
latex_code += f""" \\bottomrule
|
||||
\\end{{tabularx}}
|
||||
\\label{{tab:pc_pm_results_{n}}}
|
||||
\\end{{table}}"""
|
||||
|
||||
return latex_code
|
||||
|
||||
|
||||
def main():
|
||||
"""Основная функция скрипта."""
|
||||
experiments_path = Path("experiments")
|
||||
|
||||
if not experiments_path.exists():
|
||||
print("Папка experiments не найдена!")
|
||||
return
|
||||
|
||||
tables = []
|
||||
|
||||
# Сканируем все подпапки в experiments, сортируем по числовому значению N
|
||||
subdirs = [
|
||||
subdir
|
||||
for subdir in experiments_path.iterdir()
|
||||
if subdir.is_dir() and subdir.name.isdigit()
|
||||
]
|
||||
subdirs.sort(key=lambda x: int(x.name))
|
||||
|
||||
for subdir in subdirs:
|
||||
n = subdir.name
|
||||
csv_file = subdir / "results.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_fitness = find_best_fitness(data_rows)
|
||||
latex_table = generate_latex_table(n, header, data_rows)
|
||||
tables.append(latex_table)
|
||||
print(
|
||||
f"✓ Таблица для N={n} готова (лучшее время: {best_time}, лучший фитнес: {best_fitness})"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ Ошибка при обработке {csv_file}: {e}")
|
||||
else:
|
||||
print(f"✗ Файл {csv_file} не найден")
|
||||
|
||||
# Сохраняем все таблицы в файл
|
||||
if tables:
|
||||
with open("tables.tex", "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✓ Все таблицы сохранены в файл 'tables.tex'")
|
||||
print(f"Сгенерировано таблиц: {len(tables)}")
|
||||
else:
|
||||
print("Не найдено данных для генерации таблиц!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
179
lab2/expirements.py
Normal file
@@ -0,0 +1,179 @@
|
||||
import math
|
||||
import os
|
||||
import shutil
|
||||
import statistics
|
||||
|
||||
import numpy as np
|
||||
from gen import GARunConfig, genetic_algorithm
|
||||
from prettytable import PrettyTable
|
||||
|
||||
|
||||
def fitness_function(chromosome: np.ndarray) -> np.ndarray:
|
||||
return chromosome[0] ** 2 + 2 * chromosome[1] ** 2
|
||||
|
||||
|
||||
# Базовая папка для экспериментов
|
||||
BASE_DIR = "experiments"
|
||||
|
||||
# Параметры для экспериментов
|
||||
POPULATION_SIZES = [10, 25, 50, 100]
|
||||
PC_VALUES = [0.3, 0.4, 0.5, 0.6, 0.7, 0.8] # вероятности кроссинговера
|
||||
PM_VALUES = [0.001, 0.01, 0.05, 0.1, 0.2] # вероятности мутации
|
||||
SAVE_AVG_BEST_FITNESS = True
|
||||
|
||||
# Количество запусков для усреднения результатов
|
||||
NUM_RUNS = 1
|
||||
|
||||
# Базовые параметры (как в main.py)
|
||||
BASE_CONFIG = {
|
||||
"x_min": np.array([-5.12, -5.12]),
|
||||
"x_max": np.array([5.12, 5.12]),
|
||||
"fitness_func": fitness_function,
|
||||
"max_generations": 200,
|
||||
"seed": None, # None для случайности, т. к. всё усредняем
|
||||
"minimize": True,
|
||||
# "fitness_avg_threshold": 0.05, # критерий остановки
|
||||
# "max_best_repetitions": 10,
|
||||
"best_value_threshold": 0.005,
|
||||
# при включенном сохранении графиков на время смотреть бессмысленно
|
||||
# "save_generations": [1, 50, 199],
|
||||
}
|
||||
|
||||
|
||||
def run_single_experiment(
|
||||
pop_size: int, pc: float, pm: float
|
||||
) -> tuple[float, float, float, float, float, float]:
|
||||
"""
|
||||
Запускает несколько экспериментов с заданными параметрами и усредняет результаты.
|
||||
Возвращает (среднее_время_в_мс, стд_отклонение_времени, среднее_поколений,
|
||||
стд_отклонение_поколений, среднее_лучшее_значение_фитнеса, стд_отклонение_лучшего_значения_фитнеса).
|
||||
"""
|
||||
times = []
|
||||
generations = []
|
||||
best_fitnesses = []
|
||||
|
||||
for run_num in range(NUM_RUNS):
|
||||
config = GARunConfig(
|
||||
**BASE_CONFIG,
|
||||
pop_size=pop_size,
|
||||
pc=pc,
|
||||
pm=pm,
|
||||
results_dir=os.path.join(
|
||||
BASE_DIR,
|
||||
str(pop_size),
|
||||
f"pc_{pc:.3f}",
|
||||
f"pm_{pm:.3f}",
|
||||
f"run_{run_num}",
|
||||
),
|
||||
)
|
||||
|
||||
result = genetic_algorithm(config)
|
||||
times.append(result.time_ms)
|
||||
generations.append(result.generations_count)
|
||||
best_fitnesses.append(result.best_generation.best_fitness)
|
||||
|
||||
# Вычисляем средние значения и стандартные отклонения
|
||||
avg_time = statistics.mean(times)
|
||||
std_time = statistics.stdev(times) if len(times) > 1 else 0.0
|
||||
avg_generations = statistics.mean(generations)
|
||||
std_generations = statistics.stdev(generations) if len(generations) > 1 else 0.0
|
||||
avg_best_fitness = statistics.mean(best_fitnesses)
|
||||
std_best_fitness = (
|
||||
statistics.stdev(best_fitnesses) if len(best_fitnesses) > 1 else 0.0
|
||||
)
|
||||
|
||||
return (
|
||||
avg_time,
|
||||
std_time,
|
||||
avg_generations,
|
||||
std_generations,
|
||||
avg_best_fitness,
|
||||
std_best_fitness,
|
||||
)
|
||||
|
||||
|
||||
def run_experiments_for_population(pop_size: int) -> PrettyTable:
|
||||
"""
|
||||
Запускает эксперименты для одного размера популяции.
|
||||
Возвращает таблицу результатов.
|
||||
"""
|
||||
print(f"\nЗапуск экспериментов для популяции размером {pop_size}...")
|
||||
print(f"Количество запусков для усреднения: {NUM_RUNS}")
|
||||
|
||||
# Создаем таблицу
|
||||
table = PrettyTable()
|
||||
table.field_names = ["Pc \\ Pm"] + [f"{pm:.3f}" for pm in PM_VALUES]
|
||||
|
||||
# Запускаем эксперименты для всех комбинаций Pc и Pm
|
||||
for pc in PC_VALUES:
|
||||
row = [f"{pc:.1f}"]
|
||||
for pm in PM_VALUES:
|
||||
print(f" Эксперимент: pop_size={pop_size}, Pc={pc:.1f}, Pm={pm:.3f}")
|
||||
(
|
||||
avg_time,
|
||||
std_time,
|
||||
avg_generations,
|
||||
std_generations,
|
||||
avg_best_fitness,
|
||||
std_best_fitness,
|
||||
) = run_single_experiment(pop_size, pc, pm)
|
||||
|
||||
# Форматируем результат: среднее_время±стд_отклонение (среднее_поколения±стд_отклонение)
|
||||
# cell_value = f"{avg_time:.1f}±{std_time:.1f} ({avg_generations:.1f}±{std_generations:.1f})"
|
||||
cell_value = f"{avg_time:.1f} ({avg_generations:.0f})"
|
||||
|
||||
if SAVE_AVG_BEST_FITNESS:
|
||||
cell_value += f" {avg_best_fitness:.5f}"
|
||||
|
||||
if avg_generations == BASE_CONFIG["max_generations"]:
|
||||
cell_value = "—"
|
||||
|
||||
row.append(cell_value)
|
||||
table.add_row(row)
|
||||
|
||||
return table
|
||||
|
||||
|
||||
def main():
|
||||
"""Основная функция для запуска всех экспериментов."""
|
||||
print("=" * 60)
|
||||
print("ЗАПУСК ЭКСПЕРИМЕНТОВ ПО ПАРАМЕТРАМ ГЕНЕТИЧЕСКОГО АЛГОРИТМА")
|
||||
print("=" * 60)
|
||||
print(f"Размеры популяции: {POPULATION_SIZES}")
|
||||
print(f"Значения Pc: {PC_VALUES}")
|
||||
print(f"Значения Pm: {PM_VALUES}")
|
||||
print(f"Количество запусков для усреднения: {NUM_RUNS}")
|
||||
print("=" * 60)
|
||||
|
||||
# Создаем базовую папку
|
||||
if os.path.exists(BASE_DIR):
|
||||
shutil.rmtree(BASE_DIR)
|
||||
os.makedirs(BASE_DIR)
|
||||
|
||||
# Запускаем эксперименты для каждого размера популяции
|
||||
for pop_size in POPULATION_SIZES:
|
||||
table = run_experiments_for_population(pop_size)
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f"РЕЗУЛЬТАТЫ ДЛЯ ПОПУЛЯЦИИ РАЗМЕРОМ {pop_size}")
|
||||
print(f"{'='*60}")
|
||||
print(
|
||||
f"Формат: среднее_время±стд_отклонение_мс (среднее_поколения±стд_отклонение)"
|
||||
)
|
||||
print(f"Усреднено по {NUM_RUNS} запускам")
|
||||
print(table)
|
||||
|
||||
pop_exp_dir = os.path.join(BASE_DIR, str(pop_size))
|
||||
os.makedirs(pop_exp_dir, exist_ok=True)
|
||||
with open(os.path.join(pop_exp_dir, "results.csv"), "w", encoding="utf-8") as f:
|
||||
f.write(table.get_csv_string())
|
||||
print(f"Результаты сохранены в папке: {pop_exp_dir}")
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print("ВСЕ ЭКСПЕРИМЕНТЫ ЗАВЕРШЕНЫ!")
|
||||
print(f"Результаты сохранены в {BASE_DIR}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
454
lab2/gen.py
Normal file
@@ -0,0 +1,454 @@
|
||||
import os
|
||||
import random
|
||||
import shutil
|
||||
import time
|
||||
from copy import deepcopy
|
||||
from dataclasses import dataclass
|
||||
from typing import Callable
|
||||
|
||||
import numpy as np
|
||||
import plotly.graph_objects as go
|
||||
from matplotlib import pyplot as plt
|
||||
from matplotlib.axes import Axes
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
from numpy.typing import NDArray
|
||||
|
||||
type Chromosome = NDArray[np.float64]
|
||||
type Population = list[Chromosome]
|
||||
type Fitnesses = NDArray[np.float64]
|
||||
type FitnessFn = Callable[[Chromosome], Fitnesses]
|
||||
type CrossoverFn = Callable[[Chromosome, Chromosome], tuple[Chromosome, Chromosome]]
|
||||
type MutationFn = Callable[[Chromosome], Chromosome]
|
||||
|
||||
|
||||
@dataclass
|
||||
class GARunConfig:
|
||||
x_min: Chromosome
|
||||
x_max: Chromosome
|
||||
fitness_func: FitnessFn
|
||||
pop_size: int # размер популяции
|
||||
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 = (
|
||||
None # индексы поколений для сохранения графиков
|
||||
)
|
||||
results_dir: str = "results" # папка для сохранения графиков
|
||||
fitness_avg_threshold: float | None = (
|
||||
None # порог среднего значения фитнес функции для остановки
|
||||
)
|
||||
best_value_threshold: float | None = (
|
||||
None # остановка при достижении значения фитнеса лучше заданного
|
||||
)
|
||||
log_every_generation: bool = False # логировать каждое поколение
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Generation:
|
||||
number: int
|
||||
best: Chromosome
|
||||
best_fitness: float
|
||||
population: Population
|
||||
fitnesses: Fitnesses
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GARunResult:
|
||||
generations_count: int
|
||||
best_generation: Generation
|
||||
history: list[Generation]
|
||||
time_ms: float
|
||||
|
||||
|
||||
def initialize_population(
|
||||
pop_size: int, x_min: Chromosome, x_max: Chromosome
|
||||
) -> Population:
|
||||
"""Инициализирует популяцию случайными векторами из заданного диапазона."""
|
||||
return [np.random.uniform(x_min, x_max, x_min.shape) for _ in range(pop_size)]
|
||||
|
||||
|
||||
def reproduction(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 arithmetical_crossover_fn(
|
||||
p1: Chromosome, p2: Chromosome, w: float = 0.5
|
||||
) -> tuple[Chromosome, Chromosome]:
|
||||
"""Арифметический кроссинговер."""
|
||||
h1 = w * p1 + (1 - w) * p2
|
||||
h2 = (1 - w) * p1 + w * p2
|
||||
return h1, h2
|
||||
|
||||
|
||||
def geometrical_crossover_fn(
|
||||
p1: Chromosome, p2: Chromosome, w: float = 0.5
|
||||
) -> tuple[Chromosome, Chromosome]:
|
||||
"""Геометрический кроссинговер."""
|
||||
h1 = np.power(p1, w) * np.power(p2, 1 - w)
|
||||
h2 = np.power(p2, w) * np.power(p1, 1 - w)
|
||||
return h1, h2
|
||||
|
||||
|
||||
def crossover(
|
||||
population: Population,
|
||||
pc: float,
|
||||
crossover_fn: CrossoverFn,
|
||||
) -> Population:
|
||||
"""Оператор кроссинговера (скрещивания) выполняется с заданной вероятностью pc.
|
||||
|
||||
Две хромосомы (родители) выбираются случайно из промежуточной популяции.
|
||||
|
||||
Если популяция нечетного размера, то последняя хромосома скрещивается со случайной
|
||||
другой хромосомой из популяции. В таком случае одна из хромосом может поучаствовать
|
||||
в кроссовере дважды.
|
||||
"""
|
||||
# Создаем копию популяции и перемешиваем её для случайного выбора пар
|
||||
shuffled_population = population.copy()
|
||||
np.random.shuffle(shuffled_population)
|
||||
|
||||
next_population = []
|
||||
pop_size = len(shuffled_population)
|
||||
|
||||
for i in range(0, pop_size, 2):
|
||||
p1 = shuffled_population[i]
|
||||
p2 = shuffled_population[(i + 1) % pop_size]
|
||||
if np.random.random() <= pc:
|
||||
p1, p2 = crossover_fn(p1, p2)
|
||||
next_population.append(p1)
|
||||
next_population.append(p2)
|
||||
|
||||
return next_population[:pop_size]
|
||||
|
||||
|
||||
def build_random_mutation_fn(x_min: Chromosome, x_max: Chromosome) -> MutationFn:
|
||||
"""Создаёт функцию случайной мутации."""
|
||||
|
||||
def mutation_fn(chrom: Chromosome) -> Chromosome:
|
||||
chrom_new = chrom.copy()
|
||||
k = np.random.randint(0, chrom_new.shape[0])
|
||||
chrom_new[k] = np.random.uniform(x_min[k], x_max[k])
|
||||
return chrom_new
|
||||
|
||||
return mutation_fn
|
||||
|
||||
|
||||
def mutation(population: Population, pm: float, mutation_fn: MutationFn) -> Population:
|
||||
"""Мутация происходит с вероятностью pm."""
|
||||
next_population = []
|
||||
for chrom in population:
|
||||
next_population.append(
|
||||
mutation_fn(chrom) if np.random.random() <= pm else chrom
|
||||
)
|
||||
return next_population
|
||||
|
||||
|
||||
def clear_results_directory(results_dir: str) -> None:
|
||||
"""Очищает папку с результатами перед началом эксперимента."""
|
||||
if os.path.exists(results_dir):
|
||||
shutil.rmtree(results_dir)
|
||||
os.makedirs(results_dir, exist_ok=True)
|
||||
|
||||
|
||||
def eval_population(population: Population, fitness_func: FitnessFn) -> Fitnesses:
|
||||
return np.array([fitness_func(chrom) for chrom in population])
|
||||
|
||||
|
||||
def plot_fitness_surface(
|
||||
fitness_func: FitnessFn,
|
||||
x_min: Chromosome,
|
||||
x_max: Chromosome,
|
||||
ax: Axes3D,
|
||||
num_points: int = 100,
|
||||
) -> None:
|
||||
"""Рисует поверхность функции фитнеса в 3D."""
|
||||
assert (
|
||||
x_min.shape == x_max.shape == (2,)
|
||||
), "Рисовать графики можно только для функции от двух переменных"
|
||||
X = np.linspace(x_min[0], x_max[0], num_points)
|
||||
Y = np.linspace(x_min[1], x_max[1], num_points)
|
||||
X, Y = np.meshgrid(X, Y)
|
||||
|
||||
vectorized_fitness = np.vectorize(lambda x, y: fitness_func(np.array([x, y])))
|
||||
Z = vectorized_fitness(X, Y)
|
||||
|
||||
return ax.plot_surface(
|
||||
X, Y, Z, cmap="viridis", edgecolor="none", alpha=0.7, shade=False
|
||||
)
|
||||
|
||||
|
||||
def plot_fitness_contour(
|
||||
fitness_func: FitnessFn,
|
||||
x_min: Chromosome,
|
||||
x_max: Chromosome,
|
||||
ax: Axes,
|
||||
num_points: int = 100,
|
||||
) -> None:
|
||||
"""Рисует контурный график функции фитнеса в 2D."""
|
||||
X = np.linspace(x_min[0], x_max[0], num_points)
|
||||
Y = np.linspace(x_min[1], x_max[1], num_points)
|
||||
X, Y = np.meshgrid(X, Y)
|
||||
|
||||
vectorized_fitness = np.vectorize(lambda x, y: fitness_func(np.array([x, y])))
|
||||
Z = vectorized_fitness(X, Y)
|
||||
|
||||
# Рисуем контуры
|
||||
# X и Y поменяны местами для единообразия с 3D графиками, там ось Y изображена
|
||||
# горизонтально из-за особенностей функции в моём варианте
|
||||
contourf = ax.contourf(Y, X, Z, levels=20, cmap="viridis", alpha=0.7)
|
||||
ax.set_xlabel("Y")
|
||||
ax.set_ylabel("X")
|
||||
|
||||
# Добавляем цветовую шкалу
|
||||
plt.colorbar(contourf, ax=ax, shrink=0.5)
|
||||
|
||||
# По умолчанию matplotlib пытается растянуть график по оси Y, тут мы это отключаем
|
||||
ax.set_aspect("equal")
|
||||
|
||||
|
||||
def save_generation(
|
||||
generation: Generation, history: list[Generation], config: GARunConfig
|
||||
) -> None:
|
||||
"""Сохраняем графики поколения.
|
||||
|
||||
Функция не самая универсальная, тут есть хардкод, однако для большинства вариантов
|
||||
должна работать и так.
|
||||
"""
|
||||
assert (
|
||||
config.x_min.shape == config.x_max.shape == (2,)
|
||||
), "Рисовать графики можно только для функции от двух переменных"
|
||||
os.makedirs(config.results_dir, exist_ok=True)
|
||||
|
||||
fig = plt.figure(figsize=(21, 7))
|
||||
fig.suptitle(
|
||||
f"Поколение #{generation.number}. "
|
||||
f"Лучшая особь: {generation.best_fitness:.4f}. "
|
||||
f"Среднее значение: {np.mean(generation.fitnesses):.4f}",
|
||||
fontsize=14,
|
||||
y=0.85,
|
||||
)
|
||||
|
||||
# Контурный график (как вид сверху)
|
||||
ax1 = fig.add_subplot(1, 3, 1)
|
||||
plot_fitness_contour(config.fitness_func, config.x_min, config.x_max, ax1)
|
||||
|
||||
# Популяция на контурном графике
|
||||
arr = np.array(generation.population)
|
||||
# Координаты специально поменяны местами (см. plot_fitness_contour)
|
||||
ax1.scatter(
|
||||
arr[:, 1],
|
||||
arr[:, 0],
|
||||
c="red",
|
||||
marker="o",
|
||||
alpha=0.9,
|
||||
s=20,
|
||||
)
|
||||
|
||||
# Подпись под первым графиком
|
||||
ax1.text(
|
||||
0.5,
|
||||
-0.3,
|
||||
"(a)",
|
||||
transform=ax1.transAxes,
|
||||
ha="center",
|
||||
fontsize=16,
|
||||
)
|
||||
|
||||
# 3D графики с разных ракурсов
|
||||
views_3d = [
|
||||
# (elev, azim)
|
||||
(50, 0),
|
||||
(50, 15),
|
||||
]
|
||||
|
||||
for i, (elev, azim) in enumerate(views_3d):
|
||||
ax = fig.add_subplot(1, 3, i + 2, projection="3d", computed_zorder=False)
|
||||
|
||||
plot_fitness_surface(config.fitness_func, config.x_min, config.x_max, ax)
|
||||
|
||||
ax.set_xlabel("X")
|
||||
ax.set_ylabel("Y")
|
||||
ax.set_zlabel("f(X, Y)")
|
||||
|
||||
ax.scatter(
|
||||
arr[:, 0],
|
||||
arr[:, 1],
|
||||
generation.fitnesses + 1, # type: ignore
|
||||
c="red",
|
||||
s=10,
|
||||
marker="o",
|
||||
alpha=0.9,
|
||||
)
|
||||
|
||||
# Устанавливаем угол обзора
|
||||
ax.view_init(elev=elev, azim=azim)
|
||||
|
||||
# Подпись под 3D графиками
|
||||
label = chr(ord("b") + i) # 'b' для i=0, 'c' для i=1
|
||||
ax.text2D(
|
||||
0.5,
|
||||
-0.15,
|
||||
f"({label})",
|
||||
transform=ax.transAxes,
|
||||
ha="center",
|
||||
fontsize=16,
|
||||
)
|
||||
|
||||
filename = f"generation_{generation.number:03d}.png"
|
||||
path_png = os.path.join(config.results_dir, filename)
|
||||
fig.savefig(path_png, dpi=150, bbox_inches="tight")
|
||||
# Можно раскомментировать, чтобы подобрать более удачные ракурсы
|
||||
# в интерактивном режиме
|
||||
# fig.show()
|
||||
# plt.pause(1000)
|
||||
plt.close(fig)
|
||||
|
||||
|
||||
def genetic_algorithm(config: GARunConfig) -> GARunResult:
|
||||
if config.seed is not None:
|
||||
random.seed(config.seed)
|
||||
np.random.seed(config.seed)
|
||||
|
||||
if config.save_generations:
|
||||
clear_results_directory(config.results_dir)
|
||||
|
||||
population = initialize_population(config.pop_size, config.x_min, config.x_max)
|
||||
|
||||
start = time.perf_counter()
|
||||
history: list[Generation] = []
|
||||
best: Generation | None = None
|
||||
|
||||
generation_number = 1
|
||||
best_repetitions = 0
|
||||
|
||||
while True:
|
||||
# Вычисляем фитнес для всех особей в популяции
|
||||
fitnesses = eval_population(population, config.fitness_func)
|
||||
|
||||
# Находим лучшую особь в поколении
|
||||
best_index = (
|
||||
int(np.argmin(fitnesses)) if config.minimize else int(np.argmax(fitnesses))
|
||||
)
|
||||
|
||||
# Добавляем эпоху в историю
|
||||
current = Generation(
|
||||
number=generation_number,
|
||||
best=population[best_index],
|
||||
best_fitness=fitnesses[best_index],
|
||||
population=deepcopy(population),
|
||||
fitnesses=deepcopy(fitnesses),
|
||||
)
|
||||
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
|
||||
or (config.minimize and current.best_fitness < best.best_fitness)
|
||||
or (not config.minimize and current.best_fitness > best.best_fitness)
|
||||
):
|
||||
best = current
|
||||
|
||||
# Проверка критериев остановки
|
||||
stop_algorithm = False
|
||||
|
||||
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:
|
||||
# stop_algorithm = True
|
||||
|
||||
if config.best_value_threshold is not None:
|
||||
if (
|
||||
config.minimize and current.best_fitness < config.best_value_threshold
|
||||
) or (
|
||||
not config.minimize
|
||||
and current.best_fitness > config.best_value_threshold
|
||||
):
|
||||
stop_algorithm = True
|
||||
|
||||
if config.fitness_avg_threshold is not None:
|
||||
mean_fitness = np.mean(fitnesses)
|
||||
if (config.minimize and mean_fitness < config.fitness_avg_threshold) or (
|
||||
not config.minimize and mean_fitness > config.fitness_avg_threshold
|
||||
):
|
||||
stop_algorithm = True
|
||||
|
||||
# Сохраняем указанные поколения и последнее поколение
|
||||
if config.save_generations and (
|
||||
stop_algorithm or generation_number in config.save_generations
|
||||
):
|
||||
# save_generation(current, history, config)
|
||||
save_generation(current, history, config)
|
||||
|
||||
if stop_algorithm:
|
||||
break
|
||||
|
||||
# селекция (для минимума инвертируем знак)
|
||||
parents = reproduction(
|
||||
population, fitnesses if not config.minimize else -fitnesses
|
||||
)
|
||||
|
||||
# кроссинговер попарно
|
||||
next_population = crossover(parents, config.pc, arithmetical_crossover_fn)
|
||||
|
||||
# мутация
|
||||
next_population = mutation(
|
||||
next_population,
|
||||
config.pm,
|
||||
build_random_mutation_fn(config.x_min, config.x_max),
|
||||
)
|
||||
|
||||
population = next_population[: config.pop_size]
|
||||
generation_number += 1
|
||||
|
||||
end = time.perf_counter()
|
||||
|
||||
assert best is not None, "Best was never set"
|
||||
return GARunResult(
|
||||
len(history),
|
||||
best,
|
||||
history,
|
||||
(end - start) * 1000.0,
|
||||
)
|
||||
31
lab2/main.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from gen import GARunConfig, genetic_algorithm
|
||||
|
||||
|
||||
def fitness_function(chromosome: np.ndarray) -> np.ndarray:
|
||||
return chromosome[0] ** 2 + 2 * chromosome[1] ** 2
|
||||
|
||||
|
||||
config = GARunConfig(
|
||||
x_min=np.array([-5.12, -5.12]),
|
||||
x_max=np.array([5.12, 5.12]),
|
||||
fitness_func=fitness_function,
|
||||
pop_size=25,
|
||||
pc=0.5,
|
||||
pm=0.01,
|
||||
max_generations=200,
|
||||
max_best_repetitions=10,
|
||||
minimize=True,
|
||||
seed=17,
|
||||
save_generations=[1, 2, 3, 5, 7, 9, 10, 15, 19],
|
||||
log_every_generation=True,
|
||||
)
|
||||
|
||||
result = genetic_algorithm(config)
|
||||
|
||||
# Выводим результаты
|
||||
print(f"Лучшая особь: {result.best_generation.best}")
|
||||
print(f"Лучшее значение фитнеса: {result.best_generation.best_fitness:.6f}")
|
||||
print(f"Количество поколений: {result.generations_count}")
|
||||
print(f"Время выполнения: {result.time_ms:.2f} мс")
|
||||
6
lab2/report/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
*
|
||||
|
||||
!**/
|
||||
!.gitignore
|
||||
!report.tex
|
||||
!img/**/*.png
|
||||
BIN
lab2/report/img/alg.png
Normal file
|
After Width: | Height: | Size: 172 KiB |
BIN
lab2/report/img/arithmetic_crossover.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
lab2/report/img/blx_crossover.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
lab2/report/img/geometric_crossover.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
lab2/report/img/results/generation_001.png
Normal file
|
After Width: | Height: | Size: 560 KiB |
BIN
lab2/report/img/results/generation_002.png
Normal file
|
After Width: | Height: | Size: 554 KiB |
BIN
lab2/report/img/results/generation_003.png
Normal file
|
After Width: | Height: | Size: 552 KiB |
BIN
lab2/report/img/results/generation_005.png
Normal file
|
After Width: | Height: | Size: 550 KiB |
BIN
lab2/report/img/results/generation_007.png
Normal file
|
After Width: | Height: | Size: 550 KiB |
BIN
lab2/report/img/results/generation_009.png
Normal file
|
After Width: | Height: | Size: 545 KiB |
BIN
lab2/report/img/results/generation_010.png
Normal file
|
After Width: | Height: | Size: 544 KiB |
BIN
lab2/report/img/results/generation_015.png
Normal file
|
After Width: | Height: | Size: 542 KiB |
BIN
lab2/report/img/results/generation_019.png
Normal file
|
After Width: | Height: | Size: 542 KiB |
BIN
lab2/report/img/sbx_crossover.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
747
lab2/report/report.tex
Normal file
@@ -0,0 +1,747 @@
|
||||
\documentclass[a4paper, final]{article}
|
||||
%\usepackage{literat} % Нормальные шрифты
|
||||
\usepackage[14pt]{extsizes} % для того чтобы задать нестандартный 14-ый размер шрифта
|
||||
\usepackage{tabularx}
|
||||
\usepackage{booktabs}
|
||||
\usepackage[T2A]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[russian]{babel}
|
||||
\usepackage{amsmath}
|
||||
\usepackage[left=25mm, top=20mm, right=20mm, bottom=20mm, footskip=10mm]{geometry}
|
||||
\usepackage{ragged2e} %для растягивания по ширине
|
||||
\usepackage{setspace} %для межстрочно го интервала
|
||||
\usepackage{moreverb} %для работы с листингами
|
||||
\usepackage{indentfirst} % для абзацного отступа
|
||||
\usepackage{moreverb} %для печати в листинге исходного кода программ
|
||||
\usepackage{pdfpages} %для вставки других pdf файлов
|
||||
\usepackage{tikz}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{afterpage}
|
||||
\usepackage{longtable}
|
||||
\usepackage{float}
|
||||
\usepackage{xcolor}
|
||||
|
||||
|
||||
|
||||
|
||||
% \usepackage[paper=A4,DIV=12]{typearea}
|
||||
\usepackage{pdflscape}
|
||||
% \usepackage{lscape}
|
||||
|
||||
\usepackage{array}
|
||||
\usepackage{multirow}
|
||||
|
||||
\renewcommand\verbatimtabsize{4\relax}
|
||||
\renewcommand\listingoffset{0.2em} %отступ от номеров строк в листинге
|
||||
\renewcommand{\arraystretch}{1.4} % изменяю высоту строки в таблице
|
||||
\usepackage[font=small, singlelinecheck=false, justification=centering, format=plain, labelsep=period]{caption} %для настройки заголовка таблицы
|
||||
\usepackage{listings} %листинги
|
||||
\usepackage{xcolor} % цвета
|
||||
\usepackage{hyperref}% для гиперссылок
|
||||
\usepackage{enumitem} %для перечислений
|
||||
|
||||
\newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}}
|
||||
|
||||
|
||||
\setlist[enumerate,itemize]{leftmargin=1.2cm} %отступ в перечислениях
|
||||
|
||||
\hypersetup{colorlinks,
|
||||
allcolors=[RGB]{010 090 200}} %красивые гиперссылки (не красные)
|
||||
|
||||
% подгружаемые языки — подробнее в документации listings (это всё для листингов)
|
||||
\lstloadlanguages{ SQL}
|
||||
% включаем кириллицу и добавляем кое−какие опции
|
||||
\lstset{tabsize=2,
|
||||
breaklines,
|
||||
basicstyle=\footnotesize,
|
||||
columns=fullflexible,
|
||||
flexiblecolumns,
|
||||
numbers=left,
|
||||
numberstyle={\footnotesize},
|
||||
keywordstyle=\color{blue},
|
||||
inputencoding=cp1251,
|
||||
extendedchars=true
|
||||
}
|
||||
\lstdefinelanguage{MyC}{
|
||||
language=SQL,
|
||||
% ndkeywordstyle=\color{darkgray}\bfseries,
|
||||
% identifierstyle=\color{black},
|
||||
% morecomment=[n]{/**}{*/},
|
||||
% commentstyle=\color{blue}\ttfamily,
|
||||
% stringstyle=\color{red}\ttfamily,
|
||||
% morestring=[b]",
|
||||
% showstringspaces=false,
|
||||
% morecomment=[l][\color{gray}]{//},
|
||||
keepspaces=true,
|
||||
escapechar=\%,
|
||||
texcl=true
|
||||
}
|
||||
|
||||
\textheight=24cm % высота текста
|
||||
\textwidth=16cm % ширина текста
|
||||
\oddsidemargin=0pt % отступ от левого края
|
||||
\topmargin=-1.5cm % отступ от верхнего края
|
||||
\parindent=24pt % абзацный отступ
|
||||
\parskip=5pt % интервал между абзацами
|
||||
\tolerance=2000 % терпимость к "жидким" строкам
|
||||
\flushbottom % выравнивание высоты страниц
|
||||
|
||||
|
||||
% Настройка листингов
|
||||
\lstset{
|
||||
language=python,
|
||||
extendedchars=\true,
|
||||
inputencoding=utf8,
|
||||
keepspaces=true,
|
||||
% captionpos=b, % подписи листингов снизу
|
||||
}
|
||||
|
||||
\begin{document} % начало документа
|
||||
|
||||
|
||||
|
||||
% НАЧАЛО ТИТУЛЬНОГО ЛИСТА
|
||||
\begin{center}
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\
|
||||
федеральное государственное автономное образовательное учреждение высшего образования «Санкт-Петербургский политехнический университет Петра Великого»\\[10pt]}
|
||||
\normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt]
|
||||
\normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt]
|
||||
\normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\
|
||||
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\large{Лабораторная работа №2}\\
|
||||
\large{по дисциплине}\\
|
||||
\large{<<Генетические алгоритмы>>}\\
|
||||
\large{Вариант 18}\\
|
||||
|
||||
% \hfill \break
|
||||
\hfill \break
|
||||
\end{center}
|
||||
|
||||
\small{
|
||||
\begin{tabular}{lrrl}
|
||||
\!\!\!Студент, & \hspace{2cm} & & \\
|
||||
\!\!\!группы 5130201/20101 & \hspace{2cm} & \underline{\hspace{3cm}} &Тищенко А. А. \\\\
|
||||
\!\!\!Преподаватель & \hspace{2cm} & \underline{\hspace{3cm}} & Большаков А. А. \\\\
|
||||
&&\hspace{4cm}
|
||||
\end{tabular}
|
||||
\begin{flushright}
|
||||
<<\underline{\hspace{1cm}}>>\underline{\hspace{2.5cm}} 2025г.
|
||||
\end{flushright}
|
||||
}
|
||||
|
||||
\hfill \break
|
||||
% \hfill \break
|
||||
\begin{center} \small{Санкт-Петербург, 2025} \end{center}
|
||||
\thispagestyle{empty} % выключаем отображение номера для этой страницы
|
||||
|
||||
% КОНЕЦ ТИТУЛЬНОГО ЛИСТА
|
||||
\newpage
|
||||
|
||||
\tableofcontents
|
||||
|
||||
\newpage
|
||||
\section {Постановка задачи}
|
||||
В данной работе были поставлены следующие задачи:
|
||||
|
||||
\begin{itemize}
|
||||
\item Изучить теоретический материал;
|
||||
\item Ознакомиться с вариантами кодирования хромосомы;
|
||||
\item Рассмотреть способы выполнения операторов репродукции,
|
||||
кроссинговера и мутации;
|
||||
\item Выполнить индивидуальное задание на любом языке высокого
|
||||
уровня
|
||||
\end{itemize}
|
||||
|
||||
\textbf{Индивидуальное задание вариант 18:}
|
||||
|
||||
\textbf{Дано:} Функция Axis parallel hyper-ellipsoid function.
|
||||
|
||||
Общая формула для n-мерного случая:
|
||||
$$f(\mathbf{x}) = \sum_{i=1}^{n} i \cdot x_i^2$$
|
||||
где $\mathbf{x} = (x_1, x_2, \ldots, x_n)$, область определения $x_i \in [-5.12, 5.12]$ для всех $i = 1, \ldots, n$.
|
||||
|
||||
Для двумерного случая (n=2):
|
||||
$$f(x, y) = 1 \cdot x^2 + 2 \cdot y^2 = x^2 + 2y^2$$
|
||||
область нахождения решения $x \in [-5.12, 5.12], y \in [-5.12, 5.12]$.
|
||||
|
||||
Глобальный минимум: $f(\mathbf{x}) = 0$ в точке $x_i = 0$ для всех $i = 1, \ldots, n$. Для двумерного случая: $\min f(x, y) = f(0, 0) = 0$.
|
||||
|
||||
\vspace{0.3cm}
|
||||
\textbf{Требуется:}
|
||||
|
||||
\begin{enumerate}
|
||||
\item Создать программу, использующую генетический алгоритм для нахождения минимума данной функции;
|
||||
\item Для n=2 вывести на экран график функции с указанием найденного экстремума и точек популяции. Предусмотреть возможность пошагового просмотра процесса поиска решения;
|
||||
\item Исследовать зависимость времени поиска, числа поколений (генераций), точности нахождения решения от основных параметров генетического алгоритма: числа особей в популяции, вероятности кроссинговера и мутации;
|
||||
\item Повторить процесс поиска решения для n=3, сравнить результаты и скорость работы программы.
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
\newpage
|
||||
\section{Теоретические сведения}
|
||||
|
||||
Генетические алгоритмы (ГА) используют принципы и терминологию, заимствованные у биологической науки – генетики. В ГА каждая особь представляет потенциальное решение некоторой
|
||||
проблемы. В классическом ГА особь кодируется строкой двоичных символов – хромосомой. Однако при работе с оптимизационными задачами в непрерывных пространствах вполне естественно представлять гены напрямую вещественными числами. В этом случае хромосома есть вектор вещественных чисел (real-coded алгоритмы). Их точность определяется исключительно разрядной сеткой ЭВМ. Длина хромосомы совпадает с длиной вектора-решения оптимизационной задачи, каждый ген отвечает за одну переменную. Генотип объекта становится идентичным его фенотипу.
|
||||
|
||||
Множество особей – потенциальных решений составляет популяцию. Поиск (суб)оптимального решения проблемы выполняется в процессе эволюции популяции - последовательного преобразования одного конечного множества решений в другое с помощью генетических операторов репродукции, кроссинговера и мутации.
|
||||
|
||||
Предварительно простой ГА случайным образом генерирует начальную популяцию стрингов
|
||||
(хромосом). Затем алгоритм генерирует следующее поколение (популяцию), с помощью трех основных генетических операторов:
|
||||
|
||||
\begin{enumerate}
|
||||
\item Оператор репродукции (ОР);
|
||||
\item Оператор скрещивания (кроссинговера, ОК);
|
||||
\item Оператор мутации (ОМ).
|
||||
\end{enumerate}
|
||||
|
||||
ГА работает до тех пор, пока не будет выполнено заданное количество поколений (итераций)
|
||||
процесса эволюции или на некоторой генерации будет получено заданное качество или вследствие
|
||||
преждевременной сходимости при попадании в некоторый локальный оптимум. На Рис.~\ref{fig:alg} представлен простой генетический алгоритм.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.9\linewidth]{img/alg.png}
|
||||
\caption{Простой генетический алгоритм}
|
||||
\label{fig:alg}
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
\subsection{Основная терминология в генетических алгоритмах}
|
||||
|
||||
\textbf{Ген} -- элементарный код в хромосоме $s_i$, называемый также знаком или детектором
|
||||
(в классическом ГА $s_i = 0, 1$).
|
||||
|
||||
\textbf{Хромосома} -- упорядоченная последовательность генов в виде закодированной структуры
|
||||
данных $S = (s_1, s_2, \ldots, s_n)$, определяющая решение. Может быть представлена как двоичная
|
||||
последовательность (где $s_i = 0, 1$) или как вектор вещественных чисел (real-coded представление).
|
||||
|
||||
\textbf{Локус} -- местоположение (позиция, номер бита) данного гена в хромосоме.
|
||||
|
||||
\textbf{Аллель} -- значение, которое принимает данный ген (например, 0 или 1).
|
||||
|
||||
\textbf{Особь} -- одно потенциальное решение задачи (представляемое хромосомой).
|
||||
|
||||
\textbf{Популяция} -- множество особей (хромосом), представляющих потенциальные решения.
|
||||
|
||||
\textbf{Поколение} -- текущая популяция ГА на данной итерации алгоритма.
|
||||
|
||||
\textbf{Генотип} -- набор хромосом данной особи. В популяции могут использоваться как отдельные
|
||||
хромосомы, так и целые генотипы.
|
||||
|
||||
\textbf{Генофонд} -- множество всех возможных генотипов.
|
||||
|
||||
\textbf{Фенотип} -- набор значений, соответствующий данному генотипу. Это декодированное множество
|
||||
параметров задачи (например, десятичное значение $x$, соответствующее двоичному коду).
|
||||
|
||||
\textbf{Размер популяции $N$} -- число особей в популяции.
|
||||
|
||||
\textbf{Число поколений} -- количество итераций, в течение которых производится поиск.
|
||||
|
||||
\textbf{Селекция} -- совокупность правил, определяющих выживание особей на основе значений целевой функции.
|
||||
|
||||
\textbf{Эволюция популяции} -- чередование поколений, в которых хромосомы изменяют свои признаки,
|
||||
чтобы каждая новая популяция лучше приспосабливалась к среде.
|
||||
|
||||
\textbf{Фитнесс-функция} -- функция полезности, определяющая меру приспособленности особи.
|
||||
В задачах оптимизации она совпадает с целевой функцией или описывает близость к оптимальному решению.
|
||||
|
||||
\subsection{Генетические операторы}
|
||||
|
||||
\subsubsection{Оператор репродукции}
|
||||
|
||||
Репродукция -- процесс копирования хромосом в промежуточную популяцию для дальнейшего
|
||||
``размножения'' в соответствии со значениями фитнесс-функции. В данной работе рассматривается метод колеса рулетки. Каждой хромосоме соответствует сектор, пропорциональный значению фитнесс-функции.
|
||||
Хромосомы с большим значением имеют больше шансов попасть в следующее поколение.
|
||||
|
||||
\subsubsection{Операторы кроссинговера для real-coded алгоритмов}
|
||||
|
||||
Оператор скрещивания непрерывного ГА (кроссовер) порождает одного или нескольких потомков от двух хромосом. Требуется из двух векторов вещественных чисел получить новые векторы по определённым законам. Большинство real-coded алгоритмов генерируют новые векторы в окрестности родительских пар.
|
||||
|
||||
Пусть $C_1=(c_{11},c_{21},\ldots,c_{n1})$ и $C_2=(c_{12},c_{22},\ldots,c_{n2})$ -- две хромосомы, выбранные оператором селекции для скрещивания.
|
||||
|
||||
\textbf{Арифметический кроссовер (arithmetical crossover):} создаются два потомка $H_1=(h_{11},\ldots,h_{n1})$, $H_2=(h_{12},\ldots,h_{n2})$, где:
|
||||
$$h_{k1}=w \cdot c_{k1}+(1-w) \cdot c_{k2}$$
|
||||
$$h_{k2}=w \cdot c_{k2}+(1-w) \cdot c_{k1}$$
|
||||
где $k=1,\ldots,n$, $w$ -- весовой коэффициент из интервала $[0;1]$.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.5\linewidth]{img/arithmetic_crossover.png}
|
||||
\caption{Арифметический кроссовер}
|
||||
\label{fig:arithmetic_crossover}
|
||||
\end{figure}
|
||||
|
||||
\textbf{Геометрический кроссовер (geometrical crossover):} создаются два потомка $H_1=(h_{11},\ldots,h_{n1})$, $H_2=(h_{12},\ldots,h_{n2})$, где:
|
||||
$$h_{k1}=(c_{k1})^w \cdot (c_{k2})^{(1-w)}$$
|
||||
$$h_{k2}=(c_{k2})^w \cdot (c_{k1})^{(1-w)}$$
|
||||
где $w$ -- случайное число из интервала $[0;1]$.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.5\linewidth]{img/geometric_crossover.png}
|
||||
\caption{Геометрический кроссовер}
|
||||
\label{fig:geometric_crossover}
|
||||
\end{figure}
|
||||
|
||||
\textbf{Смешанный кроссовер (BLX-alpha crossover):} генерируется один потомок $H=(h_1,\ldots,h_k,\ldots,h_n)$, где $h_k$ -- случайное число из интервала $[c_{min}-I \cdot \alpha, c_{max}+I \cdot \alpha]$, $c_{min}=\min(c_{k1},c_{k2})$, $c_{max}=\max(c_{k1},c_{k2})$, $I=c_{max}-c_{min}$.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.5\linewidth]{img/blx_crossover.png}
|
||||
\caption{Смешанный кроссовер}
|
||||
\label{fig:blx_crossover}
|
||||
\end{figure}
|
||||
|
||||
\textbf{SBX кроссовер (Simulated Binary Crossover):} кроссовер, имитирующий двоичный, разработанный в 1995 году исследовательской группой под руководством K. Deb'а. Моделирует принципы работы двоичного оператора скрещивания, сохраняя важное свойство -- среднее значение функции приспособленности остаётся неизменным у родителей и их потомков.
|
||||
|
||||
Создаются два потомка $H_k=(h_{1k}, \ldots, h_{jk}, \ldots, h_{nk})$, $k=1,2$, где:
|
||||
$$h_{j1} = 0.5[(1+\beta_k)c_{j1} + (1-\beta_k)c_{j2}]$$
|
||||
$$h_{j2} = 0.5[(1-\beta_k)c_{j1} + (1+\beta_k)c_{j2}]$$
|
||||
|
||||
где $\beta_k \geq 0$ -- число, полученное по формуле:
|
||||
$$\beta_k = \begin{cases}
|
||||
(2u)^{\frac{1}{n+1}}, & \text{при } u \leq 0.5 \\
|
||||
\left(\frac{1}{2(1-u)}\right)^{\frac{1}{n+1}}, & \text{при } u > 0.5
|
||||
\end{cases}$$
|
||||
|
||||
где $u \in (0,1)$ -- случайное число, распределённое по равномерному закону, $n \in [2,5]$ -- параметр кроссовера. Увеличение $n$ повышает вероятность появления потомка в окрестности родителей.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/sbx_crossover.png}
|
||||
\caption{SBX кроссовер}
|
||||
\label{fig:sbx_crossover}
|
||||
\end{figure}
|
||||
|
||||
\subsubsection{Операторы мутации для real-coded алгоритмов}
|
||||
|
||||
В качестве оператора мутации наибольшее распространение получили: случайная и неравномерная мутация.
|
||||
|
||||
\textbf{Случайная мутация (random mutation):} ген, подлежащий изменению, принимает случайное значение из интервала своего изменения.
|
||||
|
||||
\textbf{Неравномерная мутация (non-uniform mutation):} из особи случайно выбирается точка $c_k$ с разрешёнными пределами изменения $[c_{kl}, c_{kr}]$. Точка меняется на:
|
||||
$$c_k' = \begin{cases}
|
||||
c_k + \Delta(t, c_{kr} - c_k), & \text{при } a = 1 \\
|
||||
c_k - \Delta(t, c_k - c_{kl}), & \text{при } a = 0
|
||||
\end{cases}$$
|
||||
|
||||
где $a$ -- случайно выбранное направление изменения, $\Delta(t, y)$ -- функция, возвращающая случайную величину в пределах $[0, y]$ таким образом, что при увеличении $t$ среднее возвращаемое значение уменьшается:
|
||||
$$\Delta(t, y) = y \cdot r \cdot \left(1 - \frac{t}{T}\right)^b$$
|
||||
|
||||
где $r$ -- случайная величина на интервале $[0, 1]$, $t$ -- текущая эпоха работы генетического алгоритма, $T$ -- общее разрешённое число эпох алгоритма, $b$ -- задаваемый пользователем параметр, определяющий степень зависимости от числа эпох.
|
||||
|
||||
\newpage
|
||||
\section{Особенности реализации}
|
||||
В рамках работы создана мини-библиотека \texttt{gen.py} для экспериментов с real-coded
|
||||
генетическим алгоритмом для многомерных функций. Второй модуль
|
||||
\texttt{expirements.py} организует серийные эксперименты (перебор параметров,
|
||||
форматирование и сохранение результатов).
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{Кодирование особей}: каждая хромосома представлена как \texttt{np.ndarray} вещественных чисел (\texttt{Chromosome = NDArray[np.float64]}). Длина хромосомы соответствует размерности задачи оптимизации. Популяция -- список хромосом (\texttt{Population = list[Chromosome]}). Инициализация случайными векторами в заданном диапазоне:
|
||||
\begin{itemize}
|
||||
\item \texttt{initialize\_population(pop\_size: int, x\_min: Chromosome, x\_max:}\\ \texttt{Chromosome) -> Population}
|
||||
\end{itemize}
|
||||
\item \textbf{Фитнесс и минимум/максимум}: целевая функция принимает хромосому (вектор) и возвращает скалярное значение фитнесса. Для режима минимизации используется внутреннее преобразование при селекции (сдвиг на минимальное значение), что позволяет применять рулетку при отрицательных значениях:
|
||||
\begin{itemize}
|
||||
\item \texttt{eval\_population(population: Population, fitness\_func: FitnessFn) -> Fitnesses}
|
||||
\item Логика режима минимизации в \texttt{genetic\_algorithm(config: GARunConfig) -> GARunResult}
|
||||
\end{itemize}
|
||||
\item \textbf{Селекция (рулетка)}: вероятности нормируются после сдвига на минимальное значение в поколении (устойчиво к отрицательным фитнессам). Функция:
|
||||
\texttt{reproduction(population: Population, fitnesses: Fitnesses) -> Population}.
|
||||
\item \textbf{Кроссинговер}: реализованы арифметический и геометрический кроссоверы для real-coded алгоритмов. Кроссинговер выполняется попарно по перемешанной популяции с вероятностью $p_c$. Функции:
|
||||
\begin{itemize}
|
||||
\item \texttt{arithmetical\_crossover\_fn(p1: Chromosome, p2: Chromosome, w: float) -> tuple[Chromosome, Chromosome]}
|
||||
\item \texttt{geometrical\_crossover\_fn(p1: Chromosome, p2: Chromosome, w: float) -> tuple[Chromosome, Chromosome]}
|
||||
\item \texttt{crossover(population: Population, pc: float, crossover\_fn: CrossoverFn) -> Population}
|
||||
\end{itemize}
|
||||
\item \textbf{Мутация}: случайная мутация -- с вероятностью $p_m$ на хромосому изменяется один случайно выбранный ген на случайное значение из допустимого диапазона. Функции:
|
||||
\begin{itemize}
|
||||
\item \texttt{build\_random\_mutation\_fn(x\_min: Chromosome, x\_max: Chromosome) -> MutationFn}
|
||||
\item \texttt{mutation(population: Population, pm: float, mutation\_fn: MutationFn) -> Population}
|
||||
\end{itemize}
|
||||
|
||||
\item \textbf{Критерий остановки}: поддерживается критерий по среднему значению фитнесс-функции в популяции и максимальному количеству поколений. Хранится история всех поколений. Проверка выполняется в функции:
|
||||
|
||||
\texttt{genetic\_algorithm(config: GARunConfig) -> GARunResult}.
|
||||
\item \textbf{Визуализация}: для двумерных функций реализованы 3D-графики поверхности и 2D-контурные графики с отображением популяций. Функции:
|
||||
\begin{itemize}
|
||||
\item \texttt{plot\_fitness\_surface(fitness\_func: FitnessFn, x\_min: Chromosome, x\_max: Chromosome, ax: Axes3D)}
|
||||
\item \texttt{plot\_fitness\_contour(fitness\_func: FitnessFn, x\_min: Chromosome, x\_max: Chromosome, ax: Axes)}
|
||||
\item \texttt{save\_generation(generation: Generation, history: list[Generation], config: GARunConfig)}
|
||||
\end{itemize}
|
||||
\item \textbf{Измерение времени}: длительность вычислений возвращается в миллисекундах как часть \texttt{GARunResult.time\_ms}.
|
||||
\item \textbf{Файловая организация}: результаты экспериментов сохраняются иерархически в структуре \texttt{experiments/N/} с таблицами результатов в формате CSV. Задействованные функции:
|
||||
\begin{itemize}
|
||||
\item \texttt{clear\_results\_directory(results\_dir: str) -> None}
|
||||
\item \texttt{run\_single\_experiment(pop\_size: int, pc: float, pm: float) -> tuple[float, float, float, float]}
|
||||
\item \texttt{run\_experiments\_for\_population(pop\_size: int) -> PrettyTable}
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
|
||||
В модуле \texttt{expirements.py} задаётся целевая функция axis parallel hyper-ellipsoid: $f(x, y) = x^2 + 2y^2$ и параметры экспериментов.
|
||||
Серийные запуски и сохранение результатов реализованы в функциях \texttt{run\_single\_experiment}, \texttt{run\_experiments\_for\_population} и \texttt{main}.
|
||||
|
||||
\newpage
|
||||
\section{Результаты работы}
|
||||
На Рис.~\ref{fig:gen1}--\ref{fig:lastgen} представлены результаты работы генетического алгоритма со следующими параметрами:
|
||||
\begin{itemize}
|
||||
\item $N = 25$ -- размер популяции.
|
||||
\item $p_c = 0.5$ -- вероятность кроссинговера.
|
||||
\item $p_m = 0.01$ -- вероятность мутации.
|
||||
\item Алгоритм останавливался, если лучшее значение фитнеса не изменялось $10$ поколений подряд.
|
||||
\item Использован арифметический кроссовер для real-coded хромосом.
|
||||
\end{itemize}
|
||||
|
||||
Популяция постепенно консолидируется вокруг глобального минимума в точке $(0, 0)$. Лучшая особь была найдена на поколнении №9 (см. Рис.~\ref{fig:gen9}), но судя по всему она подверглась мутации или кроссинговеру, поэтому алгоритм не остановился. На поколении №19 (см. Рис.~\ref{fig:lastgen}) было получено значение фитнеса $0.0201$, которое затем повторялось в следующих 10 поколениях. Алгоритм остановился на поколлении №29. На графиках показаны 2D-контурный график (a) и 3D-поверхность целевой функции с точками популяции текущего поколения (b) и (c).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/results/generation_001.png}
|
||||
\caption{График целевой функции и популяции поколения №1}
|
||||
\label{fig:gen1}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/results/generation_002.png}
|
||||
\caption{График целевой функции и популяции поколения №2}
|
||||
\label{fig:gen2}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/results/generation_003.png}
|
||||
\caption{График целевой функции и популяции поколения №3}
|
||||
\label{fig:gen3}
|
||||
\end{figure}
|
||||
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/results/generation_005.png}
|
||||
\caption{График целевой функции и популяции поколения №5}
|
||||
\label{fig:gen5}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/results/generation_007.png}
|
||||
\caption{График целевой функции и популяции поколения №7}
|
||||
\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}
|
||||
\caption{График целевой функции и популяции поколения №10}
|
||||
\label{fig:gen10}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\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}
|
||||
|
||||
|
||||
\newpage
|
||||
\phantom{text}
|
||||
\newpage
|
||||
\phantom{text}
|
||||
\newpage
|
||||
\phantom{text}
|
||||
|
||||
\newpage
|
||||
\section{Исследование реализации}
|
||||
\subsection{Проведение измерений}
|
||||
В рамках лабораторной работы необходимо было исследовать зависимость времени выполнения задачи и количества поколений от популяции и вероятностей кроссинговера и мутации хромосомы
|
||||
|
||||
Для исследования были выбраны следующие значения параметров:
|
||||
\begin{itemize}
|
||||
\item $N = 10, 25, 50, 100$ -- размер популяции.
|
||||
\item $p_c = 0.3, 0.4, 0.5, 0.6, 0.7, 0.8$ -- вероятность кроссинговера.
|
||||
\item $p_m = 0.001, 0.01, 0.05, 0.1, 0.2$ -- вероятность мутации.
|
||||
\end{itemize}
|
||||
|
||||
Измерения были проведены для двух критериев остановки:
|
||||
\begin{itemize}
|
||||
\item Лучшее значение фитнеса не изменялось 10 поколений.
|
||||
\item Лучшее значение фитнеса достигло заданного значения $0.005$.
|
||||
\end{itemize}
|
||||
|
||||
\subsubsection*{Результаты для первого критерия остановки}
|
||||
|
||||
Результаты измерений представлены в таблицах \ref{tab:pc_pm_results_10}--\ref{tab:pc_pm_results_100}. В ячейках указано время в миллисекундах нахождения минимума функции. В скобках указано количество поколений, за которое было найдено решение. Во второй строке указано усреднённое по всем запускам лучшее значение фитнеса. Если в ячейке стоит прочерк, то это означает, что решение не было найдено за 200 поколений. Лучшее значение по времени выполнения и по значению фитнеса для каждого размера популяции выделено цветом и жирным шрифтом.
|
||||
|
||||
\newcolumntype{Y}{>{\centering\arraybackslash}X}
|
||||
% Автоматически сгенерированные LaTeX таблицы
|
||||
% Лучший результат по времени и по фитнесу выделены жирным отдельно
|
||||
% Убедитесь, что подключен \usepackage{tabularx}
|
||||
% ВНИМАНИЕ: Убедитесь, что подключен \usepackage{xcolor} для цветового выделения
|
||||
% Используйте \newcolumntype{Y}{>{\centering\arraybackslash}X} перед таблицами
|
||||
|
||||
\begin{table}[h!]
|
||||
\centering
|
||||
\small
|
||||
\caption{Результаты для $N = 10$}
|
||||
\begin{tabularx}{\linewidth}{l *{5}{Y}}
|
||||
\toprule
|
||||
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
||||
\midrule
|
||||
\textbf{0.3} & 1.3 (13) 0.36281 & 1.7 (18) 7.55685 & 1.2 (13) 1.55537 & \textcolor{magenta}{\textbf{1.0 (11)}} 1.78411 & 9.4 (87) 0.04271 \\
|
||||
\textbf{0.4} & 1.3 (14) 0.03913 & 1.6 (17) 0.02868 & 1.3 (13) 0.36232 & 2.1 (20) 0.10641 & — \\
|
||||
\textbf{0.5} & 1.4 (15) 0.87081 & 1.7 (18) 1.71634 & 2.3 (21) 0.10401 & 3.4 (25) 0.00461 & — \\
|
||||
\textbf{0.6} & 2.8 (19) 0.06375 & 1.8 (13) 0.72202 & 2.9 (22) 0.01473 & 3.4 (25) 0.01162 & 29.4 (184) \textcolor{magenta}{\textbf{0.00033}} \\
|
||||
\textbf{0.7} & 1.5 (15) 1.25409 & 2.3 (22) 8.67464 & 1.9 (18) 0.13319 & 8.6 (66) 0.00078 & 8.9 (48) 0.11136 \\
|
||||
\textbf{0.8} & 1.9 (15) 3.10415 & 1.4 (13) 1.09275 & 2.1 (19) 0.43094 & 6.4 (54) 0.00191 & — \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
\label{tab:pc_pm_results_10}
|
||||
\end{table}
|
||||
|
||||
|
||||
\begin{table}[h!]
|
||||
\centering
|
||||
\small
|
||||
\caption{Результаты для $N = 25$}
|
||||
\begin{tabularx}{\linewidth}{l *{5}{Y}}
|
||||
\toprule
|
||||
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
||||
\midrule
|
||||
\textbf{0.3} & 3.0 (18) 0.16836 & \textcolor{magenta}{\textbf{2.2 (13)}} 0.04190 & 4.7 (27) 0.00544 & — & — \\
|
||||
\textbf{0.4} & 4.1 (24) 0.00808 & 4.6 (26) 0.01101 & 5.8 (31) 0.02330 & 3.8 (19) 0.05414 & — \\
|
||||
\textbf{0.5} & 3.1 (17) 0.05259 & 5.0 (26) 0.47018 & 27.8 (138) \textcolor{magenta}{\textbf{0.00024}} & 14.5 (67) 0.00312 & — \\
|
||||
\textbf{0.6} & 6.1 (31) 0.01033 & 6.8 (34) 0.00148 & — & — & — \\
|
||||
\textbf{0.7} & 4.1 (21) 0.00107 & 3.2 (16) 0.32522 & — & — & — \\
|
||||
\textbf{0.8} & 23.9 (109) 0.00352 & 15.8 (72) 0.11662 & 28.3 (123) 0.00038 & — & — \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
\label{tab:pc_pm_results_25}
|
||||
\end{table}
|
||||
|
||||
|
||||
\begin{table}[h!]
|
||||
\centering
|
||||
\small
|
||||
\caption{Результаты для $N = 50$}
|
||||
\begin{tabularx}{\linewidth}{l *{5}{Y}}
|
||||
\toprule
|
||||
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
||||
\midrule
|
||||
\textbf{0.3} & 14.9 (51) 0.05874 & 19.3 (59) \textcolor{magenta}{\textbf{0.00003}} & 36.7 (113) 0.00190 & — & — \\
|
||||
\textbf{0.4} & 12.5 (40) 0.01955 & \textcolor{magenta}{\textbf{5.6 (18)}} 0.00022 & — & — & — \\
|
||||
\textbf{0.5} & 65.0 (195) 0.04790 & 26.4 (78) 0.01673 & — & — & — \\
|
||||
\textbf{0.6} & 16.4 (47) 0.00329 & 18.5 (50) 0.00065 & — & — & — \\
|
||||
\textbf{0.7} & 51.0 (137) 0.00120 & 59.3 (158) 0.00010 & — & — & — \\
|
||||
\textbf{0.8} & 48.8 (126) 0.01393 & 67.6 (172) 0.00650 & — & — & — \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
\label{tab:pc_pm_results_50}
|
||||
\end{table}
|
||||
|
||||
|
||||
\begin{table}[h!]
|
||||
\centering
|
||||
\small
|
||||
\caption{Результаты для $N = 100$}
|
||||
\begin{tabularx}{\linewidth}{l *{5}{Y}}
|
||||
\toprule
|
||||
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
||||
\midrule
|
||||
\textbf{0.3} & 24.2 (44) 0.00110 & 17.9 (32) 0.00113 & \textcolor{magenta}{\textbf{17.6 (29)}} 0.00193 & — & — \\
|
||||
\textbf{0.4} & 30.7 (51) 0.00173 & — & — & — & — \\
|
||||
\textbf{0.5} & 27.4 (43) 0.00016 & — & — & — & — \\
|
||||
\textbf{0.6} & 20.4 (31) 0.00115 & 129.8 (186) 0.00025 & — & — & — \\
|
||||
\textbf{0.7} & 115.4 (162) 0.00002 & — & — & — & — \\
|
||||
\textbf{0.8} & 106.5 (143) \textcolor{magenta}{\textbf{0.00001}} & — & — & — & — \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
\label{tab:pc_pm_results_100}
|
||||
\end{table}
|
||||
|
||||
\newpage
|
||||
\phantom{text}
|
||||
\newpage
|
||||
\subsubsection*{Результаты для второго критерия остановки}
|
||||
|
||||
Результаты измерений представлены в таблицах \ref{tab:1_pc_pm_results_10}--\ref{tab:1_pc_pm_results_100}.
|
||||
|
||||
|
||||
% Автоматически сгенерированные LaTeX таблицы
|
||||
% Лучший результат по времени и по фитнесу выделены жирным отдельно
|
||||
% Убедитесь, что подключен \usepackage{tabularx}
|
||||
% ВНИМАНИЕ: Убедитесь, что подключен \usepackage{xcolor} для цветового выделения
|
||||
% Используйте \newcolumntype{Y}{>{\centering\arraybackslash}X} перед таблицами
|
||||
|
||||
\begin{table}[h!]
|
||||
\centering
|
||||
\small
|
||||
\caption{Результаты для $N = 10$}
|
||||
\begin{tabularx}{\linewidth}{l *{5}{Y}}
|
||||
\toprule
|
||||
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
||||
\midrule
|
||||
\textbf{0.3} & — & — & — & 15.6 (155) 0.00063 & 7.8 (69) 0.00409 \\
|
||||
\textbf{0.4} & — & — & — & 8.9 (81) \textcolor{magenta}{\textbf{0.00038}} & \textcolor{magenta}{\textbf{4.6 (40)}} 0.00317 \\
|
||||
\textbf{0.5} & — & — & 8.7 (85) 0.00199 & — & 16.5 (140) 0.00453 \\
|
||||
\textbf{0.6} & — & — & — & 8.9 (77) 0.00310 & 14.3 (117) 0.00082 \\
|
||||
\textbf{0.7} & — & — & 8.2 (70) 0.00089 & 5.6 (49) 0.00431 & 7.1 (58) 0.00047 \\
|
||||
\textbf{0.8} & — & 19.7 (180) 0.00397 & — & 5.0 (42) 0.00494 & 5.5 (44) 0.00357 \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
\label{tab:1_pc_pm_results_10}
|
||||
\end{table}
|
||||
|
||||
|
||||
\begin{table}[h!]
|
||||
\centering
|
||||
\small
|
||||
\caption{Результаты для $N = 25$}
|
||||
\begin{tabularx}{\linewidth}{l *{5}{Y}}
|
||||
\toprule
|
||||
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
||||
\midrule
|
||||
\textbf{0.3} & 1.1 (7) 0.00277 & 30.0 (173) \textcolor{magenta}{\textbf{0.00059}} & — & 2.2 (12) 0.00191 & 30.2 (139) 0.00200 \\
|
||||
\textbf{0.4} & 1.8 (10) 0.00384 & — & 12.2 (63) 0.00164 & 6.6 (33) 0.00354 & 18.5 (82) 0.00224 \\
|
||||
\textbf{0.5} & — & — & 12.5 (58) 0.00233 & 2.3 (11) 0.00196 & 17.1 (73) 0.00116 \\
|
||||
\textbf{0.6} & — & 30.9 (151) 0.00265 & 36.7 (175) 0.00146 & 10.0 (46) 0.00449 & 5.7 (23) 0.00281 \\
|
||||
\textbf{0.7} & 1.1 (6) 0.00472 & — & 0.8 (4) 0.00233 & 3.9 (17) 0.00112 & \textcolor{magenta}{\textbf{0.3 (2)}} 0.00371 \\
|
||||
\textbf{0.8} & — & — & 10.3 (43) 0.00137 & 7.7 (32) 0.00379 & 10.5 (41) 0.00155 \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
\label{tab:1_pc_pm_results_25}
|
||||
\end{table}
|
||||
|
||||
|
||||
\begin{table}[h!]
|
||||
\centering
|
||||
\small
|
||||
\caption{Результаты для $N = 50$}
|
||||
\begin{tabularx}{\linewidth}{l *{5}{Y}}
|
||||
\toprule
|
||||
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
||||
\midrule
|
||||
\textbf{0.3} & 3.7 (12) 0.00354 & 3.4 (9) 0.00075 & 23.7 (73) 0.00467 & 4.9 (14) 0.00043 & 2.1 (6) 0.00029 \\
|
||||
\textbf{0.4} & 3.6 (12) 0.00270 & 4.2 (13) 0.00061 & 9.2 (25) 0.00251 & 18.2 (51) 0.00490 & 6.6 (16) 0.00063 \\
|
||||
\textbf{0.5} & 4.0 (10) 0.00099 & 48.8 (141) 0.00324 & 3.8 (11) 0.00087 & 14.7 (39) \textcolor{magenta}{\textbf{0.00017}} & 1.2 (3) 0.00115 \\
|
||||
\textbf{0.6} & 1.6 (5) 0.00070 & 51.6 (139) 0.00217 & 4.7 (13) 0.00294 & 2.6 (7) 0.00397 & 11.5 (27) 0.00053 \\
|
||||
\textbf{0.7} & — & — & 2.6 (7) 0.00144 & 3.5 (9) 0.00182 & \textcolor{magenta}{\textbf{1.1 (3)}} 0.00072 \\
|
||||
\textbf{0.8} & 4.1 (11) 0.00240 & 3.5 (8) 0.00380 & 2.5 (6) 0.00422 & 2.7 (7) 0.00126 & 4.3 (10) 0.00060 \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
\label{tab:1_pc_pm_results_50}
|
||||
\end{table}
|
||||
|
||||
|
||||
\begin{table}[h!]
|
||||
\centering
|
||||
\small
|
||||
\caption{Результаты для $N = 100$}
|
||||
\begin{tabularx}{\linewidth}{l *{5}{Y}}
|
||||
\toprule
|
||||
$\mathbf{P_c \;\backslash\; P_m}$ & \textbf{0.001} & \textbf{0.010} & \textbf{0.050} & \textbf{0.100} & \textbf{0.200} \\
|
||||
\midrule
|
||||
\textbf{0.3} & 9.3 (17) 0.00451 & 6.0 (11) 0.00344 & 10.0 (17) 0.00343 & 5.3 (8) 0.00046 & 9.8 (14) 0.00412 \\
|
||||
\textbf{0.4} & 5.7 (9) \textcolor{magenta}{\textbf{0.00005}} & 8.4 (14) 0.00108 & 3.5 (6) 0.00254 & 4.0 (6) 0.00186 & 6.5 (9) 0.00283 \\
|
||||
\textbf{0.5} & 3.8 (6) 0.00019 & 4.9 (8) 0.00103 & 3.6 (6) 0.00260 & 11.1 (16) 0.00204 & 7.5 (10) 0.00374 \\
|
||||
\textbf{0.6} & — & 6.5 (10) 0.00107 & 3.6 (5) 0.00079 & \textcolor{magenta}{\textbf{0.9 (2)}} 0.00324 & 10.1 (13) 0.00044 \\
|
||||
\textbf{0.7} & 1.7 (3) 0.00106 & 6.6 (10) 0.00489 & 4.1 (6) 0.00031 & 12.4 (16) 0.00240 & 4.8 (6) 0.00276 \\
|
||||
\textbf{0.8} & 5.0 (7) 0.00387 & 58.4 (77) 0.00453 & 7.8 (10) 0.00259 & 11.2 (13) 0.00210 & 6.1 (7) 0.00493 \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
\label{tab:1_pc_pm_results_100}
|
||||
\end{table}
|
||||
|
||||
|
||||
\newpage
|
||||
\phantom{text}
|
||||
\newpage
|
||||
\phantom{text}
|
||||
\newpage
|
||||
\phantom{text}
|
||||
\subsection{Анализ результатов}
|
||||
|
||||
\subsubsection*{Обоснование применения двух критериев остановки}
|
||||
|
||||
В исследовании использовались два различных критерия остановки алгоритма, поскольку критерий по количеству поколений (отсутствие улучшения в течение 10 поколений) не всегда обеспечивал достижение достаточно хороших значений фитнеса, особенно для малых популяций. Это делало некорректным сравнение эффективности различных комбинаций параметров только по времени выполнения. Введение второго критерия (достижение фитнеса 0.005) позволило получить более объективную оценку скорости нахождении качественных решений.
|
||||
|
||||
\subsubsection*{Первый критерий остановки (отсутствие улучшения в течение 10 поколений)}
|
||||
|
||||
При использовании первого критерия остановки наблюдаются следующие закономерности:
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{Малые популяции ($N=10$):} Оптимальный баланс достигается при умеренных значениях параметров. Лучший результат по времени показывает комбинация $p_c=0.3$, $p_m=0.1$ (1.0 мс, 11 поколений), однако лучшее значение фитнеса достигается при $p_c=0.6$, $p_m=0.2$ (0.00033). Качество решений существенно варьируется.
|
||||
|
||||
\item \textbf{Средние популяции ($N=25$):} Демонстрируют высокую эффективность при низких значениях мутации. Минимальное время выполнения достигается при $p_c=0.3$, $p_m=0.01$ (2.2 мс, 13 поколений), а наилучший фитнес — при $p_c=0.5$, $p_m=0.05$ (0.00024).
|
||||
|
||||
\item \textbf{Большие популяции ($N=50, 100$):} Характеризуются критической чувствительностью к высоким значениям мутации и демонстрируют заметное улучшение качества фитнеса. Для $N=50$ лучшие результаты при $p_c=0.4$, $p_m=0.01$ (5.6 мс по времени) и $p_c=0.3$, $p_m=0.01$ (фитнес 0.00003). Для $N=100$ работают только комбинации с очень низкой мутацией, но обеспечивают отличное качество (фитнес до 0.00001).
|
||||
|
||||
\item \textbf{Проблема сходимости:} С увеличением размера популяции значительно возрастает количество комбинаций параметров, не обеспечивающих сходимость за 200 поколений, особенно при $p_m \geq 0.05$.
|
||||
\end{itemize}
|
||||
|
||||
\subsubsection*{Второй критерий остановки (достижение фитнеса 0.005)}
|
||||
|
||||
Использование фиксированного порога фитнеса демонстрирует принципиально иную картину и подтверждает правильность введения альтернативного критерия:
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{Инверсия требований к мутации:} В отличие от первого критерия, здесь малые популяции требуют более высоких значений мутации для достижения целевого фитнеса. Для $N=10$ большинство комбинаций с $p_m \leq 0.01$ вообще не достигают порога, что подтверждает проблему качества при первом критерии.
|
||||
|
||||
\item \textbf{Лучшие результаты больших популяций:} Популяции $N=50$ и $N=100$ показывают отличные результаты — достижение высокого качества за минимальное время: $N=50$ при $p_c=0.7$, $p_m=0.2$ (1.1 мс, 3 поколения) и $N=100$ при $p_c=0.6$, $p_m=0.1$ (0.9 мс, 2 поколения).
|
||||
\end{itemize}
|
||||
|
||||
|
||||
\newpage
|
||||
\section{Ответ на контрольный вопрос}
|
||||
|
||||
\textbf{Вопрос}: Опишите понятие «оптимизационная задача».
|
||||
|
||||
\textbf{Ответ}: Оптимизационная задача — это математическая задача, в которой требуется найти такие значения переменных, при которых некоторая функция, называемая целевой, принимает наибольшее или наименьшее значение. При этом искомые значения должны удовлетворять определённым условиям или ограничениям, задающим допустимую область решений. Цель оптимизации заключается в выборе наилучшего варианта среди множества возможных с точки зрения заданного критерия эффективности.
|
||||
|
||||
Такие задачи широко применяются в науке, технике, экономике и управлении для рационального распределения ресурсов, минимизации затрат или максимизации прибыли. В зависимости от формы целевой функции и ограничений оптимизационные задачи могут быть линейными, нелинейными, дискретными или непрерывными. Их решение позволяет принимать обоснованные решения и повышать эффективность различных процессов и систем.
|
||||
|
||||
|
||||
\newpage
|
||||
\section*{Заключение}
|
||||
\addcontentsline{toc}{section}{Заключение}
|
||||
|
||||
В ходе второй лабораторной работы была успешно решена задача оптимизации функции Axis parallel hyper-ellipsoid function с использованием генетических алгоритмов:
|
||||
|
||||
\begin{enumerate}
|
||||
\item Изучен теоретический материал о real-coded генетических алгоритмах и различных операторах кроссинговера и мутации;
|
||||
\item Создана программная библиотека на языке Python с реализацией арифметического и геометрического кроссоверов, случайной мутации и селекции методом рулетки;
|
||||
\item Проведено исследование влияния параметров ГА на эффективность поиска для популяций размером 10, 25, 50 и 100 особей;
|
||||
\end{enumerate}
|
||||
|
||||
\newpage
|
||||
\section*{Список литературы}
|
||||
\addcontentsline{toc}{section}{Список литературы}
|
||||
|
||||
\vspace{-1.5cm}
|
||||
\begin{thebibliography}{0}
|
||||
\bibitem{vostrov}
|
||||
Методические указания по выполнению лабораторных работ к курсу «Генетические алгоритмы», 119 стр.
|
||||
\end{thebibliography}
|
||||
|
||||
\end{document}
|
||||
6
presentation/report/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
*
|
||||
|
||||
!.gitignore
|
||||
!**/
|
||||
!img/*
|
||||
!*.tex
|
||||
BIN
presentation/report/img/angle.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
presentation/report/img/angle_chromosome.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
presentation/report/img/angle_experiment1.png
Normal file
|
After Width: | Height: | Size: 205 KiB |
BIN
presentation/report/img/angle_experiment2.png
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
presentation/report/img/angle_experiment3.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
presentation/report/img/binary.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
presentation/report/img/classification.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
presentation/report/img/crossover.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
presentation/report/img/decimal.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
presentation/report/img/experiment1.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
presentation/report/img/experiment2.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
presentation/report/img/genalg.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
presentation/report/img/grid.png
Normal file
|
After Width: | Height: | Size: 143 KiB |
BIN
presentation/report/img/obstacle1.png
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
presentation/report/img/obstacle2.png
Normal file
|
After Width: | Height: | Size: 201 KiB |
BIN
presentation/report/img/ordered.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
presentation/report/img/static.png
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
presentation/report/img/table.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
presentation/report/img/table1.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
presentation/report/img/table2.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
presentation/report/img/task.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
580
presentation/report/ГА. Реферат. Тищенко.tex
Normal file
@@ -0,0 +1,580 @@
|
||||
\documentclass[a4paper, final]{article}
|
||||
%\usepackage{literat} % Нормальные шрифты
|
||||
\usepackage[14pt]{extsizes} % для того чтобы задать нестандартный 14-ый размер шрифта
|
||||
\usepackage{tabularx}
|
||||
\usepackage[T2A]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[russian]{babel}
|
||||
\usepackage{amsmath}
|
||||
\usepackage[left=25mm, top=20mm, right=20mm, bottom=20mm, footskip=10mm]{geometry}
|
||||
\usepackage{ragged2e} %для растягивания по ширине
|
||||
\usepackage{setspace} %для межстрочно го интервала
|
||||
\usepackage{moreverb} %для работы с листингами
|
||||
\usepackage{indentfirst} % для абзацного отступа
|
||||
\usepackage{moreverb} %для печати в листинге исходного кода программ
|
||||
\usepackage{pdfpages} %для вставки других pdf файлов
|
||||
\usepackage{tikz}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{afterpage}
|
||||
\usepackage{longtable}
|
||||
\usepackage{float}
|
||||
|
||||
|
||||
|
||||
% \usepackage[paper=A4,DIV=12]{typearea}
|
||||
\usepackage{pdflscape}
|
||||
% \usepackage{lscape}
|
||||
|
||||
\usepackage{array}
|
||||
\usepackage{multirow}
|
||||
|
||||
\renewcommand\verbatimtabsize{4\relax}
|
||||
\renewcommand\listingoffset{0.2em} %отступ от номеров строк в листинге
|
||||
\renewcommand{\arraystretch}{1.4} % изменяю высоту строки в таблице
|
||||
\usepackage[font=small, singlelinecheck=false, justification=centering, format=plain, labelsep=period]{caption} %для настройки заголовка таблицы
|
||||
\usepackage{listings} %листинги
|
||||
\usepackage{xcolor} % цвета
|
||||
\usepackage{hyperref}% для гиперссылок
|
||||
\usepackage{enumitem} %для перечислений
|
||||
|
||||
\newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}}
|
||||
|
||||
|
||||
\setlist[enumerate,itemize]{leftmargin=1.2cm} %отступ в перечислениях
|
||||
|
||||
\hypersetup{colorlinks,
|
||||
allcolors=[RGB]{010 090 200}} %красивые гиперссылки (не красные)
|
||||
|
||||
% подгружаемые языки — подробнее в документации listings (это всё для листингов)
|
||||
\lstloadlanguages{ SQL}
|
||||
% включаем кириллицу и добавляем кое−какие опции
|
||||
\lstset{tabsize=2,
|
||||
breaklines,
|
||||
basicstyle=\footnotesize,
|
||||
columns=fullflexible,
|
||||
flexiblecolumns,
|
||||
numbers=left,
|
||||
numberstyle={\footnotesize},
|
||||
keywordstyle=\color{blue},
|
||||
inputencoding=cp1251,
|
||||
extendedchars=true
|
||||
}
|
||||
\lstdefinelanguage{MyC}{
|
||||
language=SQL,
|
||||
% ndkeywordstyle=\color{darkgray}\bfseries,
|
||||
% identifierstyle=\color{black},
|
||||
% morecomment=[n]{/**}{*/},
|
||||
% commentstyle=\color{blue}\ttfamily,
|
||||
% stringstyle=\color{red}\ttfamily,
|
||||
% morestring=[b]",
|
||||
% showstringspaces=false,
|
||||
% morecomment=[l][\color{gray}]{//},
|
||||
keepspaces=true,
|
||||
escapechar=\%,
|
||||
texcl=true
|
||||
}
|
||||
|
||||
\textheight=24cm % высота текста
|
||||
\textwidth=16cm % ширина текста
|
||||
\oddsidemargin=0pt % отступ от левого края
|
||||
\topmargin=-1.5cm % отступ от верхнего края
|
||||
\parindent=24pt % абзацный отступ
|
||||
\parskip=5pt % интервал между абзацами
|
||||
\tolerance=2000 % терпимость к "жидким" строкам
|
||||
\flushbottom % выравнивание высоты страниц
|
||||
|
||||
|
||||
% Настройка листингов
|
||||
\lstset{
|
||||
language=python,
|
||||
extendedchars=\true,
|
||||
inputencoding=utf8,
|
||||
keepspaces=true,
|
||||
% captionpos=b, % подписи листингов снизу
|
||||
}
|
||||
|
||||
\begin{document} % начало документа
|
||||
|
||||
|
||||
|
||||
% НАЧАЛО ТИТУЛЬНОГО ЛИСТА
|
||||
\begin{center}
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\
|
||||
федеральное государственное автономное образовательное учреждение высшего образования «Санкт-Петербургский политехнический университет Петра Великого»\\[10pt]}
|
||||
\normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt]
|
||||
\normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt]
|
||||
\normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\
|
||||
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\large{Реферат по дисциплине}\\
|
||||
\large{<<Генетические алгоритмы>>}\\
|
||||
\large{<<Планирование пути робота>>}\\
|
||||
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\end{center}
|
||||
|
||||
\small{
|
||||
\begin{tabular}{lrrl}
|
||||
\!\!\!Студент, & \hspace{2cm} & & \\
|
||||
\!\!\!группы 5130201/20102 & \hspace{2cm} & \underline{\hspace{3cm}} &Тищенко А. А. \\\\
|
||||
\!\!\!Преподаватель & \hspace{2cm} & \underline{\hspace{3cm}} & Большаков А. А. \\\\
|
||||
&&\hspace{4cm}
|
||||
\end{tabular}
|
||||
\begin{flushright}
|
||||
<<\underline{\hspace{1cm}}>>\underline{\hspace{2.5cm}} 2025г.
|
||||
\end{flushright}
|
||||
}
|
||||
|
||||
\hfill \break
|
||||
% \hfill \break
|
||||
\begin{center} \small{Санкт-Петербург, 2025} \end{center}
|
||||
\thispagestyle{empty} % выключаем отображение номера для этой страницы
|
||||
|
||||
% КОНЕЦ ТИТУЛЬНОГО ЛИСТА
|
||||
\newpage
|
||||
|
||||
\tableofcontents
|
||||
|
||||
|
||||
\newpage
|
||||
|
||||
\section*{Введение}
|
||||
\addcontentsline{toc}{section}{Введение}
|
||||
Планирование пути мобильного робота — это задача построения коллизие-свободной траектории из начальной точки в целевую в присутствии препятствий и ограничений среды. Актуальность задачи обусловлена широким спектром приложений: от автономной логистики и сельского хозяйства до роботизированной инспекции и сервисной робототехники. Традиционные графовые методы (Дейкстра, A*) обеспечивают точный поиск на известных картах, однако их эффективность снижается при росте размерности, наличии динамики и сложных ограничений. Эволюционные подходы, в частности генетические алгоритмы (ГА), предоставляют гибкую эвристическую альтернативу, позволяя естественно учитывать несколько критериев качества и штрафы за нарушения ограничений.
|
||||
|
||||
Цель работы — исследовать применение ГА для планирования пути на дискретном представлении среды (grid) и в параметризации траекторий через направления движения (угловое кодирование), а также сравнить различные способы кодирования хромосом и их влияние на качество маршрутов и вычислительные затраты.
|
||||
|
||||
В работе:
|
||||
\begin{itemize}
|
||||
\item кратко рассмотрена классификация алгоритмов планирования пути по характеру среды (статическая/динамическая), принципу (глобальные/локальные) и полноте (точные/эвристические);
|
||||
\item формализована фитнесс-функция с учётом длины пути и штрафов за пересечение препятствий, а для углового кодирования — с дополнительными слагаемыми за число поворотов;
|
||||
\item изучены три варианта grid-кодирования узлов пути (двоичное, десятичное, упорядоченное) и угловое кодирование с 4 и 8 направлениями движения;
|
||||
\item проведены эксперименты в статических сценах и сценарии с поэтапным появлением новых препятствий (offline/online-перепланирование);
|
||||
\item сопоставлены метрики: длина маршрута, число поколений и время решения.
|
||||
\end{itemize}
|
||||
|
||||
Полученные результаты демонстрируют применимость ГА к задачам планирования как в статических, так и в динамически меняющихся средах, а также выявляют влияние выбора представления пути на качество и стоимость поиска.
|
||||
|
||||
|
||||
\newpage
|
||||
\section{Постановка задачи}
|
||||
Планирование пути робота -- вычисление пути, свободного от столкновений, от начальной позиции до конечной
|
||||
среди множества препятствий
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.8\linewidth]{img/task.png}
|
||||
\caption{Задача планирования пути}
|
||||
\label{fig:task}
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
\section{Классификация алгоритмов планирования пути}
|
||||
На схеме \ref{fig:classification} показана классификация алгоритмов планирования пути.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{img/classification.png}
|
||||
\caption{Классификация алгоритмов планирования пути}
|
||||
\label{fig:classification}
|
||||
\end{figure}
|
||||
|
||||
Алгоритмы планирования пути можно классифицировать по трём главным признакам:
|
||||
|
||||
\begin{itemize}
|
||||
|
||||
\item По характеру среды (based on environment)
|
||||
|
||||
\begin{itemize}
|
||||
|
||||
\item \textbf{Статические (Static)}
|
||||
Используются, когда карта среды известна заранее и не меняется. Все препятствия заранее заданы, и алгоритм строит маршрут до старта движения.
|
||||
Пример: классические алгоритмы поиска пути на графе (A*, Дейкстра).
|
||||
|
||||
\item \textbf{Динамические (Dynamic)}
|
||||
Применяются в условиях, где препятствия могут появляться или перемещаться во время движения робота. Алгоритм должен уметь перестраивать путь «на лету» (онлайн-планирование).
|
||||
Пример: D* Lite, Anytime Repairing A*, алгоритмы на основе предсказаний и реактивного управления.
|
||||
\end{itemize}
|
||||
|
||||
|
||||
\item По принципу алгоритма (based on algorithm)
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{Глобальные (Global)}
|
||||
Предполагают знание полной карты окружающей среды. Алгоритм ищет оптимальный путь целиком от старта до цели.
|
||||
Пример: A*, Дейкстра, волновой алгоритм (Wavefront).
|
||||
|
||||
\item \textbf{Локальные (Local)}
|
||||
Робот строит путь только на основе данных с датчиков «здесь и сейчас». Такие методы хорошо работают в неизвестных или частично известных средах.
|
||||
Пример: Bug Algorithms, Potential Fields, Dynamic Window Approach.
|
||||
|
||||
\end{itemize}
|
||||
|
||||
\item По полноте поиска (based on completeness)
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{Точные (Exact)}
|
||||
Гарантируют нахождение решения (если оно существует), а также могут обеспечить оптимальность. Но часто требуют много вычислительных ресурсов.
|
||||
Пример: алгоритм Дейкстры, точный A*.
|
||||
|
||||
\item \textbf{Эвристические (Heuristic)}
|
||||
Используют приближённые методы и эвристики для ускорения поиска. Решение находится быстрее, но не всегда оптимальное.
|
||||
Пример: эвристический A*, эволюционные методы (генетические алгоритмы, рой частиц, муравьиные алгоритмы).
|
||||
|
||||
\end{itemize}
|
||||
|
||||
\end{itemize}
|
||||
|
||||
|
||||
|
||||
\newpage
|
||||
\section{Генетические алгоритмы для построения пути}
|
||||
Для решения произвольной задачи с помощью ГА необходимо определить:
|
||||
|
||||
\begin{itemize}
|
||||
\item особь и популяцию;
|
||||
\item генетические операторы;
|
||||
\item фитнесс функцию;
|
||||
\item параметры ГА.
|
||||
\end{itemize}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.5\linewidth]{img/genalg.png}
|
||||
\caption{Составляющие ГА}
|
||||
\label{fig:genalg}
|
||||
\end{figure}
|
||||
|
||||
\subsection{Grid-кодирование пути}
|
||||
|
||||
\subsubsection*{Кодирование хромосом}
|
||||
Рассмотрим часто используемое на практике представление окружения робота в виде «решетки» -- сетки
|
||||
(grid).
|
||||
Для представления окружения используется:
|
||||
\begin{itemize}
|
||||
\item двоичное;
|
||||
\item десятичное;
|
||||
\item упорядоченное кодирование;
|
||||
\end{itemize}
|
||||
показанное на рисунке \ref{fig:grid} (белые клетки--свободное пространство, серые -- препятствия).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.5\linewidth]{img/grid.png}
|
||||
\caption{Grid-кодирование пути}
|
||||
\label{fig:grid}
|
||||
\end{figure}
|
||||
|
||||
Тогда хромосома (особь популяции) представляет потенциальное решение -
|
||||
Путь от начальной точки S (левый верхний угол) до конечной точки- цели T (правый нижний угол)
|
||||
Хромосома (путь робота) содержит начальную и конечную вершины графа, представляющего решетку, а
|
||||
также вершины, пересекаемые роботом
|
||||
Эти вершины (или шаги) называются генами хромосомы.
|
||||
Различные методы кодирования используются для хромосомы, в зависимости от формы представления
|
||||
окружения:
|
||||
|
||||
\newpage
|
||||
\begin{enumerate}
|
||||
\item Двоичное кодирование – содержит двоичные коды номеров строк и столбцов (координаты x,y)
|
||||
«узловых» вершин пути.
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.5\linewidth]{img/binary.png}
|
||||
\caption{Двоичное кодирование}
|
||||
\label{fig:binary}
|
||||
\end{figure}
|
||||
\item Десятичное кодирование – содержит десятичные номера строк и столбцов (координаты x,y)
|
||||
«узловых» вершин пути.
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.45\linewidth]{img/decimal.png}
|
||||
\caption{Десятичное кодирование}
|
||||
\label{fig:decimal}
|
||||
\end{figure}
|
||||
\item Упорядоченное кодирование - содержит номера(в общем порядке ячеек - вершин) проходимых
|
||||
«узловых» вершин пути.
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.4\linewidth]{img/ordered.png}
|
||||
\caption{Упорядоченное кодирование}
|
||||
\label{fig:ordered}
|
||||
\end{figure}
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
\subsubsection*{Инициализация популяции}
|
||||
Выполняется случайно
|
||||
При этом некоторые построенные хромосомы соответствуют нереализуемым путям, которые пересекают
|
||||
препятствия
|
||||
В таблице представлены экспериментальные данные по генерации начальной популяции
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.5\linewidth]{img/table.png}
|
||||
\caption{Таблица экспериментальных данных}
|
||||
\label{fig:table}
|
||||
\end{figure}
|
||||
|
||||
Далее находятся оптимальные или близкие к оптимальным решения (пути роботов), даже если начальная
|
||||
популяция содержит нереализуемые решения
|
||||
|
||||
\subsubsection*{Фитнесс функция}
|
||||
Целью планирования пути является построение пути робота между начальной и конечной точками.
|
||||
Оптимальный путь должен иметь минимальную длину, время и минимальные энергозатраты.
|
||||
Прежде всего, минимизируется длина пути.
|
||||
Поэтому все эволюционные методы построения пути включают в фитнесс-функцию длину пути робота,
|
||||
которая может быть определена следующим образом.
|
||||
|
||||
Здесь $P_i$
|
||||
- i-й ген хромосомы $P$ , $d$ - расстояние между 2-мя узлами.
|
||||
Фитнесс-функция для реализуемых путей определяется как сумма расстояний между узлами пути.
|
||||
Заметим, что для нереализуемых путей (имеющих пересечения с препятствия) фитнесс-функция имеет
|
||||
дополнительную штрафную функцию. Если между узлами встречается препятствие, то в фитнессфункцию добавляется штраф.
|
||||
Для поиска пути робота используется классический ГА.
|
||||
На последнем шаге итерации (поколения) в соответствии с вычисленными значениями фитнесс-функции
|
||||
отбираются хромосомы для производства потомков с помощью генетических операторов кроссинговера
|
||||
и мутации.
|
||||
Для выбора родителей применяется ранговый отбор
|
||||
|
||||
\begin{equation}
|
||||
f =
|
||||
\begin{cases}
|
||||
\displaystyle \sum_{i=1}^{n-1} d(p_i, p_{i+1}), & \text{для допустимых путей (без столкновений)}, \\[1.5ex]
|
||||
\displaystyle \sum_{i=1}^{n-1} d(p_i, p_{i+1}) + \text{штраф}, & \text{для недопустимых путей}.
|
||||
\end{cases}
|
||||
\end{equation}
|
||||
|
||||
\begin{equation}
|
||||
d(p_i, p_{i+1}) = \sqrt{(x_{(i+1)} - x_i)^2 + (y_{(i+1)} - y_i)^2},
|
||||
\end{equation}
|
||||
где $p_i = (x_i, y_i)$ — координаты $i$-й вершины пути.
|
||||
|
||||
|
||||
\subsubsection*{Генетические операторы}
|
||||
Для генерации потомков используется обычный 1-точечный кроссинговер с заданной вероятностью $P_c$.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/crossover.png}
|
||||
\caption{Одноточечный кроссинговер}
|
||||
\label{fig:crossover}
|
||||
\end{figure}
|
||||
|
||||
Пример на Рис.~\ref{fig:crossover} показывает кроссинговер для упорядоченного кодирования хромосом.
|
||||
|
||||
Далее к потомкам (каждому гену) с малой вероятностью $P_m$ применяется оператор мутации, который вносит
|
||||
небольшое изменение в ген в зависимости от способа кодирования.
|
||||
Мутация расширяет пространство поиска и препятствует преждевременной сходимости в локальном
|
||||
экстремуме
|
||||
|
||||
\subsubsection*{Эксперименты}
|
||||
|
||||
\textbf{Эксперимент 1.} Построение пути проводилось в следующем окружении (сетка 16х16) с 8
|
||||
препятствиями с использованием указанных выше методов кодирования хромосом.
|
||||
|
||||
Параметры ГА:
|
||||
\begin{itemize}
|
||||
\item Мощность популяции $N=60$;
|
||||
\item Вероятности: кроссинговера $P_c=1.0$, мутации $P_m=0.1$
|
||||
\end{itemize}
|
||||
После 10 запусков ГА для каждого метода получены средние значения расстояния, число поколений и
|
||||
время решения, которые представлены на
|
||||
Рис.~\ref{fig:table1}.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.45\linewidth]{img/experiment1.png}
|
||||
\caption{Эксперимент 1}
|
||||
\label{fig:experiment1}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.8\linewidth]{img/table1.png}
|
||||
\caption{Таблица эксперимента 1}
|
||||
\label{fig:table1}
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
\textbf{Эксперимент 2.} Эксперимент 2 для окружения с 7 препятствиями. Параметры ГА те же.
|
||||
|
||||
Данные для различных методов кодирования представлены на рисунке \ref{fig:table3}. Видно, что десятичное и упорядоченное кодирование показывают лучшие результаты.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.45\linewidth]{img/experiment2.png}
|
||||
\caption{Эксперимент 2}
|
||||
\label{fig:experiment2}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.8\linewidth]{img/table2.png}
|
||||
\caption{Таблица эксперимента 2}
|
||||
\label{fig:table2}
|
||||
\end{figure}
|
||||
|
||||
|
||||
|
||||
|
||||
\subsection{Угловое кодирование пути}
|
||||
|
||||
\subsubsection*{Кодирование хромосом}
|
||||
|
||||
Для кодирования пути робота эволюционные методы используют различные методы.
|
||||
Альтернативным рассмотренному ранее кодированию (узлов) вершин проходимого пути,
|
||||
является кодирование на основе углов направлений движения робота.
|
||||
Это можно сделать по-разному (с различной точностью).
|
||||
На Рис.~\ref{fig:angle} представлены 2 варианта кодирования направлений движения:
|
||||
|
||||
а) 4 направления - робот может передвигаться по горизонталям и вертикалям.
|
||||
|
||||
б) 8 направлений- робот может передвигаться также по диагоналям.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/angle.png}
|
||||
\caption{Для кодирования направлений используются целые числа:\\ а) (0,1,2,3), либо
|
||||
б) (0,1,2,3,4,5,6,7)}
|
||||
\label{fig:angle}
|
||||
\end{figure}
|
||||
|
||||
|
||||
Тогда каждый ген в хромосоме представляет направление движения робота на следующем
|
||||
шаге.
|
||||
|
||||
В этом случае хромосома (см. Рис.~\ref{fig:angle_chromosome}) представляет «коллекцию» направлений движения робота в каждой
|
||||
точке, начиная от старта, и кончая финалом.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.9\linewidth]{img/angle_chromosome.png}
|
||||
\caption{Хромосома с угловым кодированием пути. D – целое число, определяющее направление движения на следующем шаге.}
|
||||
\label{fig:angle_chromosome}
|
||||
\end{figure}
|
||||
|
||||
\subsubsection*{Фитнесс-функция}
|
||||
|
||||
Фитнесс-функция в задаче планирования пути робота определяется следующим образом:
|
||||
|
||||
\begin{equation}
|
||||
\text{Cost}(P) = w_1 D + w_2 O + w_3 C,
|
||||
\end{equation}
|
||||
|
||||
где:
|
||||
\begin{itemize}
|
||||
\item $w_1, w_2, w_3$ -- коэффициенты (константы), задающие вклад каждой компоненты;
|
||||
\item $D$ -- расстояние (длина) пути;
|
||||
\item $O$ -- число пересекаемых ячеек препятствий;
|
||||
\item $C$ -- число изменений направлений движения.
|
||||
\end{itemize}
|
||||
|
||||
Особь (маршрут) с минимальным значением фитнесс-функции соответствует оптимальному пути.
|
||||
Очевидно, что увеличение числа изменений направления приводит к увеличению времени движения,
|
||||
а каждое пересечение препятствия сопровождается значительным штрафом.
|
||||
|
||||
|
||||
\subsubsection*{Эксперименты}
|
||||
|
||||
\textbf{Эксперимент 1}. Метод апробирован на различного типа препятствиях с разным числом направлений движений (4 или 8).
|
||||
Начальная точка -- (0,0). Конечная точка -- (15, 9).
|
||||
В 1-м эксперименте использовались только статические препятствия.
|
||||
|
||||
На Рис.~\ref{fig:angle_experiment1}-\ref{fig:angle_experiment2} представлены результаты для 2- вариантов направлений движений(4 или 8), которые показывают способность ГА решать данную проблему.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/angle_experiment1.png}
|
||||
\caption{Загроможденные препятствия. \\ (a) 4 направления, (b) 8 направлений}
|
||||
\label{fig:angle_experiment1}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/angle_experiment2.png}
|
||||
\caption{Региональные препятствия. \\ (c) 4 направления, (d) 8 направлений}
|
||||
\label{fig:angle_experiment2}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/angle_experiment3.png}
|
||||
\caption{Препятствия окружают конечную точку. \\ (e) 4 направления, (f) 8 направлений}
|
||||
\label{fig:angle_experiment3}
|
||||
\end{figure}
|
||||
|
||||
|
||||
\newpage
|
||||
\textbf{Эксперимент 2.} Динамическую систему поиска пути можно разделить на две компоненты.
|
||||
|
||||
\begin{itemize}
|
||||
\item Ищем маршрут по заранее заданной карте ещё до запуска робота (offline компонента).
|
||||
\item Если робот встретил новые препятствия, которых не было в исходной карте, то запускается новый алгоритм поиска с актуальной точки (online компонента).
|
||||
\end{itemize}
|
||||
|
||||
Сначал генетический алгоритм был запущен для статической среды (см.~Рис.~\ref{fig:static}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/static.png}
|
||||
\caption{Запуск для статической среды (offline компонента)}
|
||||
\label{fig:static}
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
И далее добавлялись (случайно) 1 препятствие (см. Рис.~\ref{fig:obstacle1}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/obstacle1.png}
|
||||
\caption{Обновлённый путь после добавления ещё одного препятствия.}
|
||||
\label{fig:obstacle1}
|
||||
\end{figure}
|
||||
|
||||
И ещё одно препятствие (см. Рис.~\ref{fig:obstacle2}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/obstacle2.png}
|
||||
\caption{Обновлённый путь после добавления второго препятствия.}
|
||||
\label{fig:obstacle2}
|
||||
\end{figure}
|
||||
|
||||
|
||||
|
||||
\newpage
|
||||
% ---------- ЗАКЛЮЧЕНИЕ ----------
|
||||
\section*{Заключение}
|
||||
\addcontentsline{toc}{section}{Заключение}
|
||||
|
||||
В работе рассмотрено применение генетических алгоритмов к задаче планирования пути робота на дискретной карте и в форме углового (направленного) кодирования. Показано, что:
|
||||
\begin{enumerate}
|
||||
\item Для grid-представления десятичное и упорядоченное кодирование обеспечивают в среднем более короткие пути и/или меньшее время поиска по сравнению с двоичным кодированием при сопоставимых параметрах ГА (\(N=60\), \(P_c=1.0\), \(P_m=0.1\)).
|
||||
\item Угловое кодирование с 8 направлениями, как правило, даёт более гибкие и короткие траектории, чем с 4 направлениями, однако требует контроля числа поворотов (штраф \(C\)) для предотвращения «ломаных» маршрутов.
|
||||
\item В динамическом сценарии поэтапное появление препятствий эффективно обрабатывается за счёт online-перепланирования: повторный запуск ГА из текущего положения восстанавливает коллизие-свободный путь без полной переразметки карты.
|
||||
\end{enumerate}
|
||||
|
||||
Ограничения подхода включают чувствительность к настройкам операторов и вероятностей, риск преждевременной сходимости и вычислительные затраты при частых проверках коллизий.
|
||||
|
||||
ГА представляют собой практичный и переносимый инструмент планирования пути в условиях неопределённости и динамики, при этом выбор кодирования и архитектуры фитнесс-функции критически влияет на компромисс между качеством траектории и затратами вычислений.
|
||||
|
||||
|
||||
|
||||
\newpage
|
||||
\section*{Список литературы}
|
||||
\addcontentsline{toc}{section}{Список литературы}
|
||||
|
||||
\vspace{-1.5cm}
|
||||
\begin{thebibliography}{0}
|
||||
\bibitem{skobtsov} М.В. Прохоров, Ю.А. Скобцов. Многокритериальный генетический алгоритм построения пути робота в сложной среде. — Санкт-Петербург: СПбПУ, 2016. — 36с.
|
||||
|
||||
\bibitem{matveeva} Матвеева А.В. Оптимизация построения маршрута с помощью генетического алгоритма // Финансовый университет при Правительстве Российской Федерации, 2022. — С. 10–12.
|
||||
\end{thebibliography}
|
||||
|
||||
\end{document}
|
||||