从n“随机”堆栈中查找k个元素的所有组合的算法

时间:2009-05-11 10:03:11

标签: algorithm combinations

我遇到类似于此处描述的问题:

Algorithm to return all combinations of k elements from n

我正在寻找类似的东西,涵盖了来自n的k的所有可能组合。但是,我需要一个子集,与之前绘制的子集有很大不同。例如,如果我要从一组8中绘制3个元素的子集,则以下算法对我没用,因为每个子集都与之前绘制的子集非常相似:

11100000, 11010000, 10110000, 01110000, ...

我正在寻找一种能够以更“随机”的方式选择子集的算法,即。其中一个子集中的大多数元素未在下一个中重复使用:

11100000, 00010011, 00101100, ...

有谁知道这样的算法?

我希望我的问题能够成为现实,有人可以帮助我=)

亲切的问候,

基督教

6 个答案:

答案 0 :(得分:1)

如何首先从n生成k的所有可能组合,然后借助随机函数重新排列它们。

如果你有一个向量的结果,循环遍历向量:对于每个元素,让它改变位置,元素在随机位置。

对于大k和n来说,这当然变慢了。

答案 1 :(得分:1)

这不是随机的,但根据您的需要,它可能适合您。

  1. 计算可能的组合数。我们将它们命名为N。
  2. 计算一个与N互质的大数字。我们将其命名为P.
  3. 对组合进行排序并给出从1到N的数字。让我们将它们命名为C 1 到C N
  4. 迭代输出组合。第一个是V P mod N ,第二个是C 2 * P mod N ,第三个是C 3 * P mod N ,etc。本质上,Output i = C i * P mod N。 Mod是指模数运算符。
  5. 如果仔细挑选P,你会得到看似随意的组合。接近1或N的值将产生相差很小的值。更好的选择值接近,比如N / 4或N / 5。您还可以为您需要的每次迭代随机化P的生成。

答案 2 :(得分:1)

作为我对this answer的评论的后续行动,以下是一些代码,允许用户以“紧凑的顺序”从“索引”中确定子集的构成。 从我自己的任务中无耻地偷走了。

//////////////////////////////////////
// NChooseK
//
// computes    n!
//          --------
//          k!(n-k)!
//
// using Pascal's identity
// i.e. (n,k) = (n-1,k-1) + (n-1,k)
//
// easily optimizable by memoization
long long NChooseK(int n, int k)
{
    if(k >= 0 && k <= n && n >= 1)
    {
        if( k > n / 2)
            k = n - k;
        if(k == 0 || n == 0)
            return 1;
        else
            return NChooseK(n-1, k-1) + NChooseK(n-1, k);
    } 
    else
        return 0;
}

///////////////////////////////////////////////////////////////////////
// SubsetColexUnrank
//  The unranking works by finding each element
//  in turn, beginning with the biggest, leftmost one.
//  We just have to find, for each element, how many subsets there are
//  before the one beginning with the elements we have already found.
//
// It stores its results (indices of the elements present in the subset) into T, in ascending order.
void SubsetColexUnrank(long long r, int * T, int subsetSize)
{
    assert( subsetSize >= 1 );

    // For each element in the k-subset to be found
    for(int i = subsetSize; i >= 1; i--)
    {
        // T[i] cannot be less than i
        int x = i;

        // Find the smallest element such that, of all the k-subsets that contain it,
        // none has a rank that exceeds r.            
        while( NChooseK(x, i) <= r )
            x++;

        // update T with the newly found element
        T[i] = x;

        // if the subset we have to find is not the first one containing this element
        if(r > 0)
        {
            // finding the next element of our k-subset
            // is like finding the first one of the same subset
            // divided by {T[i]}
            r -= NChooseK(x - 1, i);
        }
    }
}

随机输入,随机输出。

colex order是这样的,它的unranking函数不需要从中选择元素的集合的大小;假设元素的数量为NChooseK(集合的大小,子集的大小)。

答案 3 :(得分:0)

如何随机选择k个元素。即选择p在1和n之间随机的pth,然后重新排序剩下的并选择q在1和n-1等之间的qth?

或者我误解了。你还想要所有的可能吗?在这种情况下,您始终可以先生成它们,然后从列表中选择随机条目

答案 4 :(得分:0)

通过“随意查看”我认为你的意思是按字典顺序排列..这是否适用于组合ii-1i相对于之前的所有组合?

如果是这样,这里有一些建议:

  • 由于大多数组合器产生有序输出,因此有两种选择:

    1. 设计或找到以某种方式产生无序输出的发电机
    2. 在tie'd数组文件/ db
    3. 中枚举并存储足够/所有组合
  • 如果您决定使用2号门,那么您可以通过1和组合之间的随机整数访问随机排序的组合

  • 正如最终检查一样,使用组合之间的差异/距离度量来比较当前和之前的组合,例如:对于Perl中的无符号Bit::Vector

    $ vec1-&gt; Lexicompare($ vec2)&gt; = $ MIN_LEX_DIST

  • 您可能会再次关注#1门,因为即使是nk的中等值,您也可以得到一个大阵列:

  

C(n,k) = n!/(k!(n-k)!)

编辑:

刚刚看到你对AnnK的评论......也许lexicompare可能仍然可以帮助你跳过类似的组合?

答案 5 :(得分:0)

根据你想要做的事情,你可以做一些像扑克牌一样的事情。保留两个列表:Source是您的源(未使用)列表;和Used第二个是“已经选择”的列表。当您从k中随机选择Source项时,会将其移至Used列表。

如果您需要再次选择时k中有Source个项目,请选择所有项目并交换列表。如果项目少于k,您可以从j中选择Used项,然后将其添加到Source以在k中生成Source项,然后挑选它们并交换清单。

这有点像从牌组中挑选k张牌。你将它们丢弃到用过的堆中。一旦你到达终点或需要更多的牌,你就可以将旧牌重新洗牌。

这只是为了确保每个集合与以前的子集明确不同。 此外,这并不能保证在旧的子集开始重复之前选择所有可能的子集。

好处是您不必担心预先计算所有子集,并且您的内存要求与数据呈线性关系(2 n - 大小的列表)。

相关问题