用子线性存储器生成排列

时间:2011-11-07 23:39:43

标签: algorithm complexity-theory generator permutation memory-efficient

我想知道是否有足够简单的算法来生成N个元素的排列,比如1..N,它使用的内存少于O(N)。它不必是计算第n个排列,但它必须能够计算所有排列。

当然,这个算法应该是某种类型的生成器,或者使用一些使用少于O(N)内存的内部数据结构,因为将结果作为大小为N的向量返回已经违反了对子线性记忆的限制。

5 个答案:

答案 0 :(得分:1)

假设随机排列一次生成一个条目。生成器的状态必须对剩余的元素集进行编码(运行完成),因此,由于不能排除任何可能性,因此生成器状态至少为n位。

答案 1 :(得分:0)

我认为答案必须是" no"。

将N元素排列的生成器视为状态机:它必须包含至少与排列一样多的状态,否则它将在完成所有状态生成之前开始重复。

有N!这样的排列,至少需要ceil(log2(N!))位来表示。 Stirling's approximation告诉我们log2(N!)是O(N log N),因此我们将无法使用子线性内存创建这样的生成器。

答案 2 :(得分:0)

C ++算法next_permutation执行序列的就地重新排列到其下一个排列中,或者在不存在进一步排列时返回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;
        }
    }
}

这为生成的每个排列使用常量空间(迭代器)。你认为这是线性的吗?

答案 3 :(得分:0)

我认为甚至存储你的结果(这将是N项的有序列表)将在内存中是O(N),不是吗?

无论如何,要回答你后来随机挑选排列的问题,这里的技术将比生产所有N更好!列表中的可能性,比如说,然后随机选择一个索引。如果我们可以随机选择索引并从中生成相关的排列,我们就会好多了。

我们可以想象您的单词/排列上的字典顺序,并根据单词/排列在字典中的出现顺序将唯一的数字与这些顺序相关联。例如,三个字符的单词将是

   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}(设置减法)。对于Q = R mod(N-1)!我们想要这个列表的Qth置换。除了考虑到缺少数字这一事实外,这与我们刚刚解决的问题相同。递归。

答案 4 :(得分:0)

也许你可以用事实数字来表达。您可以逐步从中提取结果排列,因此您永远不必将整个结果存储在内存中。

但我开始的原因可能是,我不确定事实数字本身大小的增长行为是什么。如果它适合32位整数或类似的东西,N将被限制为常数,因此O(N)将等于O(1),所以我们必须使用数组,但我不确定它会有多大以N为单位。