给出一个大小为3n的数组
[x1, x2, x3... xn, y1, y2, y3... yn, z1, z2, z3... zn]
将其转换为[x1, y1, z1, x2, y2, z2, ... xn, yn, zn]
这里xn,yn,zn可以是任何整数。请参阅下面的示例输入和输出。
两个约束
示例输入和输出如下。
输入:
[5, 8, 11, 3, 2, 17, 21, 1, 9]
3n = 9.所以n = 3。
下面
x1=5 x2=8 x3=11 y1=3 y2=2 y3=17 z1=21 z2=1 z3=9
输出:
[5, 3, 21, 8, 2, 1, 11, 17, 9]
一个可能的O(n log n)soln: 只考虑x和y。现在我可以将所有y换成它的位置,这将使我x2,x4,x6换出位置。然后我将交换x2,x4,这将使x3,x7离开位置。下一次迭代将是x8,x16。这将带我到O(n log n)但不是O(n)。
答案 0 :(得分:3)
这个答案是基于Peiyush Jain的工作(其参考书目非常不完整,但我不想花时间理顺就地换位问题的历史)。观察到3是25 = 5 ^ 2的原始根,因为
>>> len(set(pow(3,n,25)for n in range(25)))
20
和20是欧拉的25个算子。根据Jain的定理1,数论中的经典结果,3是所有5 ^ k的原始根。
当数组长度为3n时,位置k * n + j处元素的新位置为3 * j + k。通常,i的新位置(除了最后一个元素)是(i * n)%(3 * n - 1)。注意,n是3模3 * n - 1的乘法逆,所以3是原始根,当且仅当n是。
在这种情况下,耆那教的观察结果是,如果3 * n - 1是5的幂,则上面的置换有log_5(3 * n - 1)+ 1个不同的周期,由5引导^ k表示从0到log_5(3 * n - 1)的k。 (这或多或少是原始根的定义。)对于每个循环,我们所要做的就是移动领导者,移动被领导者移位的元素,移动被领导者移位的元素移位的元素等,直到我们回到领导者那里。
对于其他数组大小,将数组分解为长度为3的O(log n)隐式子数组,以及可被3:6,126,3126,78126等整除的1加上5的幂。执行一系列旋转,减小几何尺寸,使子阵列连续,然后运行上述算法。
如果您实际执行此操作,请对其进行基准测试。我做了Jain算法的基本情况(3 ^ n - 1,成对而不是三元组)并且发现,在我的机器上, O(n log n)-time算法对于非银河输入大小。 YMMV当然。
答案 1 :(得分:0)
由于大卫似乎没有兴趣写下来(显然他 感兴趣,请参阅另一个答案:),我将使用his reference来获得案例算法有3个分区。
首先请注意,如果我们能够有效地解决某些 m< n 使用算法 A ,我们可以重新排列数组,以便我们可以应用 A ,然后留下一个较小的子问题。假设原始数组是
x1 .. xm x{m+1}.. xn y1 .. ym y{m+1} .. yn z1 .. zm z{m+1} .. zn
我们想将其重新排列为
x1 .. xm y1 .. ym z1 .. zm x{m+1} .. xn y{m+1} .. yn z{m+1} .. zn
这基本上是模式AaBbCc
到ABCabc
的转换,其中A,B,C和a,b,c分别具有相同的长度。我们可以通过一系列逆转来实现这一目标。让X'表示字符串X的反转:
AaBbCc
-> Aa(BbCc)' = Aac'C'b'B'
-> Aac'(C'b')'B' = Aac'bCB'
-> A(ac'bCB')' = ABC'b'ca'
-> ABCb'ca'
-> ABC(b'ca')' = ABCac'b
-> ABCa(c'b)' = ABCab'c
-> ABCabc
这可能是一种较短的方式,但这只是一个恒定的操作次数,所以它只需要线性时间。可以在这里使用更复杂的算法来实现一些循环移位,但这只是一种优化。
现在我们可以递归地解决数组的两个分区,我们已经完成了。
问题仍然存在,什么是一个很好的m,可以让我们轻松解决左边的部分?
为了解决这个问题,我们需要意识到我们想要实现的是数组索引的特定permutation P.每个排列can be decomposed into a set of cycles a0 -> a1 -> ... -> a{k-1} -> a0
,我们有P(ai)= a {(i + 1)%k}。很容易就地处理这样的循环,算法概述on Wikipedia。
现在的问题是,在完成一个循环的处理之后,找到一个尚未处理的循环元素。对此没有通用的解决方案,但是对于某些特定的排列,有很好的公式可以描述不同周期中的位置究竟是什么。
对于你的问题,你只需选择m =(5 ^(2k) - 1)/ 3,这样m< n和k是最大值。作为所有不同周期的一部分的元素序列是5 ^ 0,5 ^ 1,...,5 ^ {k-1}。您可以使用它们在O(m)的数组左侧(移位后)实现循环引导算法。
我们递归地解决剩余的右边部分并获得一个算法来及时解决问题
T(n) = O(m) + T(n - m)
由于m> = Omega(n),我们得到T(n)= O(n)。