如何使用Linq生成唯一的随机数

时间:2010-07-15 08:00:54

标签: linq

这是我的Linq代码,用于生成随机数列表,其中包含10个数字,范围从0到20

Random rand = new Random();
var randomSeq = Enumerable.Repeat(0, 10).Select(i => rand.Next(0,20));

结果:

6

19

18

7

18

12

12

9

2

18

你可以看到我有三个18和两个12 ..

我曾尝试使用Distinct()函数,但它不会填满列表(例如只填充10个数字中的8个) 问题:如何生成唯一编号(即不可重复的数字) 非常感谢

10 个答案:

答案 0 :(得分:8)

您希望生成数字0到19的随机排列,并选择其中10个数字。用于生成随机排列的标准算法是Fisher-Yates shuffle。生成随机排列后,您可以选择前10个数字。

如果碰撞发生但是它们通常无法具有良好的统计属性,具有不确定的运行时间或者甚至不能保证在最坏的情况下终止,那么就不难想出像ad-hoc算法那样重复选择新的数字。情况下。

请注意,如果数字的顺序不同,则此解决方案不是一个好选择。生成数百万以下的数字只能选择10,这不是最聪明的事情。

<强>更新

我刚刚意识到你可以在生成排列的前十个元素后停止算法 - 不需要构建整个排列。

答案 1 :(得分:5)

在函数式编程中,通常会创建无限序列。起初听起来有点奇怪,但在某些情况下它可能非常有用。假设你有这样的延伸:

public static class EnumerableExtentions
{
    public static IEnumerable<T> Infinite<T>(Func<int, T> generator)
    {
        int count = 0;
        checked {
            while (true)
                yield return generator(count++);
        }
    }
}

我可以用它来创建无限的序列,如:

var oddNumbers = EnumerableExtentions.Infinite(n => 2*n + 1)

这是所有奇数的无限序列。我可以只采取前10个,例如:

oddNumbers.Take(10);

会产生:

1 3 5 7 9 11 13 15 17 19

由于延迟执行,我们得不到StackOverflowException(你必须小心)。

可以使用相同的原理创建无限随机序列,区分它然后取第10个:

var r = new Random();
var randomNumbers = EnumerableExtentions
                    .Infinite(i=> r.Next (0, 20))
                    .Distinct()
                    .Take(10);

如果需要,您可以在最后制作OrderBy(s =&gt; s)。

答案 2 :(得分:3)

LINQ exchange,他们讨论了一种使用LINQ随机重新排序列表的方法,并给出一个代码示例,它将生成所需数字的随机排列。

他们说(解释,并适应这个问题):

使用LINQ OrderBy随机排序列表数组

// create and populate the original list with 20 elements
   List<int> MyList = new List<int>(20);
   for (int i = 0; i < 20; i++)
   MyList.Add(i);
// use System.GUID to generate a new GUID for each item in the list
   List<int> RandomList = MyList.OrderBy(x => System.Guid.NewGuid()).ToList();

LINQ OrderBy然后将按返回的GUID列表对数组进行排序。

现在你可以拿下列表的前10个元素,然后就可以得到解决方案了。

他们注意到使用System.Guid.NewGuid()会产生与Fisher-Yates shuffle算法相同的分布范围,这样您就不必自己实际实现算法。

答案 3 :(得分:2)

为什么不这样做:

Enumerable.Range(0, 20)
          .OrderBy(x => Guid.NewGuid().GetHashCode())
          .Distinct()
          .Take(10)
          .ToArray();

答案 4 :(得分:1)

只需创建顺序有效数字列表即可。然后从该列表中生成一个随机索引,并返回(并从列表中删除)索引处的数字。

static class Excensions
{
    public static T PopAt<T>(this List<T> list, int index)
    {
        T ret = list[index];
        list.RemoveAt(index);
        return ret;
    }
}

class Program
{
    static void Main()
    {
        Random rng = new Random();
        int length = 10; //sequence length
        int limit = 20; //maximum value
        var avail = Enumerable.Range(0, limit).ToList();
        var seq = from i in Enumerable.Range(0, length)
                  select avail.PopAt(rng.Next(avail.Count));
    }
}

答案 5 :(得分:1)

如何使用实用程序可枚举方法:

    static IEnumerable<int> RandomNumbersBetween(int min, int max) 
    {
        int availableNumbers = (max - min) + 1 ;
        int yieldedNumbers = 0;

        Random rand = new Random();
        Dictionary<int, object> used = new Dictionary<int, object>();

        while (true)
        {
            int n = rand.Next(min, max+1); //Random.Next max value is exclusive, so add one
            if (!used.ContainsKey(n))
            {
                yield return n;
                used.Add(n, null);

                if (++yieldedNumbers == availableNumbers)
                    yield break;
            }
        }
    }

因为它返回IEnumerable,你可以将它与LINQ和IEnumerable扩展方法一起使用:

RandomNumbersBetween(0, 20).Take(10)

或者只取奇数:

RandomNumbersBetween(1, 1000).Where(i => i%2 == 1).Take(100)

等等。

修改

请注意,如果您尝试在minmax之间生成一组完整的随机数,则此解决方案具有糟糕的性能特征。

然而,如果你想生成0到20之间的10个随机数,甚至更好,0到1000之间,它会有效地工作。

在最糟糕的情况下,它还可以占用(max - min)空间。

答案 6 :(得分:0)

将生成的结果存储在一个数组中,因此任何时候生成新的数字检查是否已经生成过,如果是,则生成另一个,否则取数字并将其保存在数组中

答案 7 :(得分:0)

使用自定义RepeatUntil扩展并依赖闭包:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1
{
    public static class CoolExtensions
    {
        public static IEnumerable<TResult> RepeatUntil<TResult>( TResult element, Func<bool> condition )
        {
            while (!condition())
                yield return element;
        }
    }

    class Program
    {
        static void Main( string[] args )
        {
            Random rand = new Random();
            HashSet<int> numbers = new HashSet<int>();

            var randomSeq = CoolExtensions.RepeatUntil( 0, () => numbers.Count >= 10).Select( i => rand.Next( 0, 20 ) ).Select( x => numbers.Add(x));

            // just used to evaluate the sequence
            randomSeq.ToList();

            foreach (int number in numbers)
                Console.WriteLine( number );

            Console.ReadLine();
        }
    }
}

答案 8 :(得分:0)

为什么不随机订购?像这样

var rnd = new Random();
var randomSeq = Enumerable.Range(1,20).OrderBy(r => rnd.NextDouble()).Take(10).ToList();

答案 9 :(得分:-4)

你能做这样的事吗?

Random rand = new Random();
var randomSeq = Enumerable.Range(0, 20).OrderBy(i => rand.Next(0,20)).Take(10);