Why Hill Climbing / Random Restart Matters
Suppose a candidate timetable, placement, or configuration has a numerical quality score. You can make small legal changes and evaluate their effects, but enumerating every complete candidate is impractical. Hill climbing keeps one current state and repeatedly replaces it with a better neighbor. It can improve a candidate quickly without storing a large search frontier.
The limitation is local information. A state can beat every immediately available alternative while a much better state lies beyond a valley or a flat shoulder. Random restart starts fresh from another feasible state and keeps the best result found across all attempts. A restart is a new initialization, so its current score can fall even though every accepted local move is uphill.
This lesson implements deterministic steepest-ascent hill climbing with strict improvement and reproducible random restarts. A small integer-indexed landscape makes every score, neighbor comparison, and restart visible. The general pattern applies to implicit configuration spaces with a suitable neighborhood and evaluator; it does not promise a global optimum after a fixed number of attempts.
Improve a feasible current state by choosing its best strictly better neighbor; stop that run when no such neighbor exists or a resource cap is reached. Restart from fresh states to explore other basins, while preserving the highest-scoring evaluated state across every run.
Core Concepts and Terminology
We maximize an objective $f:S\to\mathbb{R}$ over feasible states $S$. A neighborhood $N(x)$ specifies the legal small changes from $x$. “Local” is defined by this neighborhood: changing the move set can change which states count as local maxima. For minimization, reverse the comparisons or maximize a safely computed negative cost.
At a current state $x$, steepest ascent evaluates every candidate in $N(x)$ and chooses a neighbor $y$ with maximum score. It commits the move only if $f(y)\gt f(x)$. With strict improvement, equal-valued candidates do not move the state. A completed unsuccessful scan establishes a weak local maximum:
$$f(x)\ge f(y)\qquad\text{for every }y\in N(x).$$
That includes a plateau point with an equal-valued neighbor; it does not establish an isolated peak or global optimality. A basin of attraction is the set of starting states that lead to the same stopping state under a fixed neighborhood, tie policy, and completed hill-climbing rule.
The runnable adapter uses $S=\{0,1,\ldots,n-1\}$, a table-valued objective, and neighbors $x-1$ and $x+1$ when in range. Local step size is one integer index. In the worked landscape, local peaks at $2$, $6$, and $10$ have scores $5$, $8$, and $10$. Starting at $0$ reaches only the first peak. The saved incumbent is the best valid state evaluated anywhere, which need not be the current state or even a committed path state after an interrupted scan.
| Term | Example | Role |
|---|---|---|
| Objective $f(x)$ | Worked score $f(6)=8$ | Larger is better in this lesson |
| Neighborhood $N(x)$ | $N(4)=\{3,5\}$ | Defines legal local moves and local optimality |
| Steepest ascent | From $4$, compare scores $3$ and $4$, then choose $5$ | Scans all neighbors before committing |
| Strict improvement | Accept $f(5)=4\gt f(4)=1$; reject an equal score | Prevents repeated states within a finite run |
| Weak local maximum | State $1$ in scores $[0,3,3,3,7]$ | No strictly better immediate neighbor; a shoulder may remain |
| Basin of attraction | Worked starts $4,5,6,7,8$ lead to peak $6$ | Determines restart success probability under a start distribution |
| Restart | Seeded sequence chooses $6$, then $10$, then $4$ | Changes the initialization without requiring a local downhill move |
| Incumbent | Keep state $10$ while a later run ends at $6$ | Best evaluated score never decreases across restarts |
| Move cap / evaluation cap | One run can stop before its next scan | Resource exhaustion is not a local-optimality certificate |
| Reproducible sampling | One persistent seeded generator for all restart draws | Makes complete traces identical across languages |
How Hill Climbing / Random Restart Works, Step by Step
- Define feasible states, scores, and neighbor order. The example scans the left neighbor before the right. Candidate selection starts with the current state and its cached score. A strictly higher neighbor replaces the candidate; tied better neighbors keep the first examined one, while equality with the current state leaves it in place.
- Initialize the persistent incumbent and one random generator. An explicit
has_bestflag handles all-negative scores and zero evaluations. Seed the generator once per whole search, not once per restart. The first run uses the supplied starting state; the requested number of extra restarts follows it. - Evaluate a run's starting state. Check the shared evaluation budget before the call. Record a valid start in its path and update the incumbent if this is the first observation or a strict improvement. Reset only this run's accepted-move count.
- Check the per-run move cap before scanning. If it is reached, finish that run as
MOVE_LIMIT. Do not label the endpoint local merely because the run was stopped. With a zero move cap, only the starting state is evaluated. - Scan the complete neighborhood. Evaluate neighbors in order, recording each query. Update the incumbent on every valid observation. Select the best strictly improving candidate, but do not move the current state while the scan is incomplete. If the global evaluation budget blocks a query, record the partial scan, commit no move, and end the whole search with
EVAL_LIMIT. - Commit or stop locally. After a complete scan, count it. If no better candidate was found, finish the run as
LOCAL. Otherwise assign the selected state and reuse its already evaluated score, increment the move counters, append the new state/score to the path, and scan again. - Restart while attempts remain. Draw another feasible starting index, even if a previous restart chose the same one. Keep the incumbent and global evaluation count. If all scheduled runs finish as
LOCALorMOVE_LIMIT, reportFINISHED; this status describes the schedule, not a proof of global optimality.
Portable restart draws. All three programs use integer state $z$ and recurrence
$$z\leftarrow48271z\bmod2147483647,\qquad1\le z\le2147483646.$$
Let $M=2147483646$ and $L=M-(M\bmod n)$. Form $v=z-1$. Accept $v$ only if $v\lt L$, then return $v\bmod n$; otherwise draw again. The accepted range has a multiple of $n$ elements, so every index has the same number of preimages if raw draws are independent and uniform. This removes the unequal-residue mapping of naive modulo conversion. It does not turn a deterministic pseudorandom sequence into independent random draws. C++ and Java use 64-bit arithmetic for the multiplication.
The single generator advances across all runs. Its draw counter includes rejected raw values; the first user-specified start consumes no draw. A selected restart is recorded even if no evaluation budget remains to evaluate it. This makes the exact stopping point and final generator state reproducible.
flowchart TD
A["Validate input; initialize incumbent and one RNG"] --> B["Choose supplied start or next restart"]
B --> C{"Start evaluation fits global budget?"}
C -->|No| D["Return EVAL_LIMIT and best observation"]
C -->|Yes| E["Evaluate start; update incumbent; reset move count"]
E --> F{"Per-run move cap reached?"}
F -->|Yes| G["End run as MOVE_LIMIT"]
F -->|No| H["Candidate is current; begin ordered neighbor scan"]
H --> I{"Unscanned neighbor left?"}
I -->|Yes| J{"Evaluation fits global budget?"}
J -->|No| K["Record partial scan; commit no move"]
K --> D
J -->|Yes| L["Evaluate neighbor; update incumbent and best candidate"]
L --> I
I -->|No| M["Count complete scan"]
M --> N{"Candidate strictly improves current?"}
N -->|Yes| O["Commit candidate and cached score; count move"]
O --> F
N -->|No| P["End run as LOCAL"]
G --> Q{"Another restart scheduled?"}
P --> Q
Q -->|Yes| B
Q -->|No| R["Return FINISHED and persistent incumbent"]
Worked Example with a Full Trace
Use the ordered score table
$$[f(0),\ldots,f(11)]=[0,2,5,3,1,4,8,6,2,5,10,7].$$
Start at $0$, request $3$ extra restarts, use seed $13$, allow at most $256$ accepted moves per run, and share an evaluation budget of $1{,}000{,}000$. There are $4$ total scheduled runs. These bounds allow every run to finish locally in this example. The first start is fixed and consumes no random draw.
For $n=12$, the acceptance threshold is $L=2147483640$. The generated raw states below all pass rejection sampling:
| Extra restart | New generator state $z$ | $v=z-1$ | Starting index $v\bmod12$ |
|---|---|---|---|
| $1$ | $627523$ | $627522$ | $6$ |
| $2$ | $226391675$ | $226391674$ | $10$ |
| $3$ | $1755747989$ | $1755747988$ | $4$ |
The following table records every starting evaluation and complete neighborhood scan. A probe x:value means that state was queried at that point, including repeated queries. Candidate selection begins at the current state, so only a strictly higher value causes a move. Counts are global and cumulative; start rows do not count as scans.
| Run / event | Current state:score | Queried neighbors in order | Committed next state:score | Evaluations / scans / moves so far | Incumbent |
|---|---|---|---|---|---|
| $0$ / start | $0:0$ | Start evaluation only | $0:0$ | $1/0/0$ | $0:0$ |
| $0$ / move | $0:0$ | $1:2$ | $1:2$ | $2/1/1$ | $1:2$ |
| $0$ / move | $1:2$ | $0:0$, $2:5$ | $2:5$ | $4/2/2$ | $2:5$ |
$0$ / LOCAL | $2:5$ | $1:2$, $3:3$ | $2:5$ | $6/3/2$ | $2:5$ |
| $1$ / start | $6:8$ | Start evaluation only | $6:8$ | $7/3/2$ | $6:8$ |
$1$ / LOCAL | $6:8$ | $5:4$, $7:6$ | $6:8$ | $9/4/2$ | $6:8$ |
| $2$ / start | $10:10$ | Start evaluation only | $10:10$ | $10/4/2$ | $10:10$ |
$2$ / LOCAL | $10:10$ | $9:5$, $11:7$ | $10:10$ | $12/5/2$ | $10:10$ |
| $3$ / start | $4:1$ | Start evaluation only | $4:1$ | $13/5/2$ | $10:10$ |
| $3$ / move | $4:1$ | $3:3$, $5:4$ | $5:4$ | $15/6/3$ | $10:10$ |
| $3$ / move | $5:4$ | $4:1$, $6:8$ | $6:8$ | $17/7/4$ | $10:10$ |
$3$ / LOCAL | $6:8$ | $5:4$, $7:6$ | $6:8$ | $19/8/4$ | $10:10$ |
All accepted paths and scores are therefore: run $0$, $0:0\to1:2\to2:5$; run $1$, $6:8$; run $2$, $10:10$; run $3$, $4:1\to5:4\to6:8$. In the final run, the current score resets to $1$, then climbs to $8$, while the saved best remains $10$. At the last scan, state $6$ has neighbor scores $4$ and $6$, both below $8$, and is correctly marked local.
The complete result is FINISHED, best evaluated state $10$ with score $10$, $19$ objective evaluations, $8$ completed scans, and $4$ accepted moves. There are $3$ raw restart draws, no rejections, and final generator state $1755747989$. A separate full scan of the supplied table confirms that score $10$ is globally maximal; the hill-climbing stopping rules themselves did not establish that fact.
Trace It by Hand
In the last run, current state $4$ has score $1$. Its left neighbor $3$ scores $3$. Predict whether steepest ascent commits state $3$ immediately.
Reveal: it must also evaluate right neighbor $5$, which scores $4$, so the completed scan commits state $5$. The next completed scan commits state $6$, score $8$. First-improvement hill climbing would instead take the earlier improving state $3$ and enter the lower peak's basin.
Now run only from state $4$ with a total evaluation budget of $2$. The starting evaluation consumes one call, and left neighbor $3$ consumes the second. The right-neighbor query is refused. Return EVAL_LIMIT, keep the committed path at $[4]$, and return the best observed state $3$ with score $3$. There are zero completed scans and zero moves. An evaluated incumbent can be useful even when it was never committed as the current state.
Implementation in Python, C++, and Java
Each complete program runs the same strict steepest-ascent policy, with the same neighbor order and seeded restart mapping. The objective is scores[x] on integer indices; the local neighborhood contains only in-range adjacent indices. Current objective values are reused after accepted moves, but neighbor observations are deliberately re-evaluated on later scans. There is no memoization across different scans or restarts.
Input contract. The non-null score collection has $1\le n\le256$ integer entries, each in $[-10^9,10^9]$. The first start is an integer in $[0,n-1]$, extra restarts in $[0,32]$, seed in $[1,2147483646]$, accepted-move cap per run in $[0,256]$, and total objective-evaluation budget in $[0,10^6]$. Invalid sizes, score ranges, and numeric search parameters are rejected. Scores are copied, the caller's collection is unchanged, and each call creates a fresh generator and search state.
The result includes status, has_best, best evaluated index and value, statistics [evaluations,scans,moves,rng_draws], final generator state, and run histories. Each run records its planned start, committed path with corresponding scores, all scan records, its reason, and evaluations spent in that run. Each scan stores its original state/value, queried neighbors in order, committed next state/value, completion flag, and global evaluation count. A partial scan records the original state as its next state because no move was committed.
LOCAL is issued only after a complete scan with no strictly better neighbor; it is a weak local-maximum certificate for this neighborhood. MOVE_LIMIT issues no such certificate. Global FINISHED means the requested schedule finished, including runs stopped by their move caps. Global EVAL_LIMIT means a required query was refused. If has_best is false, best index $-1$ and value $0$ are dummy fields; otherwise, even a negative best value is meaningful.
An evaluation is counted only when performed. Exactly consuming the budget does not cancel a move whose entire scan already completed, nor an empty-neighborhood local certificate. However, a later scheduled start or a missing neighbor query can then trigger EVAL_LIMIT. The move cap is checked before starting a new scan; therefore a state reached on the last allowed move is not automatically classified local. Zero evaluation budget returns no incumbent, while zero move cap still allows each available starting evaluation.
The incumbent includes every evaluated state, including candidates from an interrupted scan. Tied best scores retain the earliest observation; it is not a smallest-index rule. Because state IDs and scores are immutable scalars, keeping the incumbent is a safe value copy. A mutable configuration adapter must instead copy or otherwise preserve the best configuration before modifying the current one.
This explicit table is a teaching adapter: simply scanning all entries is the best way to obtain its exact global maximum in $O(n)$ time. Hill climbing is useful when states and neighbors are implicit and expensive to enumerate globally. The displayed evaluation count measures logical search queries, while input validation and copying also read the supplied table. Run with Python 3, C++14 or newer, or Java 17 or newer; save Java as Main.java.
Expected standard output, identical in all three languages:
worked: FINISHED, best=10:10evaluations=19, scans=8, moves=4, rng_draws=3, rng_state=1755747989run=0, start=0, end=LOCAL, evaluations=6i=0, x=0, f=0i=1, x=1, f=2i=2, x=2, f=5run=1, start=6, end=LOCAL, evaluations=3i=0, x=6, f=8run=2, start=10, end=LOCAL, evaluations=3i=0, x=10, f=10run=3, start=4, end=LOCAL, evaluations=7i=0, x=4, f=1i=1, x=5, f=4i=2, x=6, f=8no restart: FINISHED, best=2:5evaluations=6, scans=3, moves=2, rng_draws=0, rng_state=13run=0, start=0, end=LOCAL, evaluations=6i=0, x=0, f=0i=1, x=1, f=2i=2, x=2, f=5plateau: FINISHED, best=1:3evaluations=3, scans=1, moves=0, rng_draws=0, rng_state=13run=0, start=1, end=LOCAL, evaluations=3i=0, x=1, f=3valley: FINISHED, best=0:5evaluations=2, scans=1, moves=0, rng_draws=0, rng_state=13run=0, start=0, end=LOCAL, evaluations=2i=0, x=0, f=5negative scores: FINISHED, best=1:-2evaluations=4, scans=2, moves=1, rng_draws=0, rng_state=13run=0, start=0, end=LOCAL, evaluations=4i=0, x=0, f=-8i=1, x=1, f=-2move cap: FINISHED, best=1:2evaluations=2, scans=1, moves=1, rng_draws=0, rng_state=13run=0, start=0, end=MOVE_LIMIT, evaluations=2i=0, x=0, f=0i=1, x=1, f=2partial scan: EVAL_LIMIT, best=3:3evaluations=2, scans=0, moves=0, rng_draws=0, rng_state=13run=0, start=4, end=EVAL_LIMIT, evaluations=2i=0, x=4, f=1singleton: FINISHED, best=0:7evaluations=3, scans=3, moves=0, rng_draws=2, rng_state=226391675run=0, start=0, end=LOCAL, evaluations=1i=0, x=0, f=7run=1, start=0, end=LOCAL, evaluations=1i=0, x=0, f=7run=2, start=0, end=LOCAL, evaluations=1i=0, x=0, f=7zero budget: EVAL_LIMIT, best=NONEevaluations=0, scans=0, moves=0, rng_draws=0, rng_state=13run=0, start=0, end=EVAL_LIMIT, evaluations=0checks = PASS
Complexity, Correctness, and Invariants
Let $N$ be the number of feasible states in a finite search space, $B$ the maximum neighborhood size, $K=R+1$ total scheduled starts, $M$ the per-run accepted-move cap, and $C_f$ an upper bound on objective-evaluation cost. Every accepted move strictly increases the objective. A run cannot revisit a state, so it accepts at most $N-1$ moves; a completed no-improvement scan may add one final scan. A conservative bound on scans per run is $\min(M+1,N)$, with fewer scans when the cap is zero or the run stops early.
Ignoring interruption, the evaluation bound is
$$E\le K\left(1+B\min(M+1,N)\right).$$
This counts one starting evaluation plus all neighbor queries. A global evaluation cap can reduce work but does not establish any optimum. With $G$ raw generator draws, generic search work is bounded by $O(K(1+B\min(M+1,N))(C_f+1)+G)$, also charging neighbor-generation work when it is not constant per neighbor.
For these programs, $B\le2$ and table lookups cost constant time. Let $S$ count completed scans and any final partial scan, and $A_{\mathrm{move}}$ accepted moves. Construction and execution take $O(n+K+E+S+G)$ time. Actual additional space is $O(n+K+S+A_{\mathrm{move}})=O(n+K+S)$ because the copied score table, every run, every path, and every at-most-two-probe scan are retained. Without the histories, the local-search core needs only constant scalar state for this one-dimensional adapter, in addition to the objective representation. A generic state or materialized neighborhood can require much more memory.
Best, average, worst. A zero-budget request performs no search evaluations but still validates/copies $O(n)$ input. With positive caps, an already local start needs one starting query and a neighborhood scan; the routine still processes later scheduled restarts. Long increasing chains can require $\Theta(N)$ accepted states and scans per unrestricted run. There is no landscape-independent average run length or global-success guarantee: both depend on the neighborhood, scores, tie policy, initialization distribution, and truncation caps.
Correctness of the local result. Current score equals the cached objective of the current feasible state. A complete scan selects the highest-scoring candidate among the current state and all neighbors under the specified tie policy. A committed move is therefore legal and strictly improving; failure to improve after a full scan establishes the weak local inequality. Finite strict ascent proves termination of an unrestricted discrete run, but not global optimality. The incumbent invariant is separate: after every observation, it equals the best evaluated score so far and never decreases, including during restarts and interrupted scans.
What random restarts can promise under an ideal model. Suppose each completed random-start run independently reaches a desired set with the same probability $p\gt0$. For $q$ such random runs,
$$\Pr(\text{at least one success})=1-(1-p)^q,\qquad\mathbb{E}[\text{runs until first success}]=\frac1p.$$
Here $q$ counts random runs; the code's first user-specified run is fixed. Its $R$ extra starts provide $R$ random opportunities in that idealized model. For $0\lt p\lt1$ and desired failure probability $\delta\in(0,1)$, it suffices to choose
$$q\ge\left\lceil\frac{\log\delta}{\log(1-p)}\right\rceil.$$
This requires knowing or justifying $p$ and independence. The formula does not apply unchanged to correlated starts, reseeding with the same seed, changing policies, or budget-truncated runs. A fixed pseudorandom seed makes this implementation deterministic; it is not evidence for an independence assumption. The implementation always processes its schedule unless the evaluation budget blocks it; it does not know when the true global optimum has been found.
For the worked landscape, full deterministic climbs produce basins $\{0,1,2,3\}\to2$, $\{4,5,6,7,8\}\to6$, and $\{9,10,11\}\to10$. An ideal independent uniform start reaches the global peak with probability $p=3/12=1/4$. The fixed initial start $0$ fails; three ideal random starts succeed with probability $1-(3/4)^3=37/64\approx0.578125$. Our particular seed happens to choose a successful start, which is one reproducible outcome rather than a probability estimate.
On a finite space, independent uniform starts give a global maximizer positive probability even by selecting it directly, so success probability tends to one as the number of completed attempts grows. A finite schedule still offers no certainty. In continuous spaces, an isolated exact optimum can have zero sampling probability; approximation regions and additional convergence assumptions must replace that finite-state argument.
| Approach | Work per local decision / total model | Auxiliary search state | Main tradeoff |
|---|---|---|---|
| Strict steepest-ascent hill climbing | Up to $B$ neighbor evaluations per complete scan | Current, best candidate, incumbent; state representation dependent | Best immediate improvement; can stop at a low peak or shoulder |
| First-improvement hill climbing | May stop scanning at the first improvement; worst case $B$ queries | Similar small working state | Cheaper steps can follow a different basin |
| Stochastic uphill choice | Samples among improving moves under a specified policy | Adds random-generator state | Diversifies trajectories; still cannot take a required downhill step |
| Random-restart hill climbing | Up to $K$ local runs; ideal success $1-(1-p)^q$ for $q$ independent random runs | One active run plus persistent incumbent | Explores different basins; finite attempts do not certify a global result |
| This traced one-dimensional implementation | $O(n+K+E+S+G)$ with at most two neighbors | $O(n+K+S)$ including copied scores and histories | Exact reproducibility, partial-scan handling, and honest stopping reasons |
| Simulated annealing | Depends on proposals, temperatures, and budget | Current state, incumbent, schedule, generator | Can accept worsening moves; guarantees require additional schedule assumptions |
| Tabu or iterated local search | Depends on move policy, tabu memory, or perturbations | More than one-state memory | Can escape some traps while controlling cycling |
| Exhaustive scan of this explicit table | $O(n)$ score accesses | $O(1)$ beyond input | Returns a true global maximum; preferable when the whole table is already available |
Every committed local move strictly raises the score, and LOCAL follows only a complete scan proving no strictly better neighbor exists. Independently, the incumbent is the best state ever evaluated and survives resets or partial scans; neither invariant proves that a fixed restart schedule found a global maximum.
Variants, Pitfalls, and Optimizations
Plateaus and valleys are different obstacles. In $[0,3,3,3,7]$, strict ascent from state $1$ stops at score $3$ even though equal-valued moves through $2$ and $3$ could reach $4$ with score $7$. Allowing a bounded number of sideways moves can cross such a shoulder, but unlimited equal-score moves can cycle on a flat region. In $[5,1,9]$, leaving state $0$ requires an actual downhill move before climbing; sideways moves alone do not help.
The neighborhood determines the trap. On a bounded integer grid, consider $f(x,y)=-(x-y)^2+(x+y)/4$. At $(0,0)$, each unit axis move has negative change, while diagonal move $(1,1)$ gains $1/2$. Axis-only hill climbing is stuck on a ridge relative to its move set; adding an appropriate diagonal move changes the neighborhood and the conclusion. More neighbors can improve exploration while making each steepest-ascent scan more expensive.
Keep restart diversity and the incumbent. Seeding the generator identically inside each restart can repeat one starting sequence, invalidating the intended diversification. Repeated starting states can still occur normally under sampling with replacement; the code does not force distinct starts. Keep the best score across all observations instead of returning the last run's endpoint. In the worked trace, the last run ends at score $8$ after an earlier run already found $10$.
Separate observation from commitment. A partial neighborhood scan cannot establish the steepest move or a local maximum. The implementation records any useful observed candidate in the incumbent but leaves the current path unchanged when a query is blocked. A first-improvement algorithm could commit earlier by design, but that is a different policy. Do not confuse the shared evaluation budget with the move cap that resets per run, and do not label FINISHED or MOVE_LIMIT as global convergence.
Use the right variant for the objective. Stochastic hill climbing randomly chooses among uphill moves; first-improvement stops its scan early; random restart changes initialization. Simulated annealing can accept downhill moves, and tabu search or perturb-and-reclimb methods use other mechanisms to leave a basin. These are distinct choices, not interchangeable names. For real-valued states, choose meaningful step sizes, bounds, progress tolerances, and stopping criteria: strict numerical improvement alone need not terminate an infinite sequence approaching a limit.
Preserve feasible states and trustworthy scores. Generate legal neighbors and feasible restart states, or define and validate a repair procedure. A soft penalty does not automatically make the highest-scoring observed state feasible. For noisy simulations, a single favorable observation can misrank candidates or the incumbent; repeated estimates and an explicit uncertainty policy are needed. For mutable states, a saved reference is not a preserved best configuration if later moves modify the same object. The sample avoids those issues with exact integer scores, immutable state IDs, and copied input.
A complete no-improvement scan certifies only a weak local maximum for the chosen neighborhood; plateaus, valleys, and ridges can still hide a better state. Preserve the incumbent across restarts, bound sideways or continuous progress explicitly, and apply restart probability formulas only when their sampling assumptions hold.
Practice Problems and Real-World Use
First verify a single trajectory by hand, then derive an independent transition map for every state: choose the highest score among the current state and its neighbors, preferring the current state on an equal score. Sorting states by descending score lets a separate oracle compute the final local endpoint of every strict-ascent basin. This checks hill-climbing behavior without running another online neighbor-comparison loop.
The programs were validated on 9,794 inputs per language, comprising $9{,}780$ valid cases and $14$ invalid inputs. The corpus includes $6{,}015$ exhaustive cases covering all score arrays over $\{-1,0,1\}$ of lengths $1$ through $6$ from every possible first start, plus randomized landscapes, long monotone chains, plateaus, negative scores, limits, and boundary sizes. Fifty-one seeded cases deliberately force a rejected raw restart draw. An independent transition/basin oracle and jump-ahead modular RNG oracle checked complete results, every query and path, tie rules, counters, and final generator state. There were $93{,}466$ strict-move checks, $121{,}655$ scan-history checks, and $27{,}740$ local-endpoint checks; $26$ cases retained a best observed state that was never committed to a path. Python, C++, and Java matched exactly on the full corpus and repeated runs, preserved input collections, and the C++ corpus passed address/undefined-behavior instrumentation.
For primary background, see the AIMA authors' chapter on informed and local search and Carnegie Mellon's local-search course notes. The exact score table, tie policy, portable restart mapping, incumbent semantics, budget contract, and full trace here are specified directly in this lesson.
| Practice problem or application | Concrete task | Idea tested | Difficulty |
|---|---|---|---|
| Three-peak score table | Reproduce every query and the seeded starts $6,10,4$ | Strict ascent, restarts, and persistent incumbent | Easy |
| Plateau and valley fixtures | Compare $[0,3,3,3,7]$ with $[5,1,9]$ | Sideways versus genuinely downhill escape requirements | Easy |
| Neighborhood ridge | Test axis-only and diagonal moves on the grid objective above | Local optimality depends on allowed moves | Medium |
| N-queens local search | Maximize negative attacking-pair count on complete assignments; compare with min-conflicts choices | State representation, local repair, and variant-specific move policies | Medium |
| Scheduling improvement | Swap or relocate tasks while maintaining hard feasibility | Legal neighborhoods and objective deltas | Medium |
| Tour improvement | Use valid 2-opt moves on a complete feasible tour | Large neighborhoods, first improvement versus best improvement | Hard |
| Deterministic simulator tuning | Generate feasible parameter starts and compare restart budgets | Expensive evaluations, repeated basins, and progress limits | Hard |
| Restart statistics laboratory | Enumerate small basins, then compare independent random-start trials with the ideal success formula | Known success probability, independence, and reproducible experiments | Hard |