说我有[0, ....., 499]
的一组数字。目前正在使用C ++ std::next_permutation
顺序生成组合。作为参考,我提取的每个元组的大小是3,所以我返回顺序结果,如[0,1,2], [0,1,3], [0,1,4], ... [497,498,499]
。
现在,我想并行处理它所代表的代码,因此这些组合的顺序生成将不再有效。是否存在用于计算500个数字中的ith
3个组合的现有算法?
我想确保每个线程,无论它获得的循环迭代如何,都可以根据迭代的i
计算独立组合。因此,如果我想在线程1中使用i=38
的组合,我可以计算[1,2,5]
,同时将线程2中的i=0
计算为[0,1,2]
。
编辑以下声明无关紧要,我把自己搞砸了
我已经研究过利用因子来从左到右缩小每个元素的算法,但我不能将它们用作500!肯定不适合记忆。有什么建议吗?
答案 0 :(得分:5)
这是我的镜头:
int k = 527; //The kth combination is calculated
int N=500; //Number of Elements you have
int a=0,b=1,c=2; //a,b,c are the numbers you get out
while(k >= (N-a-1)*(N-a-2)/2){
k -= (N-a-1)*(N-a-2)/2;
a++;
}
b= a+1;
while(k >= N-1-b){
k -= N-1-b;
b++;
}
c = b+1+k;
cout << "["<<a<<","<<b<<","<<c<<"]"<<endl; //The result
考虑到下一个数字增加之前有多少组合。但它只适用于三个元素。我无法保证这是正确的。如果你将它与你的结果进行比较并给出一些反馈,那就太酷了。
答案 1 :(得分:1)
如果您正在寻找获取词典索引或独特组合排名而不是排列的方法,那么您的问题属于二项式系数。二项式系数处理在K组中选择唯一组合以及总共N个项目的问题。
我在C#中编写了一个类来处理使用二项式系数的常用函数。它执行以下任务:
以任意N选择K到文件的格式输出所有K索引。 K-index可以用更具描述性的字符串或字母代替。
将K索引转换为正确的词典索引或排序二项式系数表中条目的等级。这种技术比依赖迭代的旧发布技术快得多。它通过使用Pascal三角形中固有的数学属性来实现这一点,并且与迭代集合相比非常有效。
将已排序的二项系数表中的索引转换为相应的K索引。我相信它也比旧的迭代解决方案更快。
使用Mark Dominus方法计算二项式系数,这样就不太可能溢出并使用更大的数字。
该类是用.NET C#编写的,它提供了一种通过使用通用列表来管理与问题相关的对象(如果有)的方法。此类的构造函数采用名为InitTable的bool值,当为true时,将创建一个通用列表来保存要管理的对象。如果此值为false,则不会创建表。为了使用上述4种方法,不需要创建该表。提供访问者方法来访问该表。
有一个关联的测试类,它显示了如何使用该类及其方法。它已经过2个案例的广泛测试,并且没有已知的错误。
要阅读此课程并下载代码,请参阅Tablizing The Binomial Coeffieicent。
以下测试代码将遍历每个独特的组合:
public void Test10Choose5()
{
String S;
int Loop;
int N = 500; // Total number of elements in the set.
int K = 3; // Total number of elements in each group.
// Create the bin coeff object required to get all
// the combos for this N choose K combination.
BinCoeff<int> BC = new BinCoeff<int>(N, K, false);
int NumCombos = BinCoeff<int>.GetBinCoeff(N, K);
// The Kindexes array specifies the indexes for a lexigraphic element.
int[] KIndexes = new int[K];
StringBuilder SB = new StringBuilder();
// Loop thru all the combinations for this N choose K case.
for (int Combo = 0; Combo < NumCombos; Combo++)
{
// Get the k-indexes for this combination.
BC.GetKIndexes(Combo, KIndexes);
// Verify that the Kindexes returned can be used to retrive the
// rank or lexigraphic order of the KIndexes in the table.
int Val = BC.GetIndex(true, KIndexes);
if (Val != Combo)
{
S = "Val of " + Val.ToString() + " != Combo Value of " + Combo.ToString();
Console.WriteLine(S);
}
SB.Remove(0, SB.Length);
for (Loop = 0; Loop < K; Loop++)
{
SB.Append(KIndexes[Loop].ToString());
if (Loop < K - 1)
SB.Append(" ");
}
S = "KIndexes = " + SB.ToString();
Console.WriteLine(S);
}
}
你应该可以很容易地将这个类移植到C ++上。您可能不必移植类的通用部分来实现您的目标。您的500选择3的测试用例产生20,708,500个唯一组合,这将适合4字节int。如果500选择3只是一个示例,你需要选择大于3的组合,那么你将不得不使用long或者固定点int。
答案 2 :(得分:0)
您可以将500个对象中的3个特定选项描述为三(i, j, k)
,其中i
是0到499之间的数字(第一个数字的索引),{{1} }范围从0到498(第二个索引,跳过第一个数字),j
范围从0到497(最后一个索引,跳过以前选择的数字)。鉴于此,实际上很容易枚举所有可能的选择:从k
开始,递增(0,0,0)
直到达到其最大值,然后递增k
并重置j
到0,依此类推,直到k
达到最大值,依此类推,直到j
达到自己的最大值;然后递增j
并重置i
和j
并继续。
如果这个描述听起来很熟悉,那是因为它与递增基数为10的数字完全相同,除了基数更加有趣,实际上基数从数字到数字变化 。您可以使用此洞察力来实现该概念的非常紧凑的版本:对于0到500 * 499 * 498之间的任何整数k
,您可以获得:
n
现在要分片,你可以从0到500 * 499 * 498中取数字,以你想要的任何方式将它们分成子范围,并让每个分片计算其子范围中每个值的排列。
对于需要枚举子集的任何问题,此技巧非常方便。