出于没有特别的原因,我决定寻找一种算法,该算法产生1 ... n之间k个整数的所有可能选择,其中k整数之间的顺序无关紧要(n选择k thingy)。
完全相同的原因,这完全没有理由,我也用C#实现了它。我的问题是:
您是否在我的算法或代码中看到任何错误?而且,更重要的是,你能建议一个更好的算法吗?
请注意算法而不是代码本身。这不是我写过的最漂亮的代码,但要告诉你是否看到错误。
编辑: Alogirthm解释说 -
CODE:
public class AllPossibleCombination
{
int n, k;
int[] indices;
List<int[]> combinations = null;
public AllPossibleCombination(int n_, int k_)
{
if (n_ <= 0)
{
throw new ArgumentException("n_ must be in N+");
}
if (k_ <= 0)
{
throw new ArgumentException("k_ must be in N+");
}
if (k_ > n_)
{
throw new ArgumentException("k_ can be at most n_");
}
n = n_;
k = k_;
indices = new int[k];
indices[0] = 1;
}
/// <summary>
/// Returns all possible k combination of 0..n-1
/// </summary>
/// <returns></returns>
public List<int[]> GetCombinations()
{
if (combinations == null)
{
combinations = new List<int[]>();
Iterate(0);
}
return combinations;
}
private void Iterate(int ii)
{
//
// Initialize
//
if (ii > 0)
{
indices[ii] = indices[ii - 1] + 1;
}
for (; indices[ii] <= (n - k + ii + 1); indices[ii]++)
{
if (ii < k - 1)
{
Iterate(ii + 1);
}
else
{
int[] combination = new int[k];
indices.CopyTo(combination, 0);
combinations.Add(combination);
}
}
}
}
我为长期问题道歉,它可能适合博客文章,但我确实想要社区的意见。
谢谢,
阿萨夫
答案 0 :(得分:9)
在C ++中给出以下例程:
template <typename Iterator>
inline bool next_combination(const Iterator first, Iterator k, const Iterator last)
{
/* Credits: Thomas Draper */
if ((first == last) || (first == k) || (last == k))
return false;
Iterator itr1 = first;
Iterator itr2 = last;
++itr1;
if (last == itr1)
return false;
itr1 = last;
--itr1;
itr1 = k;
--itr2;
while (first != itr1)
{
if (*--itr1 < *itr2)
{
Iterator j = k;
while (!(*itr1 < *j)) ++j;
std::iter_swap(itr1,j);
++itr1;
++j;
itr2 = k;
std::rotate(itr1,j,last);
while (last != j)
{
++j;
++itr2;
}
std::rotate(k,itr2,last);
return true;
}
}
std::rotate(first,k,last);
return false;
}
然后您可以继续执行以下操作:
std::string s = "123456789";
std::size_t k = 3;
do
{
std::cout << std::string(s.begin(),s.begin() + k) << std::endl;
}
while(next_combination(s.begin(),s.begin() + k,s.end()));
答案 1 :(得分:2)
阿萨夫,
您要求我们评估您的算法,但您不解释您的算法 - 即使在代码注释中也是如此。所以你希望每个人花一个小时或更长时间从代码中反向设计算法,这样我们才能在回答之前理解你的问题?
请编辑您的问题以解释您的算法。
有一件事是显而易见的 - 代码的内存占用是可怕的。对于n的适度值,组合的数量很容易达到数十亿,这将需要比大多数计算机更多的内存。此外,您正在使用动态增长的阵列,这些阵列随着它们的增长而不断重新分配和复制。此外,您的程序会在不同的数组中生成子集并合并它们。总而言之,您的程序需要的存储量是存储列表所需的内存量的很多倍,并且它将花费大部分时间来回复制数据。
如果必须同时拥有数组中的所有值,那么至少从计算所需数组的大小开始 - n! /(n-k)! / k! - 然后填写。
更好的是“懒惰”的代码只是根据需要计算序列的每个成员。见this question from the related questions sidebar
答案 2 :(得分:1)
这家伙似乎已经使用C#(CodeProject)在组合学方面做了认真的工作:
Permutations, Combinations, and Variations using C# Generics
答案 3 :(得分:-1)
这是我之前在C中写过的一个相对简单/高效的nCr程序:
main(n,k){float t=0,r=1;for(scanf("%d, %d",&n,&k);t++<k;r*=(1+n-t)/t);printf("%.0f\n",r);}
好的...可读的版本。 =] (不确定这是否与上述相符1:1。)
void nCr(int n, int k) {
float curK = 0, r = 1;
while(curK < k) {
++curK;
printf("%.0f\n", r);
r *= (1 + n - curK) / curK;
}
}
而不是打印,你可以yield
或其他(我不知道C#)进入你的列表。