我遇到类似于此处描述的问题:
Algorithm to return all combinations of k elements from n
我正在寻找类似的东西,涵盖了来自n的k的所有可能组合。但是,我需要一个子集,与之前绘制的子集有很大不同。例如,如果我要从一组8中绘制3个元素的子集,则以下算法对我没用,因为每个子集都与之前绘制的子集非常相似:
11100000, 11010000, 10110000, 01110000, ...
我正在寻找一种能够以更“随机”的方式选择子集的算法,即。其中一个子集中的大多数元素未在下一个中重复使用:
11100000, 00010011, 00101100, ...
有谁知道这样的算法?
我希望我的问题能够成为现实,有人可以帮助我=)
亲切的问候,
基督教
答案 0 :(得分:1)
如何首先从n生成k的所有可能组合,然后借助随机函数重新排列它们。
如果你有一个向量的结果,循环遍历向量:对于每个元素,让它改变位置,元素在随机位置。
对于大k和n来说,这当然变慢了。
答案 1 :(得分:1)
这不是随机的,但根据您的需要,它可能适合您。
如果仔细挑选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)
通过“随意查看”我认为你的意思是按字典顺序排列..这是否适用于组合i
与i-1
或i
相对于之前的所有组合?
如果是这样,这里有一些建议:
由于大多数组合器产生有序输出,因此有两种选择:
如果您决定使用2号门,那么您可以通过1和组合之间的随机整数访问随机排序的组合
正如最终检查一样,使用组合之间的差异/距离度量来比较当前和之前的组合,例如:对于Perl中的无符号Bit::Vector
:
$ vec1-&gt; Lexicompare($ vec2)&gt; = $ MIN_LEX_DIST
您可能会再次关注#1门,因为即使是n
和k
的中等值,您也可以得到一个大阵列:
编辑:
刚刚看到你对AnnK的评论......也许lexicompare可能仍然可以帮助你跳过类似的组合?
答案 5 :(得分:0)
根据你想要做的事情,你可以做一些像扑克牌一样的事情。保留两个列表:Source
是您的源(未使用)列表;和Used
第二个是“已经选择”的列表。当您从k
中随机选择Source
项时,会将其移至Used
列表。
如果您需要再次选择时k
中有Source
个项目,请选择所有项目并交换列表。如果项目少于k
,您可以从j
中选择Used
项,然后将其添加到Source
以在k
中生成Source
项,然后挑选它们并交换清单。
这有点像从牌组中挑选k
张牌。你将它们丢弃到用过的堆中。一旦你到达终点或需要更多的牌,你就可以将旧牌重新洗牌。
这只是为了确保每个集合与以前的子集明确不同。 此外,这并不能保证在旧的子集开始重复之前选择所有可能的子集。
好处是您不必担心预先计算所有子集,并且您的内存要求与数据呈线性关系(2 n
- 大小的列表)。