如果你真的只需要生成combinations——元素的顺序无关紧要——你可以使用combinadics,因为它们是implemented e.g. here by James McCaffrey。
将此与 k-permutations 进行对比,其中元素的顺序确实很重要。
第一种情况(1,2,3), (1,3,2), (2,1,3), (2,3,1), (3,1,2), (3,2,1) 被认为是相同的- 在后者中,它们被认为是不同的,尽管它们包含相同的元素。
如果您需要组合,您可能真的只需要生成一个随机数(尽管它可能有点大) - 可以直接用于查找第 m 个组合。
由于此随机数代表特定组合的索引,因此您的随机数应介于 0 和 C(n,k) 之间。
计算组合数也可能需要一些时间。
这可能只是不值得麻烦 - 除了Jerry's and Federico's answer 肯定比实现组合更简单。
但是,如果您真的只需要一个组合,并且您对生成所需的确切随机位数量感到厌烦...... ;-)
虽然不清楚您是想要组合还是 k 排列,但这里是后者的 C# 代码(是的,如果 x > y/2,我们只能生成一个补码,但是我们会留下一个必须洗牌才能得到真正的 k 排列的组合):
static class TakeHelper
{
public static IEnumerable<T> TakeRandom<T>(
this IEnumerable<T> source, Random rng, int count)
{
T[] items = source.ToArray();
count = count < items.Length ? count : items.Length;
for (int i = items.Length - 1 ; count-- > 0; i--)
{
int p = rng.Next(i + 1);
yield return items[p];
items[p] = items[i];
}
}
}
class Program
{
static void Main(string[] args)
{
Random rnd = new Random(Environment.TickCount);
int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7 };
foreach (int number in numbers.TakeRandom(rnd, 3))
{
Console.WriteLine(number);
}
}
}
另一个更精细的实现生成k-permutations,我一直在闲逛,我相信如果您只需要迭代结果,它在某种程度上是对现有算法的改进。虽然它还需要生成 x 个随机数,但它在过程中只使用 O(min(y/2, x)) 内存:
/// <summary>
/// Generates unique random numbers
/// <remarks>
/// Worst case memory usage is O(min((emax-imin)/2, num))
/// </remarks>
/// </summary>
/// <param name="random">Random source</param>
/// <param name="imin">Inclusive lower bound</param>
/// <param name="emax">Exclusive upper bound</param>
/// <param name="num">Number of integers to generate</param>
/// <returns>Sequence of unique random numbers</returns>
public static IEnumerable<int> UniqueRandoms(
Random random, int imin, int emax, int num)
{
int dictsize = num;
long half = (emax - (long)imin + 1) / 2;
if (half < dictsize)
dictsize = (int)half;
Dictionary<int, int> trans = new Dictionary<int, int>(dictsize);
for (int i = 0; i < num; i++)
{
int current = imin + i;
int r = random.Next(current, emax);
int right;
if (!trans.TryGetValue(r, out right))
{
right = r;
}
int left;
if (trans.TryGetValue(current, out left))
{
trans.Remove(current);
}
else
{
left = current;
}
if (r > current)
{
trans[r] = left;
}
yield return right;
}
}
一般的想法是做一个Fisher-Yates shuffle和memorize the transpositions in the permutation。
它没有在任何地方发表,也没有得到任何同行评议。我相信这是一种好奇心,而不是有一些实用价值。尽管如此,我对批评持开放态度,并且通常想知道您是否发现它有任何问题 - 请考虑这一点(并在投反对票之前添加评论)。