Algorithm 用次线性记忆生成置换
我想知道是否有一个足够简单的算法来生成N个元素的排列,比如Algorithm 用次线性记忆生成置换,algorithm,complexity-theory,generator,permutation,memory-efficient,Algorithm,Complexity Theory,Generator,Permutation,Memory Efficient,我想知道是否有一个足够简单的算法来生成N个元素的排列,比如1..N,它使用的内存少于O(N)。它不必计算第n个置换,但必须能够计算所有置换 当然,这个算法应该是某种生成器,或者使用一些使用少于O(N)内存的内部数据结构,因为作为大小N的向量返回结果已经违反了对亚线性内存的限制。我认为答案必须是“否” 将N元素置换生成器视为一个状态机:它必须至少包含与置换相同数量的状态,否则它将在完成所有置换生成之前开始重复 有N个!这种置换至少需要ceil(log2(N!)位来表示。告诉我们log2(N!)是O
1..N
,它使用的内存少于O(N)
。它不必计算第n个置换,但必须能够计算所有置换
当然,这个算法应该是某种生成器,或者使用一些使用少于
O(N)
内存的内部数据结构,因为作为大小N
的向量返回结果已经违反了对亚线性内存的限制。我认为答案必须是“否”
将N元素置换生成器视为一个状态机:它必须至少包含与置换相同数量的状态,否则它将在完成所有置换生成之前开始重复
有N个!这种置换至少需要ceil(log2(N!)位来表示。告诉我们log2(N!)是O(N logn),因此我们将无法创建这样一个具有次线性内存的生成器。让我们假设随机排列一次生成一个条目。生成器的状态必须对剩余的元素集进行编码(运行到完成),因此,由于没有排除的可能性,生成器状态至少为N位。 < P> C++算法<代码> NExtx置换> /CODE>执行序列的适当排列到其下一排列中,或在不存在进一步排列时返回
false
。算法如下:
template <class BidirectionalIterator>
bool next_permutation(BidirectionalIterator first, BidirectionalIterator last) {
if (first == last) return false; // False for empty ranges.
BidirectionalIterator i = first;
++i;
if (i == last) return false; // False for single-element ranges.
i = last;
--i;
for(;;) {
BidirectionalIterator ii = i--;
// Find an element *n < *(n + 1).
if (*i <*ii) {
BidirectionalIterator j = last;
// Find the last *m >= *n.
while (!(*i < *--j)) {}
// Swap *n and *m, and reverse from m to the end.
iter_swap(i, j);
reverse(ii, last);
return true;
}
// No n was found.
if (i == first) {
// Reverse the sequence to its original order.
reverse(first, last);
return false;
}
}
}
模板
布尔下一个置换(首先是双向运算符,最后是双向运算符){
if(first==last)返回false;//空范围为false。
双向运算符i=第一个;
++一,;
if(i==last)返回false;//对于单个元素范围为false。
i=最后;
--一,;
对于(;;){
双向算子ii=i--;
//查找一个元素*n<*(n+1)。
如果(*i=*n。
而(!(*i<*--j)){}
//交换*n和*m,并从m反转到末尾。
国际热核实验堆交换(i,j);
反面(二,最后一个);
返回true;
}
//未发现任何n。
如果(i==第一个){
//将顺序颠倒到原来的顺序。
反向(第一,最后);
返回false;
}
}
}
使用每个生成的置换的常量空间(迭代器)。你认为线性?
< P>我认为即使存储你的结果(这将是n个项目的有序列表)在内存中也将是O(n),不是吗?< /P> 无论如何,为了回答你后面关于随机选择排列的问题,这里有一种技术,它比在一个列表中生成所有N!个可能性,然后随机选择一个索引要好得多。如果我们可以随机选择索引并从中生成相关的排列,我们会过得更好 我们可以想象单词/排列的字典顺序,并根据单词/排列在字典中的出现顺序将唯一的数字与它们关联。例如,三个字符上的单词将是 perm. index
012 <----> 0
021 <----> 1
102 <----> 2
120 <----> 3
201 <----> 4
210 <----> 5
perm.index
012 0
021 1
102 2
120 3
201 4
210 5
稍后您将看到为什么使用我们所做的数字是最容易的,但是其他人可以通过更多的工作来适应
要随机选择一个,您可以从0…N!-1的范围内以统一的概率随机选择它的相关索引(我知道,即使对于中等大小的N,这显然是不可能的最简单实现,但我认为有适当的解决方法)然后确定其相关排列。请注意,列表以最后N-1个元素的所有排列开始,保持第一个数字固定为0。在这些可能性用尽后,我们生成所有以1开头的排列。在这些之后,下一个(N-1)!排列已用尽,我们生成以2开头的排列。依此类推,我们可以确定前导数字是Floor[R/(N-1)!],其中R是上述意义上的索引。现在看到为什么我们也为零索引了吗
为了生成置换中剩余的N-1位数字,假设我们确定了Floor[R/(N-1)!]=a0。从列表{0,…,N-1}-{a0}(集减法)开始。我们需要这个列表的Qth置换,对于Q=R mod(N-1)!.除了考虑缺少一个数字这一事实之外,这与我们刚刚解决的问题是一样的。递归。也许你可以,使用factoradic数字。你可以一步一步地从中提取结果排列,这样你就不必在内存中存储整个结果
但我开始的原因可能是,我不确定factoradic数本身大小的增长行为是什么。如果它适合32位整数或类似的东西,N将被限制为一个常数,因此O(N)将等于O(1),所以我们必须使用一个数组,但我不确定它在N方面会有多大。除非元素对它们有一个预定义的顺序,例如int,这样您就可以在需要时生成下一个,否则不只是接受N个元素作为输入就已经将您置于O(N)了吗内存限制?是的,这就是为什么我说元素是'1..N',所以它们不需要作为输入向量传递。这是怎么回事?O(1)在空间中,但运行时可能是无界的:
Random rnd=…;List l=…;while(l.size()
因为有一个使用n+O(logn)位的生成器,所以这个界基本上是紧的。这一点很好。但是如果我对概率分布均匀的随机置换感兴趣呢?是的。但是有可能生成O(n)位的随机置换吗内存。分布应该是均匀的,否则可以简单地生成1..N,这是一个有效的排列?以便