|
| 1 | +#! /usr/bin/env python3 |
| 2 | + |
| 3 | +from .common import cost, random_permutation |
| 4 | + |
| 5 | +import random |
| 6 | + |
| 7 | +""" |
| 8 | +2.7 |
| 9 | +
|
| 10 | +Variable Neighborhood Search involves iterative exploration of larger and larger |
| 11 | +neighborhoods for a given local optima until an improvement is located after |
| 12 | +which time the search across expanding neighborhoods is repeated. The strategy |
| 13 | +is motivated by three principles: 1) a local minimum for one neighborhood |
| 14 | +structure may not be a local minimum for another neighborhood structure, 2) a |
| 15 | +global minimum is local minimum for all possible neighborhood structures, and |
| 16 | +3) local minima are relatively close to global minima for many problem classes. |
| 17 | +""" |
| 18 | + |
| 19 | +def stochastic_two_opt(perm): |
| 20 | + randlimit = len(perm) - 1 |
| 21 | + c1, c2 = random.randint(0, randlimit), random.randint(0, randlimit) |
| 22 | + exclude = [c1] |
| 23 | + exclude.append(randlimit if c1 == 0 else c1 -1) |
| 24 | + exclude.append(0 if c1 == randlimit else c1 + 1) |
| 25 | + |
| 26 | + while c2 in exclude: |
| 27 | + c2 = random.randint(0, randlimit) |
| 28 | + |
| 29 | + c1, c2 = c2, c1 if c2 < c1 else None |
| 30 | + perm[c1:c2] = perm[c1:c2][::-1] |
| 31 | + return perm |
| 32 | + |
| 33 | +def local_search(best, cities, max_no_improv, neighborhood_size): |
| 34 | + count = 0 |
| 35 | + |
| 36 | + while count < max_no_improv: |
| 37 | + candidate = {} |
| 38 | + candidate["vector"] = [v for v in best["vector"]] |
| 39 | + |
| 40 | + for _ in range(neighborhood_size): |
| 41 | + stochastic_two_opt(candidate["vector"]) |
| 42 | + |
| 43 | + candidate["cost"] = cost(candidate["vector"], cities) |
| 44 | + |
| 45 | + if candidate["cost"] < best["cost"]: |
| 46 | + count, best = 0, candidate |
| 47 | + else: |
| 48 | + count += 1 |
| 49 | + |
| 50 | + return best |
| 51 | + |
| 52 | +def search(cities, neigborhoods, max_no_improv, max_no_improv_ls): |
| 53 | + best = {} |
| 54 | + best["vector"] = random_permutation(cities) |
| 55 | + best["cost"] = cost(best["vector"], cities) |
| 56 | + iter_, count = 0, 0 |
| 57 | + |
| 58 | + while count < max_no_improv: |
| 59 | + for neigh in negihborhoods: |
| 60 | + candidate = {} |
| 61 | + candidate["vector"] = [v for v in best["vector"]] |
| 62 | + |
| 63 | + for _ in range(neigh): |
| 64 | + stochastic_two_opt(candidate["vector"]) |
| 65 | + |
| 66 | + candidate["cost"] - cost(candidate["vector"], cities) |
| 67 | + candidate = local_search(candidate, cities, max_no_improv_ls, neigh) |
| 68 | + print("> iteration #%s, neigh=%s, best=%s" % (iter_ + 1, neigh, best["cost"])) |
| 69 | + iter_ += 1 |
| 70 | + |
| 71 | + if candidate["cost"] < best["cost"]: |
| 72 | + best, count = candidate, 0 |
| 73 | + print("New best, restarting neighborhood search") |
0 commit comments