Algorithm 在一个数组中交错三个大小相等的分区,并在O(n)时间内就位

Algorithm 在一个数组中交错三个大小相等的分区,并在O(n)时间内就位,algorithm,Algorithm,给定一个大小为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可以是任何整数。请参阅下面的输入和输出示例 两个约束条件 O(n)中的Do O(1)内存(就地) 输入和输出示例如下所示 输入: [5,8,11,3,2,17,21,1,9]3n=9。所以n=3 这里 x1=5x2=8x3=11y1=3y2=2y3=17z1=21z2=1

给定一个大小为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可以是任何整数。请参阅下面的输入和输出示例

两个约束条件

  • O(n)中的Do
  • O(1)内存(就地)
  • 输入和输出示例如下所示

    输入:
    [5,8,11,3,2,17,21,1,9]
    3n=9。所以n=3

    这里
    x1=5x2=8x3=11y1=3y2=2y3=17z1=21z2=1z3=9

    输出:
    [5,3,21,8,2,1,11,17,9]

    一个可能的O(n log n)解决方案:
    只考虑x和y。现在我可以将所有y换到它的位置,这将使x2、x4、x6换出位置。然后我将交换x2,x4,这将使x3,x7离开位置。下一次迭代是x8,x16。这将把我带到O(n log n)而不是O(n)。

    这个答案基于作者的工作(他的参考书目非常不完整,但我不想花时间来整理原位移位问题的历史)。注意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为

    在这种情况下,Jain的观察结果是,如果3*n-1是5的幂,那么上面的排列有log_5(3*n-1)+1个不同的循环,k从0到log_5(3*n-1)由5^k引导。(这或多或少是基元根的定义。)对于每个循环,我们所要做的就是移动引线,移动引线置换的元素,移动引线置换的元素,等等,直到我们返回引线

    对于其他数组大小,将数组分解为长度为3的O(对数n)隐式子数组和可被3:6、126、3126、78126等整除的1加5次幂。执行一系列旋转,以几何方式减小大小,以使子数组连续,然后运行上述算法


    如果您实际实现了这一点,请对其进行基准测试。我对Jain算法的基本情况(3^n-1,成对而不是三元组)进行了测试,发现,在我的机器上,对于非银河系输入大小,O(n logn)时间算法更快。当然是YMMV。

    因为David似乎对写下来不感兴趣(很明显,他感兴趣,请看另一个答案:),我将使用它来为3个分区的情况得出一个算法

    首先请注意,如果我们可以使用算法A有效地解决某个m
    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能让我们很容易地解出左边的部分

    为了解决这个问题,我们需要认识到我们想要实现的是数组索引的一个特定的p。每个排列
    a0->a1->…->a{k-1}->a0
    ,其中P(ai)=a{(i+1)%k}。它很容易处理这样一个循环到位,算法概述

    现在的问题是,在您完成了一个周期的处理之后,要找到一个元素,它是您尚未处理的周期的一部分。对于这个问题没有通用的解决方案,但是对于某些特定的排列,有一些很好的公式来描述作为不同循环一部分的位置

    对于您的问题,您只需选择m=(5^(2k)-1)/3,这样m 我们递归地解决剩余的右半部分,并得到一个算法来及时解决问题

    T(n) = O(m) + T(n - m)
    

    因为m>=Omega(n),我们得到T(n)=O(n)。

    我做了研究。我有O(nlogn)解决方案,但不能超过that@AlexandruSeverin我想你被x1,x2。。。x的y和z可以是任意随机数。它与排序无关——也许是重复的(尽管答案有点糟糕)——。也。(如果你把它改成
    x,y,z
    变成
    a,b,c
    ,谷歌搜索起来会非常容易。)@Dukeling肯定是相关的,但一般的问题可能比Nx3难很多。特别是循环顺序取决于N和M,对于M=3可能比任意MAlso更容易实现。抱歉,低估了蜂巢思维交付所需的时间:|我测试了两种尺寸,一种是1000,另一种是10米。你是对的,对于1000,O(n logn)更快。