正如@IVlad 在对您的问题Yodaness problem 的评论中指出的那样,要求您计算number of inversions 而不是最少的交换次数。
例如:
L1 = [2,3,4,5]
L2 = [2,5,4,3]
最小交换次数为 1(在 L2 中交换 5 和 3 以得到 L1),但反转次数为 3:(5 4 )、(5 3) 和 (4 3) 对的顺序错误。
计算反转次数的最简单方法来自the definition:
如果 i i,则一对元素 (pi,pj) 称为置换 p 中的反转> pj.
在 Python 中:
def count_inversions_brute_force(permutation):
"""Count number of inversions in the permutation in O(N**2)."""
return sum(pi > permutation[j]
for i, pi in enumerate(permutation)
for j in xrange(i+1, len(permutation)))
您可以使用分治策略计算O(N*log(N)) 中的反转(类似于a merge sort algorithm 的工作方式)。下面是从Counting Inversions 翻译成 Python 代码的伪代码:
def merge_and_count(a, b):
assert a == sorted(a) and b == sorted(b)
c = []
count = 0
i, j = 0, 0
while i < len(a) and j < len(b):
c.append(min(b[j], a[i]))
if b[j] < a[i]:
count += len(a) - i # number of elements remaining in `a`
j+=1
else:
i+=1
# now we reached the end of one the lists
c += a[i:] + b[j:] # append the remainder of the list to C
return count, c
def sort_and_count(L):
if len(L) == 1: return 0, L
n = len(L) // 2
a, b = L[:n], L[n:]
ra, a = sort_and_count(a)
rb, b = sort_and_count(b)
r, L = merge_and_count(a, b)
return ra+rb+r, L
例子:
>>> sort_and_count([5, 4, 2, 3])
(5, [2, 3, 4, 5])
以下是the problem 示例的 Python 解决方案:
yoda_words = "in the force strong you are".split()
normal_words = "you are strong in the force".split()
perm = get_permutation(normal_words, yoda_words)
print "number of inversions:", sort_and_count(perm)[0]
print "number of swaps:", number_of_swaps(perm)
输出:
number of inversions: 11
number of swaps: 5
get_permutation() 和 number_of_swaps() 的定义是:
def get_permutation(L1, L2):
"""Find permutation that converts L1 into L2.
See http://en.wikipedia.org/wiki/Cycle_representation#Notation
"""
if sorted(L1) != sorted(L2):
raise ValueError("L2 must be permutation of L1 (%s, %s)" % (L1,L2))
permutation = map(dict((v, i) for i, v in enumerate(L1)).get, L2)
assert [L1[p] for p in permutation] == L2
return permutation
def number_of_swaps(permutation):
"""Find number of swaps required to convert the permutation into
identity one.
"""
# decompose the permutation into disjoint cycles
nswaps = 0
seen = set()
for i in xrange(len(permutation)):
if i not in seen:
j = i # begin new cycle that starts with `i`
while permutation[j] != i: # (i σ(i) σ(σ(i)) ...)
j = permutation[j]
seen.add(j)
nswaps += 1
return nswaps