如何判断数组是否是O(n)中的排列?

INS*_*INS 39 arrays algorithm permutation

输入:包含从1到N的整数值的N个元素的只读数组(某些整数值可以出现多次!).和固定大小的存储区(10,100,1000等 - 依赖于N).

如何在O(n)中判断数组是否代表一个排列?

-什么我实现了迄今(答案证明,这是不是好): -

  1. 我使用有限的内存区域来存储数组的总和和乘积.
  2. 我将总和与N*(N + 1)/ 2和产品与N进行比较!

我知道如果条件(2)为真,我可能会有一个排列.我想知道是否有办法证明条件(2)足以判断我是否有排列.到目前为止,我还没想出来......

Jas*_*n S 16

我有点怀疑有一个解决方案.你的问题似乎与几年前在数学文献中提出的问题非常接近,这里给出的摘要("重复检测问题",S.Kamal Abdali,2003)使用循环检测 - 这个想法如下:

如果存在重复,则存在j介于1和N之间的数字,以便以下将导致无限循环:

x := j;
do
{
   x := a[x];
}
while (x != j);
Run Code Online (Sandbox Code Playgroud)

因为置换由不同元素s 0,s 1,... s k-1的一个或多个子集S组成,其中对于1和k-1之间的所有j ,s j = a [s j-1 ],并且s 0 = a [s k-1 ],因此所有元素都涉及周期 - 其中一个重复项不会成为这样一个子集的一部分.

例如,如果该数组= [2,1,4,6,8,7,9,3,8]

然后在位置5处以粗体显示的元素是重复的,因为所有其他元素形成循环:{2 - > 1,4 - > 6 - > 7 - > 9 - > 8 - > 3}.阵列[2,1,4,6,5,7,9,3,8]和[2,1,4,6,3,7,9,5,8]是有效排列(周期为{2} - > 1,4 - > 6 - > 7 - > 9 - > 8 - > 3,5}和{2 - > 1,4 - > 6 - > 7 - > 9 - > 8 - > 5 - > 3}分别).

阿卜达利进入了寻找重复的方式.基本上,以下算法(使用Floyd的循环查找算法)可以在您遇到的其中一个重复项中发生:

function is_duplicate(a, N, j)
{
     /* assume we've already scanned the array to make sure all elements
        are integers between 1 and N */
     x1 := j;
     x2 := j;
     do
     {             
         x1 := a[x1];
         x2 := a[x2];
         x2 := a[x2];
     } while (x1 != x2);

     /* stops when it finds a cycle; x2 has gone around it twice, 
        x1 has gone around it once.
        If j is part of that cycle, both will be equal to j. */
     return (x1 != j);
}
Run Code Online (Sandbox Code Playgroud)

困难在于我不确定你所说的问题是否与他论文中的问题相符,而且我也不确定他描述的方法是在O(N)中运行还是使用固定数量的空间.一个潜在的反例是以下数组:

[3,4,5,6,7,8,9,10 ...... N-10,N-9,N-8,N-7,N-2,N-5,N-5,N- 3,N-5,N-1,N,1,2]

这基本上是身份置换移动了2,元素[N-6,N-4和N-2]被[N-2,N-5,N-5]取代.这有正确的总和(不是正确的产品,但我拒绝将产品作为一种可能的检测方法,因为用任意精度算术计算N!的空间要求是O(N),这违反了"固定存储空间"的精神.要求),如果你试图寻找周期,你将获得周期{3 - > 5 - > 7 - > 9 - > ... N-7 - > N-5 - > N-1}和{4 - > 6 - > 8 - > ... N-10 - > N-8 - > N-2 - > N - > 2}.问题是可能有多达N个周期(身份置换有N个周期),每个周期都占用O(N)来找到重复,你必须跟踪某些已经跟踪的周期和哪些没有.一世' 我怀疑在固定的空间内可以做到这一点.但也许是.

这是一个足够严重的问题,值得在mathoverflow.net询问(尽管大多数时候mathoverflow.net都是在stackoverflow上引用的,但是这些问题太容易了)


编辑:我确实问过mathoverflow,那里有一些有趣的讨论.


Cra*_*ney 10

这在O(1)空间中是不可能的,至少使用单扫描算法.

证明

假设您已经处理了N个元素中的N/2个.假设序列是一个置换,那么,给定算法的状态,你应该能够找出N/2个剩余元素的集合.如果你无法找出剩余的元素,那么可以通过重复一些旧元素来欺骗算法.

N选择N/2个可能的剩余集.它们中的每一个都必须由算法的不同内部状态表示,否则你无法找出剩余的元素.但是,它需要对数空间来存储X状态,因此需要BigTheta(log(N选择N/2))空间来存储N选择N/2个状态.该值随N增长,因此算法的内部状态适合O(1)空间.

更正式的证明

你想创建一个程序P,给定最后的N/2个元素和线性时间常数空间算法在处理N/2个元素后的内部状态,确定整个序列是否为1的置换. .N.此次要计划没有时间或空间限制.

假设P存在,我们可以创建一个程序Q,只采用线性时间常数空间算法的内部状态,该算法确定序列的必要最终N/2个元素(如果它是一个置换).Q通过传递P每个可能的最终N/2个元素并返回P返回true的集合.

但是,因为Q有N选择N/2个可能的输出,所以它必须至少有N个选择N/2个可能的输入.这意味着原始算法的内部状态必须存储至少N个选择N/2个状态,需要BigTheta(log N选择N/2),这大于常数大小.

因此,如果具有恒定大小的内部状态,则具有时间和空间界限的原始算法也不能正确地工作.

[我认为这个想法可以概括,但思考并没有证明.]

后果

BigTheta(log(N选择N/2))等于BigTheta(N).因此,只需使用布尔数组并在遇到它们时勾选值(可能)是空间最优的,并且时间也是最佳的,因为它需要线性时间.

  • 你已经证明问题是_not subdividable_,但并不是说它在'O(N)'时间内无法解决.你怎么知道没有策略在列表中的"N/2"处,你可能仍然需要重新访问列表的前面部分来处理剩下的部分?只要你做得很少,它仍然可以是"O(N)". (2认同)

Jul*_*les 5

我怀疑你能否证明这一点;)

  (1, 2, 4, 4, 4, 5, 7, 9, 9)
Run Code Online (Sandbox Code Playgroud)

我认为更一般地说,这个问题不能通过按顺序处理数字来解决.假设您按顺序处理元素,并且您在数组的中间.现在你的程序状态必须以某种方式反映你到目前为止遇到的数字.这需要至少O(n)位来存储.

  • 这更像是一个评论而不是一个答案,因为它实际上并没有回答这个问题. (2认同)