AI
Artificial Intelligence (IF1706)
FSTT • ISTN Jakarta • Semester Ganjil 2025/2026
Sesi 5 – Constraint Satisfaction Problem (CSP)

CSP memformalkan masalah sebagai Variabel dengan Domain nilai yang diperbolehkan dan Constraint yang harus dipenuhi. Keunggulan CSP ada pada propagasi constraint sehingga banyak kombinasi gugur sebelum dicoba. Kita mulai dari definisi inti, lalu praktik: Map Coloring dan Sudoku.

Ulasan Materi: Definisi, Intuisi, dan Contoh

Definisi Inti

  • Variabel: besaran yang ingin kita tentukan nilainya (mis. region pada peta, sel Sudoku).
  • Domain: himpunan nilai yang mungkin untuk variabel (mis. {Merah,Hijau,Biru} atau {1..9}).
  • Constraint: aturan yang membatasi kombinasi nilai (mis. tetangga ≠ warna yang sama; baris/kolom/kotak Sudoku berisi 1..9 unik).

Tujuan: temukan penugasan nilai untuk semua variabel sehingga semua constraint terpenuhi.

Mengapa CSP Efisien?

Mengapa CSP bisa jauh lebih efisien?

Bayangkan ada 6 variabel, masing-masing punya 3 nilai → ruang kombinasi 3^6 = 729.
Dengan constraint yang ketat (mis. perpasangan harus berbeda warna), domain sering terpangkas:
- Setelah menetapkan 1 variabel, domain tetangga ikut menyusut (forward checking).
- Jika setiap variabel menyusut rata-rata dari 3 → 2 nilai, ruang efektif jadi 2^6 = 64 saja.
Intinya: propagasi constraint memangkas cabang sebelum dicoba (pruning).

Studi Kasus Ringkas

  • Map Coloring: tiap provinsi adalah variabel, domain warna {R,G,B}. Constraint: provinsi bertetangga tidak boleh sama warna.
  • Sudoku: 81 sel adalah variabel, domain {1..9}. Constraint: setiap baris, kolom, dan kotak 3×3 berisi angka unik 1..9.
Lab: Map Coloring & Sudoku
A. Map Coloring (Backtracking + MRV + Forward Checking)
# ====== CSP: Map Coloring (Backtracking + MRV + Forward Checking) ======
# Tujuan: warnai peta (graf) sehingga tetangga tidak punya warna yang sama.

from collections import defaultdict, deque

# 1) Definisikan graf peta (contoh Australia, 7 region)
regions = ["WA","NT","SA","Q","NSW","V","T"]
adj = {
    "WA":["NT","SA"],
    "NT":["WA","SA","Q"],
    "SA":["WA","NT","Q","NSW","V"],
    "Q":["NT","SA","NSW"],
    "NSW":["Q","SA","V"],
    "V":["SA","NSW"],
    "T":[],
}
colors = ["Red","Green","Blue"]

# 2) Domain awal: semua region bisa pakai semua warna
initial_domains = {r:set(colors) for r in regions}

# 3) Heuristik MRV (Minimum Remaining Values)

def select_unassigned_var(assignment, domains):
    candidates = [v for v in regions if v not in assignment]
    # pilih variabel dengan domain tersisa paling kecil (MRV)
    return min(candidates, key=lambda v: len(domains[v]))

# 4) Konsistensi biner: tetangga ≠ warna yang sama

def consistent(var, val, assignment):
    for n in adj[var]:
        if n in assignment and assignment[n] == val:
            return False
    return True

# 5) Forward checking: pangkas domain tetangga

def forward_check(var, val, domains):
    removed = []
    for n in adj[var]:
        if val in domains[n]:
            domains[n] = domains[n].copy()
            domains[n].discard(val)
            removed.append((n,val))
            if not domains[n]:
                return False, removed
    return True, removed

# 6) Backtracking search

def backtrack(assignment, domains):
    if len(assignment) == len(regions):
        return assignment
    var = select_unassigned_var(assignment, domains)
    for val in list(domains[var]):
        if consistent(var, val, assignment):
            assignment[var] = val
            # Simpan snapshot domain
            snapshot = {v:domains[v].copy() for v in regions}
            ok, removed = forward_check(var, val, domains)
            if ok:
                result = backtrack(assignment, domains)
                if result:
                    return result
            # rollback
            assignment.pop(var, None)
            domains = snapshot
    return None

solution = backtrack({}, {v:set(colors) for v in regions})
print("Solusi:", solution)

Coba tambahkan warna ke-4 atau rapatkan constraint (tambah adjacency). Amati dampak pada kecepatan.

B. Sudoku 9×9 (MRV + Forward Checking + AC-3)
# ====== CSP: Sudoku 9x9 (Backtracking + MRV + Forward Checking + AC-3) ======
# Gunakan format string 81 karakter: '0' atau '.' untuk kosong

from collections import defaultdict, deque

rows = "ABCDEFGHI"
cols = "123456789"

def cross(A,B):
    return [a+b for a in A for b in B]

cells = cross(rows, cols)
row_units = [cross(r, cols) for r in rows]
col_units = [cross(rows, c) for c in cols]
box_units = [cross(rs, cs) for rs in ("ABC","DEF","GHI") for cs in ("123","456","789")]
unitlist = row_units + col_units + box_units
units = {s:[u for u in unitlist if s in u] for s in cells}
peers = {s:set(sum(units[s],[]))-set([s]) for s in cells}

# Parser

def parse_grid(grid):
    values = {s:set("123456789") for s in cells}
    for s,ch in zip(cells, grid):
        if ch in "123456789":
            if not assign(values, s, ch):
                return False
    return values

# Assign dan Eliminate

def assign(values, s, d):
    other = values[s]-set(d)
    return all(eliminate(values, s, d2) for d2 in other)

def eliminate(values, s, d):
    if d not in values[s]:
        return values
    values[s].remove(d)
    if len(values[s]) == 0:
        return False
    elif len(values[s]) == 1:
        d2 = next(iter(values[s]))
        for s2 in peers[s]:
            if not eliminate(values, s2, d2):
                return False
    for u in units[s]:
        dplaces = [s for s in u if d in values[s]]
        if len(dplaces) == 0:
            return False
        elif len(dplaces) == 1:
            if not assign(values, dplaces[0], d):
                return False
    return values

# AC-3 (Arc Consistency)

def ac3(values):
    queue = deque([(s,t) for s in cells for t in peers[s]])
    while queue:
        (xi,xj) = queue.popleft()
        if revise(values, xi, xj):
            if len(values[xi])==0:
                return False
            for xk in peers[xi]-{xj}:
                queue.append((xk, xi))
    return values

def revise(values, xi, xj):
    revised = False
    if len(values[xi])>1:
        for d in set(values[xi]):
            if values[xj]=={d}:
                values[xi].remove(d)
                revised = True
    return revised

# MRV + Backtracking

def is_solved(values):
    return all(len(values[s])==1 for s in cells)

def select_unassigned_var(values):
    unsettled = [s for s in cells if len(values[s])>1]
    return min(unsettled, key=lambda s: len(values[s])) if unsettled else None


def search(values):
    if values is False:
        return False
    if is_solved(values):
        return values
    s = select_unassigned_var(values)
    for d in list(values[s]):
        values_copy = {k:set(v) for k,v in values.items()}
        res = assign(values_copy, s, d)
        if res is not False:
            ac3(res)
            attempt = search(res)
            if attempt:
                return attempt
    return False

# Contoh Sudoku (mudah)
grid = "530070000600195000098000060800060003400803001700020006060000280000419005000080079"
values = parse_grid(grid)
ac3(values)
sol = search(values)

# Cetak
if sol:
    print("Solved!")
    for r in rows:
        print(" ".join(next(iter(sol[r+c])) for c in cols))
else:
    print("No solution.")

Ganti grid dengan Sudoku lain (format 81-char). Uji pengaruh AC-3 terhadap jumlah percabangan.

C. Kuis 5 (Cek Mandiri)
# ====== Kuis 5 (cek mandiri) ======
qs=[
  ("Komponen CSP yang benar adalah...",{"a":"Variabel, domain, constraint","b":"Data, model, loss","c":"Neuron, bobot, aktivasi","d":"State, operator, reward"},"a"),
  ("Forward checking berfungsi untuk...",{"a":"Mencari jalur terpendek","b":"Memotong domain tetangga setelah assignment","c":"Mengurutkan variabel berdasarkan frekuensi","d":"Mengukur akurasi model"},"b"),
  ("AC-3 memastikan...",{"a":"Semua solusi unik","b":"Semua arc konsisten (domain terpangkas sesuai constraint)","c":"Selalu selesai tanpa backtracking","d":"Heuristik minimal"},"b"),
]
print('Kunci jawaban:')
for i,(_,__,ans) in enumerate(qs,1):
    print(f'Q{i}: {ans}')
Penugasan & Referensi

PR: Buat definisi masalah pilihan Anda (1 halaman) + sketsa state-space/constraint graph. Jelaskan variabel, domain, dan constraint, serta strategi pemangkasan (mis. MRV/forward checking/AC-3) yang menurut Anda cocok.

  • Russell & Norvig (2020), bab Constraint Satisfaction Problems.
  • Dokumentasi/Referensi CSP (algoritma backtracking, MRV, AC-3).