Біологія · Алгоритми
📅 Березень 2026 ⏱ ≈ 12 хв читання 🎯 Середньо

АКО: Мурашина оптимізація — від стигмергії до задачі комівояжера

Мурахи не мають центрального керівника — і все одно знаходять короткі шляхи. Секрет у феромонах: хімічному слідкуванні, яке самоорганізує колонію. Дізнайтесь, як перетворити цей природний процес на потужний алгоритм оптимізації.

1. Стигмергія та колективний інтелект

У 1959 році ентомолог П'єр-Поль Грасс ввів термін стигмергія — координація через середовище. Мурахи не передають команди напряму: кожна лишає феромоновий слід, і наступна мурашка реагує на накопичені сліди всієї колонії. Так виникає «розподілена пам'ять» без центрального сервера.

Класичний дослід: між харчуванням і гніздом ставлять перешкоду з двома обходами — коротким і довгим. Спочатку мурахи розходяться рівномірно. Але ті, хто зайшов коротким шляхом, повертаються швидше й кладуть більше феромону. Решта починає надавати перевагу короткому маршруту — позитивний зворотній зв'язок підсилює фаворита до тих пір, поки практично весь трафік іде по оптимальному шляху.

Ключова властивість ACO: алгоритм не шукає глобальний оптимум безпосередньо — він накопичує досвід популяції агентів. Це дозволяє знаходити хороші розв'язки у комбінаторних просторах, де перебір неможливий.

2. Формалізація: граф та задача комівояжера

Найкласичніша задача для ACO — Задача Комівояжера (TSP): знайти найкоротший гамільтонів цикл у повному графі з n міст. Кількість можливих маршрутів — (n−1)!/2: для 20 міст це ~6×10¹⁶. Перебір неможливий.

Модель графа:

d[i][j] = √((xᵢ − xⱼ)² + (yᵢ − yⱼ)²)
η[i][j] = 1 / d[i][j] ← видимість (heuristic)
τ[i][j] = τ₀ ← початковий феромон (однаковий)

Один цикл алгоритму складається з трьох фаз: побудова рішень мурахами → оцінка якості (довжина маршруту) → оновлення феромонів.

3. Феромонний слід: оновлення τ

Після того як усі m мурах побудували свої маршрути, феромони оновлюються двома кроками:

Крок 1 — евапорація (випаровування)

τ[i][j] ← (1 − ρ) · τ[i][j]
ρ ∈ (0,1] — швидкість випаровування (evaporation rate)

Евапорація запобігає нескінченному накопиченню феромону та дозволяє алгоритму «забувати» старі погані рішення. Типово ρ = 0.1.

Крок 2 — відкладення

τ[i][j] ← τ[i][j] + Σₖ Δτₖ[i][j]

Δτₖ[i][j] = Q / Lₖ якщо мурашка k пройшла ребро (i,j)
Δτₖ[i][j] = 0 інакше
Q — константа (напр. 100), Lₖ — довжина маршруту k-ї мурашки

Мурашки, що знайшли короткий маршрут (малий Lₖ), відкладають більше феромону на свої ребра — ці ребра стають привабливішими для наступного покоління.

Варіант Elite ACO: ще раз додати Δτ для найкращого знайденого маршруту (з більшим коефіцієнтом e·Q/L*), незалежно від поточної ітерації.

4. Ймовірнісний вибір ребра

Коли мурашка знаходиться у місті i і обирає наступне місто j, вона використовує правило пропорційного вибору (Ant System, Dorigo 1992):

p[i][j] = (τ[i][j]ᵅ · η[i][j]ᵝ) / Σₗ∈allowed (τ[i][l]ᵅ · η[i][l]ᵝ)

α ≥ 0 — вага феромону
β ≥ 0 — вага видимості (heuristic weight)
allowed = множина ще не відвіданих міст

Типові значення: α = 1, β = 5. При α = 0 — чиста жадібність за відстанню; при β = 0 — довільний пошук, керований лише феромоном.

ПараметрМалийВеликийРекомендовано
α (феромон)менше впливу минулогожадібне слідування слідам1
β (відстань)менше жадібностімайже Nearest-Neighbor2–5
ρ (evaporation)повільне забуттянестабільний, без пам'яті0.05–0.2
m (мурахи)слабка паралельністьповільна конвергенціяn/2 … n

Щоб обрати місто за ймовірністним вектором, використовують метод рулетки: генерують U(0,1), зсуваються по накопиченій сумі до першого j, де сума ≥ U.

5. Евапорація та насичення

Без евапорації феромони накопичуються нескінченно: перший знайдений маршрут залучає всіх мурах, алгоритм застряє у локальному оптимумі. Евапорація з параметром ρ вирішує це:

Баланс між exploration (дослідженням) та exploitation (використанням): мале ρ → довга пам'ять, більша ρ → швидке адаптування, але нестабільність.

Типова помилка: занадто мала кількість мурах (m < 5) при великому ρ. Феромонні сліди зникають швидше, ніж встигають оновитися, — алгоритм деградує до випадкового пошуку.

6. Min-Max ACO

Класичний Ant System має тенденцію до стагнації: через кілька сотень ітерацій усі мурахи йдуть одним маршрутом, дослідження зупиняється. MAX-MIN Ant System (Stützle & Hoos, 2000) вирішує проблему явними обмеженнями:

τ_min ≤ τ[i][j] ≤ τ_max

τ_max ≈ 1 / (ρ · L*) ← де L* — найкраша відома довжина
τ_min ≈ τ_max / (2 · n) ← емпірична формула

Додаткове правило: лише найкраща мурашка (за ітерацію чи глобально) відкладає феромон. Це зменшує шум від поганих рішень.

Перевага Min-Max

7. JavaScript реалізація

Реалізація ACO для TSP: n міст, m мурах, iterations ітерацій. Масиви tau (феромони) та eta (видимість) зберігаються як плоскі Float64Array.

function acoTSP(cities, { m = 20, alpha = 1, beta = 5,
                              rho = 0.1, Q = 100, iterations = 200 } = {}) {
  const n = cities.length;
  const idx = (i, j) => i * n + j;

  // Відстані та видимість
  const dist = new Float64Array(n * n);
  const eta  = new Float64Array(n * n);
  for (let i = 0; i < n; i++) {
    for (let j = 0; j < n; j++) {
      if (i === j) continue;
      const dx = cities[i].x - cities[j].x;
      const dy = cities[i].y - cities[j].y;
      dist[idx(i,j)] = Math.sqrt(dx*dx + dy*dy);
      eta[idx(i,j)]  = 1 / dist[idx(i,j)];
    }
  }

  // Ініціалізація феромонів
  const tau0 = 1 / n;
  const tau  = new Float64Array(n * n).fill(tau0);

  let bestTour = null, bestLen = Infinity;

  for (let iter = 0; iter < iterations; iter++) {
    const allTours = [];

    // Кожна мурашка будує маршрут
    for (let k = 0; k < m; k++) {
      const visited = new Uint8Array(n);
      const tour = [Math.floor(Math.random() * n)];
      visited[tour[0]] = 1;

      while (tour.length < n) {
        const cur = tour[tour.length - 1];
        const probs = [];
        let   sum   = 0;

        for (let j = 0; j < n; j++) {
          if (visited[j]) { probs.push(0); continue; }
          const v = Math.pow(tau[idx(cur,j)], alpha)
                  * Math.pow(eta[idx(cur,j)], beta);
          probs.push(v); sum += v;
        }

        // Рулетка
        let r = Math.random() * sum, acc = 0, next = -1;
        for (let j = 0; j < n; j++) {
          if (visited[j]) continue;
          acc += probs[j];
          if (acc >= r) { next = j; break; }
        }
        if (next === -1) next = probs.findIndex((v,j)=>!visited[j]);
        tour.push(next); visited[next] = 1;
      }

      // Довжина маршруту
      let len = 0;
      for (let i = 0; i < n; i++)
        len += dist[idx(tour[i], tour[(i+1) % n])];
      allTours.push({ tour, len });
      if (len < bestLen) { bestLen = len; bestTour = [...tour]; }
    }

    // Евапорація
    for (let i = 0; i < tau.length; i++) tau[i] *= (1 - rho);

    // Відкладення феромонів
    for (const { tour, len } of allTours) {
      const deposit = Q / len;
      for (let i = 0; i < n; i++) {
        const a = tour[i], b = tour[(i+1) % n];
        tau[idx(a,b)] += deposit;
        tau[idx(b,a)] += deposit; // симетричний граф
      }
    }
  }

  return { tour: bestTour, length: bestLen };
}

Для 50 міст на 200 ітерацій з m = 25 мурахами функція знаходить маршрут протягом <100 мс. При збільшенні n > 200 доцільно обмежити список кандидатів (nearest neighbor list) розміром 20–30.

8. Застосування ACO

ЗадачаОписРезультат
TSP Оптимальний обхід міст ≤5 % від оптимуму для 200+ міст
VRP Vehicle Routing: розвезення вантажів Практичне застосування у логістиці
Job Shop Планування задач на процесорному кластері Конкурентний із генетичними алг.
Routing Маршрутизація пакетів у мережі AntNet — адаптивний маршрутизатор
Graph coloring Розфарбування графа мінімальною кількістю кольорів Компетентний на розріджених графах

АКО особливо корисний там, де динамічне середовище: якщо граф змінюється (ребра з'являються або зникають), мурашиний алгоритм може адаптуватися в реальному часі — феромони «пам'ятають» актуальний стан.

9. Розширення та варіанти

🐜 Симуляція мурах

Подивіться, як мурашина колонія самоорганізує феромонні маршрути у реальному часі.

Відкрити симуляцію →