Consider a dynamic programming solution based on the A * algorithm. The idea is to simulate this problem as a graph and find the optimal path to the node target.
First, some basic clarifications. Each node in the graph is a string, and two nodes are neighbors if and only if they differ in one (circular) swap of adjacent characters. Running node is the first line, and node's goal is the second line. Finding the shortest path from start to goal clearly gives you the best solution.
. A * , , , . , , , , A * . , , , . , , , , , . , , -, . , -, (, ).
, , , .
_heuristic, , , , .
. , . , .
import random
import string
import copy
from Queue import PriorityQueue as priority_queue
def swap(c,i,j):
if j >= len(c):
j = j % len(c)
c = list(c)
c[i], c[j] = c[j], c[i]
return ''.join(c)
def terrible_heuristic(x, y):
lut = {}
for i in range(len(y)):
c = y[i]
if lut.has_key(c):
lut[c].append(i)
else:
lut[c] = [i]
longest_swaps = []
for i in range(len(x)):
cpos = lut[x[i]]
longest_swaps.append(min([ min((i-cpos[j])%len(x),(cpos[j]-i)%len(x)) for j in range(len(cpos)) ]))
return max(longest_swaps)-1
def better_heuristic(x, y):
lut = {}
for i in range(len(y)):
c = y[i]
if lut.has_key(c):
lut[c].append(i)
else:
lut[c] = [i]
longest_swaps = []
for i in range(len(x)):
cpos = lut[x[i]]
longest_swaps.append(min([ min((i-cpos[j])%len(x),(cpos[j]-i)%len(x)) for j in range(len(cpos)) ]))
d = 0.
for x in longest_swaps:
d += x-1
constant = 1.5
d /= constant
return d
def ngbs(x):
n = set()
for i in xrange(len(x)):
n.add(swap(x,i,i+1))
return n
class sane_priority_queue(priority_queue):
def __init__(self):
priority_queue.__init__(self)
self.counter = 0
def put(self, item, priority):
priority_queue.put(self, (priority, self.counter, item))
self.counter += 1
def get(self, *args, **kwargs):
_, _, item = priority_queue.get(self, *args, **kwargs)
return item
def a_star(x0,goal,heuristic_func=terrible_heuristic):
visited = set()
frontier_visited = set()
frontier = sane_priority_queue()
distances = {}
predecessors = {}
predecessors[x0] = x0
distances[x0] = 0
frontier.put(x0,heuristic_func(x0,goal))
while not frontier.empty():
current = frontier.get()
if current == goal:
print "goal found, distance: ", distances[current], ' nodes explored: ', len(visited)
return predecessors, distances
visited.add(current)
for n in ngbs(current):
if n in visited:
continue
tentative_distance = distances[current] + 1
if not distances.has_key(n) or tentative_distance < distances[n]:
predecessors[n] = current
distances[n] = tentative_distance
heuristic_distance = tentative_distance + heuristic_func(n,goal)
frontier.put(n,heuristic_distance)
n = 10
str1 = ''.join([random.choice(string.ascii_letters + string.digits) for n in xrange(n)])
str2 = copy.deepcopy(str1)
l = list(str2)
random.shuffle(l)
str2 = ''.join(l)
print 'str1', str1
print 'str2', str2
print 'a_star with terrible_heuristic:'
predecessors, distances = a_star(str1,str2,terrible_heuristic)
current = str2
while current != predecessors[current]:
print current
current = predecessors[current]
print str1
print 'a_star with better_heuristic:'
predecessors, distances = a_star(str1,str2,better_heuristic)
current = str2
while current != predecessors[current]:
print current
current = predecessors[current]
print str1