【问题标题】:Pick unique Random numbers选择唯一的随机数
【发布时间】:2016-05-09 07:25:23
【问题描述】:

我正在尝试将 VB.NET 中的数字随机化 3 次。每次我随机化一个数字时,它应该与其他两个数字不同。

例如,我有 3 个整数。 Int1、Int2 和 Int3。我将在 1-10 之间随机化 Int1 ,然后在 1-10 之间随机化 Int2 但是该值不应等于我在 Int1 中随机化的值,对于 Int3 也是如此,它不应等于 Int1 和 Int2。

我已经弄清楚如何随机化一个数字,这是我正在使用的代码:

Dim RndNumber As Random
Dim num,num2 As Integer
RndNumber = New Random
num = RndNumber.Next(1, 11)
num2 = RndNumber.Next(1, 11)

现在我被困在如何让 num2 随机化一个不等于 num 的 1-10 之间的数字。

感谢任何帮助,谢谢。

【问题讨论】:

    标签: .net vb.net random


    【解决方案1】:

    在所有示例中,RNG 是从 NET Random 类创建的随机数生成器:

    Private RNG = New Random()
    

    灵巧

    如果您只需要两个或三个,您可以循环直到当前选择不在结果集中。但是使用一些扩展方法会更简单:

    Dim nums = Enumerable.Range(1, 10).
                    OrderBy(Function(r) RNG.Next).
                    Take(3).
                    ToArray()
    

    这从 1 到 10 之间的所有数字开始,将它们按随机顺序排列,取前 3 个并将它们存储在 nums 数组中。我使用了多行形式,在.s 之后打破以说明这些步骤。

    只需根据需要更改范围、大小/计数和Take() 元素。例如,对于具有 5 个唯一数字 1-69(精简形式)的彩票:

    Dim winners = Enumerable.Range(1, 69).OrderBy(Function(r) RNG.Next()).Take(5).ToArray()
    Dim powerball = Enumerable.Range(1, 26).OrderBy(Function(r) RNG.Next()).Take(1).First
    

    由于强力球可以重复第一个数字,它来自自己的池。由于我们只需要一个,因此我们不需要数组,只需 First()

    手动

    了解这些事情的逻辑很好,所以这里显示的是手动版本。通过选择并实际检查随机值,这样做的方式不同:

    ' picked value storage
    Dim picks As New List(Of Int32)
    
    Dim pick As Int32          ' current candidate
    Do
        pick = RNG.Next(1, 11)
        If picks.Contains(pick) = False Then
            picks.Add(pick)
        End If
    Loop Until picks.Count = 3
    

    而不是松散的变量,它使用一个列表来保存选择。这样可以很容易地查看当前选择是否已被选中。对于不仅仅是几个值,请使用 HashSet(Of Int32) 而不是 List 以提高性能。

    随机对

    要创建一个随机的个数字,每个数字有 2 个,例如对于匹配游戏,只需将基础值池加倍,然后将它们按随机顺序排列:

    ' create pool of 2 values each for 1-13
    Dim nums = Enumerable.Range(1, 13).ToArray()
    ' concat the set to make 2 of each value, randomize 
    Dim pool = nums.Concat(nums).OrderBy(Function(r) RNG.Next).ToArray()
    

    对于手动方法,您必须检查循环中每个值的计数。

    “用完”精选

    另一种变化是当您需要定期使用随机数池时,但您不知道提前需要多少。例如 BINGO 游戏的球或一副纸牌。

    Stack(Of T)(或Queue)将根据您的需要“使用”值,而不是指向使用的最后一个插槽(或下一个要使用的插槽)的全局索引器:

    ' create, randomize pool of 100 ints
    Dim nums = Enumerable.Range(1, 100).OrderBy(Function(r) RNG.Next).ToArray
    ' use array to create Stack<T>
    Dim shoe As New Stack(Of Int32)(nums)
    
    ' same as:
    Dim shoe = New Stack(Of Int32)(Enumerable.Range(1, 100).
                                    OrderBy(Function(r) RNG.Next).ToArray())
    

    这基本上是从 100 个整数开始的,随机化并存储在一个数组中,但是没有 Take(n) 因为我们想要它们全部。然后将它们的值存储在堆栈集合中。使用它:

    Console.WriteLine(shoe.Count)
    For n As Int32 = 1 To 3
        Console.WriteLine("Picked #{0}", shoe.Pop)
    Next
    Console.WriteLine(shoe.Count)
    

    当您Pop 一个值时,它会自动从集合中删除。如果您使用鞋子中的大量值,则需要检查计数以确保它不为空。

    100
    选择 #12
    已选中 #69
    选择 #53
    97

    绘制3个值后,鞋子只剩下97个值了。

    随机笔记

    在所有情况下,您的Random 生成器都应该是您创建一次 的表单级对象。切勿在循环中创建它们,否则您可能会一遍又一遍地获得相同的值。

    OrderBy(Function(r) RNG.Next) 随机化方法通常对于随意使用来说已经足够了,但它效率低下。如果您将随机化大集合和/或经常使用它,您应该考虑使用适当的随机播放,例如the Fisher-Yates shuffle shown here

    【讨论】:

    • 感谢您的精彩解释,您的回答解决了我的问题。
    猜你喜欢
    • 2018-02-16
    • 1970-01-01
    • 2014-03-25
    • 2012-04-13
    • 1970-01-01
    • 2012-03-27
    • 2017-01-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多