Arrays 为什么数组大小必须为3^k+;1循环领导迭代算法是否有效?
是一种通过将所有偶数编号的项移到前面,将所有奇数编号的项移到后面,同时保持它们的相对顺序来洗牌数组的算法。例如,给定此输入:Arrays 为什么数组大小必须为3^k+;1循环领导迭代算法是否有效?,arrays,algorithm,math,permutation,number-theory,Arrays,Algorithm,Math,Permutation,Number Theory,是一种通过将所有偶数编号的项移到前面,将所有奇数编号的项移到后面,同时保持它们的相对顺序来洗牌数组的算法。例如,给定此输入: a 1 b 2 c 3 d 4 e 5 输出将是 a b c d e 1 2 3 4 5 该算法在O(n)时间内运行,仅使用O(1)空间 该算法的一个不寻常的细节是,它通过将阵列分成大小为3k+1的块来工作。显然,这对算法的正确运行至关重要,但我不知道为什么会这样 为什么在算法中需要选择3k+1 谢谢 这将是一个很长的答案。你的问题的答案并不简单,需要一些数论才能完全
a 1 b 2 c 3 d 4 e 5
输出将是
a b c d e 1 2 3 4 5
该算法在O(n)时间内运行,仅使用O(1)空间
该算法的一个不寻常的细节是,它通过将阵列分成大小为3k+1的块来工作。显然,这对算法的正确运行至关重要,但我不知道为什么会这样
为什么在算法中需要选择3k+1
谢谢 这将是一个很长的答案。你的问题的答案并不简单,需要一些数论才能完全回答。我花了大约半天的时间研究这个算法,现在我有了一个很好的答案,但我不确定我能不能简明扼要地描述它
简短版本:
- 将输入分解为大小为3k+1的块实质上是将输入分解为大小为3k-1的块,其周围有两个元素,这些元素最终不会移动
- 块中剩余的3k-1元素按照一个有趣的模式移动:每个元素移动到索引除以两个模3k得到的位置
- 这种特殊的运动模式与数论和群论中的一个概念有关,称为
- 因为数字2是一个原始的根模3k,从数字1、3、9、27等开始,运行模式可以保证在数组的所有元素中循环一次,并将它们放在正确的位置
- 这种模式高度依赖于这样一个事实,即2是任何k的3k的原始根≥ 1.将数组的大小更改为另一个值几乎肯定会破坏这一点,因为保留了错误的属性
A 0 1 2 3 4
σ(A) 2 3 0 4 1
描述排列的一种方法是列出排列中的新元素。然而,从算法的角度来看,将排列表示为一个,一种写出排列的方式通常更有用,它展示了如何从初始数组开始,然后循环排列其中的一些元素,形成排列
看看上面的排列。首先,看看0在哪里结束。在σ(A)中,元素0最终取代了元素2原来所在的位置。反过来,元素2最终取代了元素0原来所在的位置。我们通过写(02)来表示这一点,表示0应该去2过去所在的地方,2应该去0过去所在的地方
现在,看看元素1。元素1结束于原来的位置4。然后,数字4在原来的3处结束,元素3在原来的1处结束。我们通过书写(143)来表示这一点,1应该去4过去所在的地方,4应该去3过去所在的地方,3应该去1过去所在的地方
结合这些元素,我们可以将上述元素的整体置换表示为(02)(143)-我们应该交换0和2,然后循环置换1、4和3。如果我们从初始数组开始这样做,我们将得到我们想要的置换数组
循环分解对于就地排列数组非常有用,因为它可以在O(C)时间和O(1)辅助空间中排列任何单个循环,其中C是循环中的元素数。例如,假设您有一个循环(1 6 8 4 2)。您可以使用如下代码排列循环中的元素:
int[] cycle = {1, 6, 8, 4, 2};
int temp = array[cycle[0]];
for (int i = 1; i < cycle.length; i++) {
swap(temp, array[cycle[i]]);
}
array[cycle[0]] = temp;
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 2 4 6 8 10 12 1 3 5 7 9 11 13
Element 2 4 6 8 10 12 1 3 5 7 9 11
Index 1 2 3 4 5 6 7 8 9 10 11 12
Initial: 1 2 3 4 5 6 7 8 9 10
Final: 2 4 6 8 10 1 3 5 7 9
Initial: 1 2 3 4 5 6 7 8 9 10 11 12
Final: 2 4 6 8 10 12 1 3 5 7 9 11
Initial: 1 2 3 4 5 6 7 8 9 10 11 12 13 14
Final: 2 4 6 8 10 12 14 1 3 5 7 9 11 13
Initial: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Final: 2 4 6 8 10 12 14 16 1 3 5 7 9 11 13 15
此算法的总体时间和空间复杂度取决于以下因素:
for (each cycle in the cycle decomposition algorithm) {
apply the above algorithm to cycle those elements;
}
int[] cycle = {1, 6, 8, 4, 2};
int temp = array[cycle[0]];
for (int i = 1; i < cycle.length; i++) {
swap(temp, array[cycle[i]]);
}
array[cycle[0]] = temp;
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 2 4 6 8 10 12 1 3 5 7 9 11 13
Element 2 4 6 8 10 12 1 3 5 7 9 11
Index 1 2 3 4 5 6 7 8 9 10 11 12
Initial: 1 2 3 4 5 6 7 8 9 10
Final: 2 4 6 8 10 1 3 5 7 9
Initial: 1 2 3 4 5 6 7 8 9 10 11 12
Final: 2 4 6 8 10 12 1 3 5 7 9 11
Initial: 1 2 3 4 5 6 7 8 9 10 11 12 13 14
Final: 2 4 6 8 10 12 14 1 3 5 7 9 11 13
Initial: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Final: 2 4 6 8 10 12 14 16 1 3 5 7 9 11 13 15
我们希望对元素进行洗牌,使它们像这样出现:
int[] cycle = {1, 6, 8, 4, 2};
int temp = array[cycle[0]];
for (int i = 1; i < cycle.length; i++) {
swap(temp, array[cycle[i]]);
}
array[cycle[0]] = temp;
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 2 4 6 8 10 12 1 3 5 7 9 11 13
Element 2 4 6 8 10 12 1 3 5 7 9 11
Index 1 2 3 4 5 6 7 8 9 10 11 12
Initial: 1 2 3 4 5 6 7 8 9 10
Final: 2 4 6 8 10 1 3 5 7 9
Initial: 1 2 3 4 5 6 7 8 9 10 11 12
Final: 2 4 6 8 10 12 1 3 5 7 9 11
Initial: 1 2 3 4 5 6 7 8 9 10 11 12 13 14
Final: 2 4 6 8 10 12 14 1 3 5 7 9 11 13
Initial: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Final: 2 4 6 8 10 12 14 16 1 3 5 7 9 11 13 15
关于这种排列产生的方式,我们有一些有用的观察结果。首先,请注意,第一个元素在此排列中不移动,因为偶数索引元素应该显示在数组的前面,并且它是第一个偶数索引元素。接下来,请注意,最后一个元素在此置换中不移动