Skip to content

Local Search - Tìm Kiếm Cục Bộ

Tác giả: FPTOJ Team
Nội dung tham khảo từ: CP-Algorithms - Local Search


1. Bản chất vấn đề

Ý tưởng

Bắt đầu từ 1 nghiệm bất kỳ. Lặp lại: di chuyển đến nghiệm "láng giềng" tốt hơn cho đến khi không cải thiện được.

Ứng dụng

Bài toán Local Search
TSP xấp xỉ Hill Climbing, Simulated Annealing
Max-Cut Kernighan-Lin
SAT WalkSAT
Tối ưu hàm Gradient Descent

2. Tư duy cốt lõi

Hill Climbing

1
2
3
4
5
6
7
8
current = nghiệm bất kỳ
repeat:
    neighbor = nghiệm láng giềng tốt nhất của current
    if neighbor tốt hơn current:
        current = neighbor
    else:
        dừng
return current

Simulated Annealing

Giống Hill Climbing nhưng có xác suất chấp nghiệm tệ hơn (để thoát local optimum).

\[P(\text{accept}) = e^{\frac{\text{score}_{\text{current}} - \text{score}_{\text{neighbor}}}{T}}\]

\(T\) (nhiệt độ) giảm dần theo thời gian.

Trace: TSP bằng Hill Climbing

4 thành phố, khoảng cách:

A B C D
A 0 10 15 20
B 10 0 35 25
C 15 35 0 30
D 20 25 30 0

Nghiệm ban đầu: \(A \to B \to C \to D \to A\), tổng = \(10 + 35 + 30 + 20 = 95\)

Láng giềng (đổi 2 đỉnh):

Đổi Nghiệm mới Tổng Cải thiện?
\((B,C)\) \(A \to C \to B \to D \to A\) \(15 + 35 + 25 + 20 = 95\) Không
\((B,D)\) \(A \to D \to C \to B \to A\) \(20 + 30 + 35 + 10 = 95\) Không
\((C,D)\) \(A \to B \to D \to C \to A\) \(10 + 25 + 30 + 15 = 80\) Có!

Nghiệm mới: \(A \to B \to D \to C \to A\), tổng = 80.

Tiếp tục lặp cho đến khi không cải thiện.


3. Đánh giá độ phức tạp

Thuật toán Thời gian Chất lượng
Hill Climbing \(O(\text{iterations} \times N)\) Local optimum
Simulated Annealing \(O(\text{iterations} \times N)\) Gần global optimum
Kernighan-Lin \(O(N^2 \log N)\) Cải thiện 2-opt

Code minh họa

Simulated Annealing cho TSP

#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    cin >> n;

    vector<vector<int>> dist(n, vector<int>(n));
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            cin >> dist[i][j];

    // Nghiệm ban đầu
    vector<int> tour(n);
    iota(tour.begin(), tour.end(), 0);

    auto calc = [&](vector<int>& t) {
        int s = 0;
        for (int i = 0; i < n; i++)
            s += dist[t[i]][t[(i + 1) % n]];
        return s;
    };

    int best = calc(tour);
    mt19937 rng(42);
    double T = 1000.0;

    for (int iter = 0; iter < 100000; iter++) {
        int i = rng() % n, j = rng() % n;
        if (i == j) continue;

        swap(tour[i], tour[j]);
        int cur = calc(tour);

        if (cur < best || exp((best - cur) / T) > (double)rng() / rng.max()) {
            best = cur;
        } else {
            swap(tour[i], tour[j]); // hoàn tác
        }

        T *= 0.9999; // giảm nhiệt
    }

    cout << best << "\n";
    return 0;
}
import random
import math

n = int(input())
dist = [list(map(int, input().split())) for _ in range(n)]

tour = list(range(n))

def calc(t):
    return sum(dist[t[i]][t[(i + 1) % n]] for i in range(n))

best = calc(tour)
T = 1000.0
random.seed(42)

for _ in range(100000):
    i, j = random.randint(0, n - 1), random.randint(0, n - 1)
    if i == j:
        continue
    tour[i], tour[j] = tour[j], tour[i]
    cur = calc(tour)

    if cur < best or math.exp((best - cur) / T) > random.random():
        best = cur
    else:
        tour[i], tour[j] = tour[j], tour[i]

    T *= 0.9999

print(best)

💬 Bình luận