← Back Repo →

Solving 3-SAT: from brute force to local heuristics (GSAT, WalkSAT, ILS)

3-SATlocal searchheuristicsGSATWalkSAT

TLDR


Motivation

3-SAT is one of the classic combinatorial optimization problems: easy to explain, but it quickly becomes computationally hard. I like it as a testbed for different search strategies:

This project was a practical way for me to connect theory (NP-completeness, search space) with implementation and experimentation.


Problem

In 3-SAT, we have boolean variables and a set of clauses, where each clause is a disjunction of exactly three literals (e.g. (x1 ∨ ¬x4 ∨ x7)).

Goal: find an assignment x ∈ {0,1}^n such that all clauses are satisfied.

This is an NP-complete problem → brute force works as a baseline for small instances, but scaling quickly becomes impossible.


Input format and evaluation

Input instances are in DIMACS CNF format (e.g. uf20-01.cnf).

Internally, I parsed clauses into a list of literals in the form (variable_index, required_value), where:

Fitness (score) in most algorithms is:


Approach and algorithms

1) Brute-force search (baseline)

It iterates over all assignments 2^n and checks whether the formula satisfies all clauses. As the name suggests, this is exhaustive and exponential. An example where an exact method stops making sense (for n=20 there are already over a million possibilities), so we have to turn to heuristics.

I used it as a reference and a sanity check for small instances.


2) Hill climbing

I start from a random assignment and look at the neighborhood obtained by flipping one variable.

This approach can be fast, but it’s sensitive to local optima.


3) “Weighted” hill climbing (dynamic focus on hard clauses)

This is a hill-climbing variant where fitness is not just “number of satisfied clauses”, but includes a correction that favors satisfying clauses that are often problematic.

Intuition:


4) GSAT

A classic local SAT algorithm:

GSAT is often a good compromise: simple, but much more robust than pure hill climbing.


5) WalkSAT (random walk + greedy)

This is one of my favorite heuristics because it clearly shows why “a bit of noise” helps:

This gives the algorithm a mechanism to escape local optima without overcomplicating things.


6) Iterated Local Search (ILS)

Idea:

  1. run local search (hill climbing) → get a locally good solution
  2. apply a perturbation (flip some variables)
  3. run local search again
  4. if it’s better, accept it (or keep the best)

This is basically hill climbing + a controlled reset, and it often works surprisingly well.


Results (uf50-010)

I ran each algorithm 20 times (different random starts). The table shows wall-clock time for a single run.

AlgorithmSuccessMedian (s)Average (s)Min-Max (s)
Hill climbing6/2030.0537.540.37-92.29
Weighted hill20/2013.2116.680.22-64.04
GSAT20/2018.6825.790.97-96.67
WalkSAT20/201.472.500.09-13.05
ILS20/208.6442.641.02-246.29

Note for hill climbing: when it succeeds, the median is 4.56 s; when it fails, the median is 56.51 s.

Observations

An additional detail: the found assignments differ (there are multiple satisfying solutions), which is visible from different 50-bit outputs across runs.


Conclusion

For this instance and this implementation, WalkSAT is the most practical choice for me: fast and stable across repetitions. Hill climbing without a mechanism to escape local optima isn’t reliable enough, while ILS can be great, but needs tuning to avoid extreme “slow” cases.

If I expand this further, my next steps would be:

← Back Repo →