例如,输入是
Array 1 = [2, 3, 4, 5]
Array 2 = [3, 2, 5, 4]
所需的最小交换次数为2
。
交换不需要与相邻的单元格相交,任何两个元素都可以交换。
答案 0 :(得分:27)
正如@IVlad在对您的问题Yodaness problem的评论中指出的那样,要求您计算number of inversions而不是最小数量的互换。
例如:
L1 = [2,3,4,5]
L2 = [2,5,4,3]
交换的最小数量是一个(在L2
中交换5和3以获得L1
),但转换次数 3 :(5 4),(5 3)和(4 3)对的顺序错误。
计算倒数数量的最简单方法是the definition:
如果i <1,则一对元素(p i ,p j )在置换p中被称为反转。 j和p i &gt; P <子>Ĵ子>
在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)))
你可以使用divide&amp;&amp;征服战略(类似于a merge sort algorithm如何运作)。这里的Counting Inversions伪代码转换为Python代码:
O(N*log(N))
示例:
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
这是来自the problem的示例的Python解决方案:
>>> sort_and_count([5, 4, 2, 3])
(5, [2, 3, 4, 5])
输出:
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()
答案 1 :(得分:11)
正如Sebastian的解决方案所暗示的那样,您正在寻找的算法可以基于检查permutation's cycles。
我们应该将数组#2视为数组#1上的置换变换。在您的示例中,置换可以表示为P = [2,1,4,3]。
每个排列可以表示为一组不相交的循环,表示项目的循环位置变化。排列P例如具有2个循环:(2,1)和(4,3)。因此,两次交换就足够了。在一般情况下,您应该简单地从排列长度中减去周期数,并获得所需交换的最小数量。这是因为观察到为了“修复”N个元素的循环,N-1交换就足够了。
答案 2 :(得分:3)
这个问题有一个干净,贪婪,简单的解决方案:
通过将问题的可能性定义为array1中所有元素与array2中目标的距离之和,可以证明算法的正确性。
答案 3 :(得分:1)
可能有一些智能动态编程解决方案,但我现在无法弄清楚。您可以使用以下内容进行天真的BFS遍历:
UPDATE :在Python中实现(缓慢的O((N 3 ) n ))
def bfs(L1, L2):
assert sorted(L1) == sorted(L2)
q = deque([ (0,L1) ])
while q:
n, L = q.popleft()
if L == L2: return n
for i, j in combinations(range(len(L1)), 2): # all N*(N-1)/2 pairs
q.append((n+1, swap(L, i, j)))
from collections import deque
from itertools import combinations
def swap(L, i, j):
a = list(L) # make copy
a[i], a[j] = a[j], a[i] # swap
return a
答案 4 :(得分:1)
这可以很容易地转换为另一种类型的问题,可以更有效地解决。所需要的只是将数组转换为排列,即将值更改为其id。所以你的阵列:
L1 = [2,3,4,5]
L2 = [2,5,4,3]
会变成
P1 = [0,1,2,3]
P2 = [0,3,2,1]
分配2->0, 3->1, 4->2, 5->3
。只有在没有重复的项目时才能这样做。如果有,那就更难解决了。
通过反转O(n)中的目标置换,组成O(n)中的置换,然后从中找到交换的数量,可以将置换从一个转换为另一个转换为类似的问题(Number of swaps in a permutation)在O(m)中进行身份置换。 鉴于:
int P1[] = {0, 1, 2, 3}; // 2345
int P2[] = {0, 3, 2, 1}; // 2543
// we can follow a simple algebraic modification
// (see http://en.wikipedia.org/wiki/Permutation#Product_and_inverse):
// P1 * P = P2 | premultiply P1^-1 *
// P1^-1 * P1 * P = P1^-1 * P2
// I * P = P1^-1 * P2
// P = P1^-1 * P2
// where P is a permutation that makes P1 into P2.
// also, the number of steps from P to identity equals
// the number of steps from P1 to P2.
int P1_inv[4];
for(int i = 0; i < 4; ++ i)
P1_inv[P1[i]] = i;
// invert the first permutation in O(n)
int P[4];
for(int i = 0; i < 4; ++ i)
P[i] = P2[P1_inv[i]];
// chain the permutations in O(n)
int num_steps = NumSteps(P, 4); // will return 2
// now we just need to count the steps in O(num_steps)
要计算步数,可以设计一个简单的算法,例如:
int NumSteps(int *P, int n)
{
int count = 0;
for(int i = 0; i < n; ++ i) {
for(; P[i] != i; ++ count) // could be permuted multiple times
swap(P[P[i]], P[i]); // look where the number at hand should be
}
// count number of permutations
return count;
}
这总是将项目交换到应该在身份排列中的位置,因此在每个步骤中它都会撤消并计算一个交换。现在,只要它返回的交换次数确实最小,算法的运行时间就会被它限制并保证完成(而不是陷入无限循环)。它将在O(m)
交换或O(m + n)
循环迭代中运行,其中m
是交换次数(返回count
),n
是序列中的项目数(4
)。请注意m < n
始终为真。因此,这应该优于O(n log n)
解,因为这里的上限是交换的O(n - 1)
或者循环迭代的O(n + n - 1)
,这实际上是O(n)
(常数因子)在后一种情况下省略了2)。
该算法仅适用于有效排列,对于具有重复值的序列,它将无限循环,并且对于值不是[0, n)
的序列,将对范围内的数组进行访问(和崩溃)。可以找到一个完整的测试用例here(使用Visual Studio 2008构建,算法本身应该相当便携)。它生成长度为1到32的所有可能的排列,并且检查通过广度优先搜索(BFS)生成的解决方案似乎适用于长度为1到12的所有排列,然后它变得相当慢但我认为它将继续工作
答案 5 :(得分:0)
这似乎是一个edit distance问题,除了只允许换位。
查看Damerau–Levenshtein distance伪代码。我相信你可以调整它只计算换位。
答案 6 :(得分:0)
<强>算法:强>
<强>代码:强>
def nswaps(l1, l2):
cnt = 0
for i in range(len(l1)):
if l1[i] != l2[i]:
ind = l2.index(l1[i])
l2[i], l2[ind] = l2[ind], l2[i]
cnt += 1
pass
return cnt
答案 7 :(得分:0)
由于我们已经知道arr2对于arr1中存在的每个元素都有正确的索引。因此,我们可以简单地将arr1元素与arr2进行比较,并在索引错误的情况下将它们替换为正确的索引。
def minimum_swaps(arr1, arr2):
swaps = 0
for i in range(len(arr1)):
if arr1[i] != arr2[i]:
swaps += 1
element = arr1[i]
index = arr1.index(arr2[i]) # find index of correct element
arr1[index] = element # swap
arr1[i] = arr2[i]
return swaps
答案 8 :(得分:-1)
@ J.F。 Sebastian和@Eyal Schneider的回答非常酷。
我受到了解决类似问题的启发:计算对数组进行排序所需的最小交换,例如:排序{2,1,3,0}
,您需要至少2次交换。
这是Java代码:
// 0 1 2 3
// 3 2 1 0 (0,3) (1,2)
public static int sortWithSwap(int [] a) {
Integer[] A = new Integer[a.length];
for(int i=0; i<a.length; i++) A[i] = a[i];
Integer[] B = Arrays.copyOf(mapping(A), A.length, Integer[].class);
int cycles = 0;
HashSet<Integer> set = new HashSet<>();
boolean newCycle = true;
for(int i=0; i<B.length; ) {
if(!set.contains(B[i])) {
if(newCycle) {
newCycle = false;
cycles++;
}
set.add(B[i]);
i = B[i];
}
else if(set.contains(B[i])) { // duplicate in existing cycles
newCycle = true;
i++;
}
}
// suppose sequence has n cycles, each cycle needs swap len(cycle)-1 times
// and sum of length of all cycles is length of sequence, so
// swap = sequence length - cycles
return a.length - cycles;
}
// a b b c
// c a b b
// 3 0 1 1
private static Object[] mapping(Object[] A) {
Object[] B = new Object[A.length];
Object[] ret = new Object[A.length];
System.arraycopy(A, 0, B, 0, A.length);
Arrays.sort(A);
HashMap<Object, Integer> map = new HashMap<>();
for(int i=0; i<A.length; i++) {
map.put(A[i], i);
}
for(int i=0; i<B.length; i++) {
ret[i] = map.get(B[i]);
}
return ret;
}