【问题标题】:Building a non sequential list of numbers (From a large range)建立一个非顺序的数字列表(从一个大范围)
【发布时间】:2009-12-10 15:02:46
【问题描述】:

我需要创建一个适合某个范围的非连续数字列表。例如,我需要生成一个从 1 到 100 万的数字列表,并确保没有一个数字是按顺序排列的,它们是完全打乱的。我想我的第一个问题是,是否有任何好的算法可以提供帮助以及如何最好地实现这一点。

我目前不确定最好的实现方式,无论是通过将在 XML 文件中输出数字的 c# 控制台应用程序,还是在将数字输出到表或一组表中的数据库中,但是这对于实际找出“改组”一组数字的最佳方法来说真的是次要的。

有什么建议吗?

罗伯

【问题讨论】:

  • “完全洗牌”并不意味着两个数字不能连续。创建唯一数字的随机列表的一种可能性是它们都将按顺序排列。能举个例子吗?

标签: c# .net asp.net sql sql-server


【解决方案1】:

首先,如果没有个数字是按顺序排列的,那么序列中的每个数字都必须小于它的前一个数字。具有该属性的序列从大到小排序!显然这不是你想要的。 (或者您可能根本不想要 5、6、7 形式的任何子序列?但是 6、8、20 就可以了?)

要正确回答您的问题,我们需要了解有关问题空间的更多信息。我想知道的事情:

1) 范围的大小是等于、大于还是小于序列的大小?也就是说,你要问1到10之间的十个数字、1到10之间的五个数字还是1到10之间的五十个数字?

2) 序列包含重复项是否可以接受? (如果序列中的项目数大于范围,那么显然是。)

3) 随机性的用途是什么?大多数随机数生成器只是伪随机的;聪明的攻击者可以通过知道前面的数字来推断下一个“随机”数字。例如,如果您从一副 52 张牌中生成一系列五张牌来组成一手牌,那么您需要非常强的随机性;你不希望玩家能够推断出他们的对手手中的东西。

【讨论】:

    【解决方案2】:

    你想要它有多“非连续”?

    您可以使用 Random 类轻松地从一个范围内生成随机数列表:

    Random rnd1 = new Random();
    List<int> largeList = new List<int>();
    
    for (int i = 0, i < largeNumber, i++)
    {
      largeList.Add(rnd1.Next(1, 1000001);
    }
    

    编辑以添加

    诚然,Durstenfeld 算法(显然是 Fisher-Yates shuffle 的现代版本)要快得多:

    var fisherYates = new List<int>(upperBound);
    for (int i = 0; i < upperBound; i++)
    {
      fisherYates.Add(i);
    }
    
    int n = upperBound;
    
    while (n > 1)
    {
       n--;
       int k = rnd.Next(n + 1);
       int temp = fisherYates[k];
       fisherYates[k] = fisherYates[n];
       fisherYates[n] = temp;
    }
    

    对于 1 到 10000 的范围,执行蛮力“查找我尚未使用的随机数”大约需要 4-5 秒,而这大约需要 0.001。

    Greg Hewgill 提供链接。

    【讨论】:

    • 如果您想绝对确定没有数字是连续的,您可以使用 z 的解决方案,并将先前生成的随机数保存在局部变量中。然后将其与下一个生成的号码进行比较,并根据需要接受或拒绝该号码
    • 呵呵,正在编写我的程序并同时对其进行测试——如果你不关心重复的话会更快。
    • 嗨 Z,感谢您的代码,我不想关心重复,但我需要那个数字列表,没有重复。
    • 没有问题 - 第二种算法需要大约 0.1 秒来生成一个包含数字 0 - 1,000,000 的列表,然后将它们随机排列。
    【解决方案3】:

    我了解,您想获得一个长度为 1mio 的随机数组,其中所有数字从 1 到 1mio。没有重复,对吗?

    您应该建立一个数组,其中的数字范围从 1 到 1mio。然后开始洗牌。但是可能会发生(这是真正的随机性)两个或更多的数字是连续的。

    看看here

    【讨论】:

    • +1 如果你需要随机化一个已知的集合,那么这就是如何做到的。
    • 我想在这里引用史蒂夫·乔布斯的话,引用 iPod 上的介绍,以减少同一作者的两首歌曲的可能性,因为有人认为它不够随机:“这是实际上不那么随机”。
    【解决方案4】:

    这里有一个 C# 函数可以帮助您入门:

    public IEnumerable<int> GetRandomSequence(int max)
    {
        var r = new Random();
        while (true)
        {
           yield return r.GetNext(max);
        }
    }
    

    这样调用它可以获得一百万个 0-9999999 范围内的数字:

    var numbers = GetRandomSequence(9999999).Take(1000000);
    

    至于排序,或者如果您不想允许重复,请查看 Enumerable.GetRange()(这将为您提供 连续有序 序列)并使用 Fisher-Yates(或 Knuth) shuffle 算法(到处都可以找到)。

    【讨论】:

    • 请注意,您也可以到处发现Fisher-Yates shuffle 算法实施不正确。我编辑了不止一本“自学 C#”的书,它们的实现不正确。不要只是盲目地相信它;自己阅读以确保它实际上实现了真正的算法。
    【解决方案5】:

    “完全洗牌”是一个非常容易被误解的术语。欺诈专家在检查什么应该是“随机”数据时使用的一个技巧是观察没有重复值的情况(例如 3743***88***123,因为在真正随机的序列中,没有这样一对的机会非常低......你到底想做什么?什么,你所说的“完全洗牌”是什么意思?如果你的意思是随机的数字序列,那么只需使用 CLR 中的Random 类来生成0 到 1M 之间的随机数...任意数量...

    【讨论】:

      【解决方案6】:

      好吧,你可以用这样的东西(假设你想要每个数字恰好一次):

      DECLARE @intFrom int
      DECLARE @intTo int
      DECLARE @tblList table (_id uniqueidentifier, _number int)
      
      SET @intFrom = 0
      SET @intTo = 1000000
      
      WHILE (@intFrom < @intTo)
      BEGIN
          INSERT INTO  @tblList
          SELECT       NewID(), @intFrom
      
          SET @intFrom = @intFrom + 1
      END
      
      SELECT    *  
      FROM      @tblList
      ORDER BY  _id
      

      免责声明:我没有对此进行测试,因为我目前没有可使用的 SQL Server。

      【讨论】:

      • 小细节,反转INSERT INTO select子句上的项目列表
      【解决方案7】:

      这可能会为您提供所需的:

      1) 按顺序填充数字列表。如果您的范围是 1 - x,它将如下所示: [1, 2, 4, 5, 6, 7, 8, 9, ... , x]

      2) 循环列表 x 次,每次选择一个介于 0 和列表长度 - 1 之间的随机数。

      3) 使用这个选择的数字从列表中选择相应的元素,并将这个数字添加到输出列表中。

      4) 删除您刚刚从列表中选择的元素。冲洗,重复。

      这适用于任何范围的数字,而不仅仅是以 1 或 0 开头的列表。伪代码如下所示:

      nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
      shuffled_nums = []
      for i in range(0, len(nums)):
          random_index = rand(0,len(nums))
          shuffled_nums.add(nums[random_index])
          del(nums[random_index])
      

      【讨论】:

      • 请注意,对于列表的许多实现,从中间删除一个元素是列表大小的 O(n)。这将使您的算法 O(n^2),这可能是不可行的,因为我们已经知道该范围将达到一百万大。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-17
      • 1970-01-01
      • 2015-07-22
      • 2022-01-24
      • 1970-01-01
      • 2020-01-12
      相关资源
      最近更新 更多