Перейти к содержанию

Эвристические алгоритмы#

Презентация Лекция 11

Муравьиный алгоритм#

Применение к задачи коммивояжера#

import random
import math
import matplotlib.pyplot as plt

# -------------------------------
NUM_CITIES = 20
NUM_ANTS = 50
ALPHA = 1.0        # Влияние феромона
BETA = 5.0         # Влияние расстояния
EVAPORATION = 0.5  # Коэффициент испарения
Q = 100            # Интенсивность феромона
GENERATIONS = 100
# -------------------------------

# Генерация случайных координат городов
cities = [(random.uniform(0, 100), random.uniform(0, 100)) for _ in range(NUM_CITIES)]

# Расстояния между городами
distance_matrix = [[math.hypot(x1 - x2, y1 - y2) for x2, y2 in cities] for x1, y1 in cities]

# Начальные феромоны
pheromone = [[1.0 for _ in range(NUM_CITIES)] for _ in range(NUM_CITIES)]

# Выбор следующего города по вероятностной формуле
def select_next_city(ant_path, current_city):
    probabilities = []
    for city in range(NUM_CITIES):
        if city in ant_path:
            probabilities.append(0)
        else:
            tau = pheromone[current_city][city] ** ALPHA
            eta = (1 / distance_matrix[current_city][city]) ** BETA
            probabilities.append(tau * eta)
    total = sum(probabilities)
    if total == 0:
        return random.choice([c for c in range(NUM_CITIES) if c not in ant_path])
    probabilities = [p / total for p in probabilities]
    return random.choices(range(NUM_CITIES), weights=probabilities)[0]

# Основной цикл
best_path = None
best_length = float('inf')

for gen in range(GENERATIONS):
    all_paths = []
    all_lengths = []

    for _ in range(NUM_ANTS):
        path = [random.randint(0, NUM_CITIES - 1)]
        while len(path) < NUM_CITIES:
            path.append(select_next_city(path, path[-1]))
        path_length = sum(distance_matrix[path[i]][path[(i+1)%NUM_CITIES]] for i in range(NUM_CITIES))
        all_paths.append(path)
        all_lengths.append(path_length)

        if path_length < best_length:
            best_length = path_length
            best_path = path

    # Испарение феромона
    for i in range(NUM_CITIES):
        for j in range(NUM_CITIES):
            pheromone[i][j] *= (1 - EVAPORATION)

    # Обновление феромона
    for path, length in zip(all_paths, all_lengths):
        for i in range(NUM_CITIES):
            a, b = path[i], path[(i + 1) % NUM_CITIES]
            pheromone[a][b] += Q / length
            pheromone[b][a] += Q / length

    print(f"Generation {gen}: shortest path = {best_length:.2f}")

# Визуализация лучшего маршрута
def plot_route(route):
    x = [cities[i][0] for i in route] + [cities[route[0]][0]]
    y = [cities[i][1] for i in route] + [cities[route[0]][1]]
    plt.figure(figsize=(10, 6))
    plt.plot(x, y, marker='o')
    plt.title("Лучший найденный маршрут (ACO)")
    plt.show()

plot_route(best_path)

Генетический алгоритм#

import random
import string

# Целевая строка
TARGET = "Hello world!"
# Размер популяции
POPULATION_SIZE = 100
# Вероятность мутации
MUTATION_RATE = 0.01
# Символы, которые могут использоваться
CHARS = string.printable

def random_string(length):
    return ''.join(random.choice(CHARS) for _ in range(length))

def fitness(individual):
    return sum(1 for expected, actual in zip(TARGET, individual) if expected == actual)

def mutate(individual):
    return ''.join(
        c if random.random() > MUTATION_RATE else random.choice(CHARS)
        for c in individual
    )

def crossover(parent1, parent2):
    split = random.randint(0, len(TARGET))
    return parent1[:split] + parent2[split:]

# Инициализация популяции
population = [random_string(len(TARGET)) for _ in range(POPULATION_SIZE)]

generation = 0
while True:
    # Оценка приспособленности
    population = sorted(population, key=fitness, reverse=True)
    best = population[0]
    print(f"Gen {generation}: {best} (fitness: {fitness(best)})")

    if best == TARGET:
        break

    # Отбор лучших и создание новой популяции
    new_population = population[:2]  # Элитизм: сохранить лучших
    while len(new_population) < POPULATION_SIZE:
        parents = random.choices(population[:50], k=2)  # Скрещивать лучших
        child = mutate(crossover(*parents))
        new_population.append(child)

    population = new_population
    generation += 1

Применение к задачи коммивояжера#

import random
import math
import matplotlib.pyplot as plt

# ------------------------------
# Настройки задачи
NUM_CITIES = 20
POPULATION_SIZE = 100
GENERATIONS = 500
MUTATION_RATE = 0.01
# ------------------------------

# Генерация случайных координат городов
cities = [(random.uniform(0, 100), random.uniform(0, 100)) for _ in range(NUM_CITIES)]

def distance(a, b):
    return math.hypot(a[0] - b[0], a[1] - b[1])

def total_distance(tour):
    return sum(distance(cities[tour[i]], cities[tour[(i+1)%NUM_CITIES]]) for i in range(NUM_CITIES))

def create_route():
    route = list(range(NUM_CITIES))
    random.shuffle(route)
    return route

def mutate(route):
    if random.random() < MUTATION_RATE:
        i, j = random.sample(range(NUM_CITIES), 2)
        route[i], route[j] = route[j], route[i]
    return route

def crossover(parent1, parent2):
    start, end = sorted(random.sample(range(NUM_CITIES), 2))
    child = [None] * NUM_CITIES
    child[start:end] = parent1[start:end]
    ptr = 0
    for city in parent2:
        if city not in child:
            while child[ptr] is not None:
                ptr += 1
            child[ptr] = city
    return child

# Инициализация популяции
population = [create_route() for _ in range(POPULATION_SIZE)]

# Основной цикл
for gen in range(GENERATIONS):
    population.sort(key=total_distance)
    best = population[0]
    print(f"Gen {gen}, Distance: {total_distance(best):.2f}")

    # Сохранение лучших и создание потомков
    new_population = population[:10]
    while len(new_population) < POPULATION_SIZE:
        parents = random.choices(population[:50], k=2)
        child = mutate(crossover(*parents))
        new_population.append(child)
    population = new_population

# Визуализация лучшего маршрута
def plot_route(route):
    x = [cities[i][0] for i in route] + [cities[route[0]][0]]
    y = [cities[i][1] for i in route] + [cities[route[0]][1]]
    plt.figure(figsize=(10, 6))
    plt.plot(x, y, marker='o')
    plt.title("Лучший найденный маршрут")
    plt.show()

plot_route(best)

Метод имитации отжига#

Список источников#