Local Search and Simulated Annealing › Example
Machine Learning from ScratchAll chapters Local Search and Simulated Annealing › ExampleSection view · Sections in this chapter6 / 10 ↓
Reading tools Definitions, links & notes
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Let us work one reversal entirely by hand to see how the bookkeeping fits together. Start from the nearest-neighbor permutation
where the vertical bar separates the two vehicles. The current objective is the sum of the arc lengths on the two closed tours:
Substituting the coordinates (with and given in the array above) gives:
so the first route contributes . For the second route:
Carrying unrounded distances through the sums, the second route contributes and the total is . Summing the displayed rounded arc values instead can change the final reported decimal.
Now consider the reversal with and . Reversing positions of the permutation (that is, the segment ) produces
The reversal leaves the first customer, 1, fixed but changes the last customer of the first route from 3 to 7. Since distances are symmetric, reversing the internal edges keeps their lengths unchanged. The two changed boundary edges are replaced by , and replaced by . Therefore
Here is the new total distance minus the old total distance, and is the Euclidean distance between nodes and , including depot node . The negative sign means the reversal improves the tour. The two edges strictly inside the reversed segment retain their total length. The changed entering edge lengthens, while the changed return edge shortens by more, producing the net improvement. Checking every changed boundary edge is essential when a reversed segment ends at a route boundary.
PythonIn [18]Expand
def reverse_at(perm, i, j):
p = list(perm)
p[i : j + 1] = p[i : j + 1][::-1]
return tuple(p)
base = objective(nn_perm)
reverse_deltas = []
for i, j in [(1, 3), (2, 3), (0, 3), (3, 4)]:
p = reverse_at(nn_perm, i, j)
reverse_deltas.append((i, j, objective(p) - base, p))ConsoleOut [19]Expand
reversal (1,3): delta = -2.4637 perm = (1, 3, 8, 7, 6, 5, 4, 2) reversal (2,3): delta = +0.7225 perm = (1, 7, 3, 8, 6, 5, 4, 2) reversal (0,3): delta = +0.0000 perm = (3, 8, 7, 1, 6, 5, 4, 2) reversal (3,4): delta = +1.6437 perm = (1, 7, 8, 6, 3, 5, 4, 2)
ConsoleOut [20]Expand
Best single reversal so far: 38.3926 at (1, 3, 8, 7, 6, 5, 4, 2) Full tour improvement from NN to exact optimum: 3.5004
The output confirms that reversing to lowers the length by about 2.4637. Other reversals can worsen it, as the printed positive delta illustrates. Every complete candidate score costs one objective evaluation. A full scan contains 28 such calls; initialization and restarts also consume the 300-call limit. Annealing spends that same limit on its initial score and proposals, while NN-seeded random search uses one nearest-neighbor score followed by 299 random permutations.
For the acceptance rule, take a hypothetical worsening move with . At temperature the acceptance probability is , so even a small worsening is accepted most of the time. At temperature it is , so this worsening move is rarely accepted. And at temperature it is , effectively zero. That exponential controls acceptance of worsening proposals; annealing also requires a proposal rule, a temperature schedule, state updates, and a stopping budget. In the mathematical acceptance rule, a larger temperature accepts a fixed worsening move more often, while any finite positive temperature leaves a positive probability. In floating-point code, sufficiently small exponentials can underflow to zero.
Comments
Add to the conversation
A question, a correction, or an example from your own work.
No comments yet. Be the first to share your thoughts!