|
7 | 7 |
|
8 | 8 | """ |
9 | 9 |
|
10 | | -from collections.abc import Iterator |
| 10 | +from collections.abc import Iterator, Sequence |
11 | 11 |
|
12 | 12 |
|
13 | | -def ordinal_representation_closed(path: list[str], nodes: list[str]) -> Iterator[int]: |
| 13 | +def ordinal_representation_closed(path: Sequence[str], nodes: Sequence[str]) -> Iterator[int]: |
14 | 14 | """ |
15 | 15 | Generate the ordinal representation for a closed path. |
16 | 16 |
|
@@ -52,15 +52,32 @@ def ordinal_representation_closed(path: list[str], nodes: list[str]) -> Iterator |
52 | 52 | msg = "path and nodes must contain the same values" |
53 | 53 | raise ValueError(msg) |
54 | 54 |
|
55 | | - reference = nodes.copy() |
| 55 | + reference = list(nodes) |
56 | 56 | for city in path: |
57 | 57 | yield (index := reference.index(city) + 1) # 1-based index |
58 | 58 | reference.pop(index - 1) |
59 | 59 |
|
60 | 60 |
|
| 61 | +def tour_from_ordinal(ordinal: list[int], nodes: list[str]) -> list[str]: |
| 62 | + """ |
| 63 | + Decode an ordinal representation back into a tour. |
| 64 | +
|
| 65 | + This is the exact inverse of ``ordinal_representation_closed``. It is what |
| 66 | + makes ordinal encoding useful in a genetic algorithm: an ordinary one-point |
| 67 | + crossover of two ordinal vectors always decodes to a valid tour, with no |
| 68 | + repair step needed. |
| 69 | +
|
| 70 | + >>> nodes = list("ABCDEFGHIJKL") |
| 71 | + >>> path = list("GLADBIKEHJFC") |
| 72 | + >>> encoded = list(ordinal_representation_closed(path, nodes)) |
| 73 | + >>> tour_from_ordinal(encoded, nodes) == path |
| 74 | + True |
| 75 | + """ |
| 76 | + reference = nodes.copy() |
| 77 | + return [reference.pop(index - 1) for index in ordinal] |
| 78 | + |
| 79 | + |
61 | 80 | if __name__ == "__main__": |
62 | | - sample_path = list("ODGLAHKMBJFCNIE") |
63 | | - all_nodes = sorted(set(sample_path)) |
64 | | - print("Ordinal Representation:") |
65 | | - ordinal_values = ordinal_representation_closed(sample_path, all_nodes) |
66 | | - print(" ".join(map(str, ordinal_values))) |
| 81 | + import doctest |
| 82 | + |
| 83 | + doctest.testmod() |
0 commit comments