I have an optimization problem and I am looking for an algorithm that performs better than the naive method I have.
The problem
Consider a graph, G(N, E)
, where N
is the set of nodes and E
is the set of edges. Each node n_i
in N
has an associated state s_i
. I've used a dictionary D
to store this with nodes as keys and the corresponding state as the value. Adjacent nodes may swap states using a swap manouever. Specifically, a swap manouever is defined as
def swap(G, D, n_i, n_j):
if n_i not in neighbors(G, n_j):
print('Cannot swap between disconnected nodes')
else:
s_i = D[n_i]
s_j = D[n_j]
D[n_i] = s_j
D[n_j] = s_i
return D
Each state must now "visit" a list of other states in a given order, where visiting means both states must be on any pair of adjacent nodes on G(N, E)
. For example s_1
must visit [s_3, s_5, s_2]
, s_2
must visit [s_1, s_3]
, s_3
must visit [s_1, s_2, s_4]
and so on. The order of visits must be preserved. However, states are allowed to be on adjacent nodes without marking that as a visit. For instance, if s_1
starts off adjacent to s_5
, then it must first become adjacent to s_3
, mark that visit as done and then become adjacent to s_5
again. The visitor lists are such that if s_i
must visit s_j
, then that also means that s_j
must visit s_i
i.e. everything is self-consistent.
Typical sizes
The graph is usually around 20-30 nodes, each node is typically connected to 2-4 others. The visit list has no length restriction in principle but in practice doesn't exceed 10.
The goal
The goal is to complete the list of visits while minimizing the total number of swap manouvers. What is an efficient algorithm/heuristic for this problem?
My naive solution
My current algorithm does the simplest thing one can do. I first see which vists are (0,0)
visits according to the following function i.e. both states must visit each other before visiting anyone else.
def interaction_priority(s1, s2):
if s2 in visitors_of_s1:
b = visitors_of_s1.index(s2)
a = visitors_of_s2.index(s1)
return (a, b)
For these states, I get the current nodes and find the shortest path between those nodes on G
using a networx
function. I perform swaps till they are adjacent. Let us assume the previous function returned (0,0)
for s_1 and s_3
. Then I find the shortest path between the current nodes of s_1
and s_3
inv_D = {s : n for n, s in D.items()} #Invert the dictionary of nodes and states
def get_shortest_path(s1, s3):
n1 = inv_D[s1]
n3 = inv_D[s3]
path_nodes = get_shortest_path(G, n1, n3)
return path_nodes
Now, I perform swaps along this path
if len(path_nodes)> 0:
for i in path_nodes:
D = swap(G, D, n1, i)
visitors_of_s1.remove(s3)
visitors_of_s3.remove(s1)
I'm aware that I can probably optimize things a bit e.g. make the last method symmetric and so on but the fundamental idea is still not very clever. Is there a cleverer algorithm that optimizes how the states move about the graph but still complete all their visits.