【问题标题】:VB.NET - Removing a number from a random number generatorVB.NET - 从随机数生成器中删除一个数字
【发布时间】:2011-02-18 21:59:09
【问题描述】:

我正在尝试创建一个彩票模拟器。彩票有6个号码,产生的号码必须在1-49之间,不能在下一个产生的号码中。我曾尝试使用 OR 函数,但我不完全确定我是否正确使用它。任何帮助都会很棒。谢谢。

Public Class Form1

Private Sub cmdRun_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdRun.Click
    ''#Creates a new Random class in VB.NET
    Dim RandomClass As New Random()



    ''####################################
    Dim RandomNumber1 As Integer
    RandomNumber1 = RandomClass.Next(1, 49)
    ''#Displays first number generated
    txtFirst.Text = (RandomNumber1)



    ''####################################
    Dim RandomNumber2 As Integer
    RandomNumber2 = RandomClass.Next(1, 49)
    If RandomNumber2 = RandomNumber1 Then
        RandomNumber2 = RandomClass.Next(1, 49)
    End If

    ''#Displays second number generated
    txtSecond.Text = (RandomNumber2)



    ''####################################
    Dim RandomNumber3 As Integer
    RandomNumber3 = RandomClass.Next(1, 49)
    If RandomNumber3 = RandomNumber2 Or RandomNumber2 Then
        RandomNumber3 = RandomClass.Next(1, 49)
    End If

    ''#Displays third number generated
    txtThird.Text = (RandomNumber3)



    ''####################################
    Dim RandomNumber4 As Integer
    RandomNumber4 = RandomClass.Next(1, 49)
    If RandomNumber4 = RandomNumber3 Or RandomNumber2 Or RandomNumber1 Then
        RandomNumber4 = RandomClass.Next(1, 49)
    End If

    ''#Displays fourth number generated
    txtFourth.Text = (RandomNumber4)



    ''####################################
    Dim RandomNumber5 As Integer
    RandomNumber5 = RandomClass.Next(1, 49)
    If RandomNumber5 = RandomNumber4 Or RandomNumber3 Or RandomNumber2 Or RandomNumber1 Then
        RandomNumber5 = RandomClass.Next(1, 49)
    End If

    ''#Displays fifth number generated
    txtFifth.Text = (RandomNumber5)



    ''####################################
    Dim RandomNumber6 As Integer
    RandomNumber6 = RandomClass.Next(1, 49)
    If RandomNumber6 = RandomNumber5, RandomNumber4, RandomNumber3, RandomNumber2, RandomNumber1 Then
        RandomNumber6 = RandomClass.Next(1, 49)
    End If

    ''#Displays sixth number generated
    txtSixth.Text = (RandomNumber6)


End Sub

【问题讨论】:

  • 请注意,Next(1, 49) 不会返回 1-49 范围内的随机数,而是 1-48 范围内的随机数。

标签: vb.net random


【解决方案1】:

不要使用“If”,而是使用“While” - 换句话说,不断生成随机数,直到找到新的。目前,如果你得到一个副本,然后在第二次尝试时得到一个副本,你会继续前进。

另外,虽然我不是 VB 专家,但我相信您需要完整地指定每个比较,所以不要这样:

If RandomNumber3 = RandomNumber2 Or RandomNumber2 Then
    RandomNumber3 = RandomClass.Next(1, 49)
End If

你需要:

While RandomNumber3 = RandomNumber1 Or RandomNumber3 = RandomNumber2 Then
    RandomNumber3 = RandomClass.Next(1, 49)
End While

这里有其他选择 - 例如生成数字 1-49,将它们随机排列,然后获取前 6 个结果……或保持“选择直到有新结果”,但将结果保存在一组中。无论哪种方式,您都可以避免大量的代码重复。

【讨论】:

  • 绝对要采用洗牌方法——把它想象成一副纸牌。彩票通常的运作方式并不完全一样,但从数学上讲,结果应该完全相同。
  • 实际上这正是彩票的运作方式(在 PRNG 中打折 P)。一旦选择了一个数字(49 中的 1),该数字再次被选中的机会为零,并且任何其他特定数字的几率现在是 48 中的 1。洗牌就是这样做的。
  • @pax-- 我知道...我的意思是,对于乒乓球,你拿一个,然后将其余的洗牌,然后再拿另一个,依此类推。这是程序性的。有了这个,你只需洗牌一次(更多也可以,但没必要),你就可以一次得到所有的数字
  • 您还应该使用OrElse 而不是Or,因为OrElse 在找到返回true 的子句时会短路(而不是评估所有子句)。
  • 这正是我所需要的。很高兴看到其他人的想法,但对于我正在尝试做的事情有点复杂。谢谢大家!
【解决方案2】:

您不仅需要一个随机数生成器,还需要一个与洗牌算法结合使用的随机数生成器。

创建一个由N 项组成的数组(我们将在示例中使用七个),每个项都包含与其位置相关的整数:

+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+---+---+---+---+---+---+---+
                            <pool(7)

并将池大小设置为 7。

然后根据池大小生成随机数(即,获取 1 到 7 之间的数字)。假设您的生成器返回 3。

取出位置 3 的值,然后将其替换为顶部的值,然后减小池大小:

+---+---+---+---+---+---+---+
| 1 | 2 | 7 | 4 | 5 | 6 | 7 | -> 3
+---+---+---+---+---+---+---+
                        <pool(6)

然后您继续这样做,直到您获得所需的值数量。如果我们的乐透是 7 中的 5:

+---+---+---+---+---+---+---+
| 1 | 2 | 7 | 4 | 5 | 6 | 7 |
+---+---+---+---+---+---+---+
                            <pool(7)
rnd(7) returns 3
+---+---+---+---+---+---+---+
| 1 | 2 | 7 | 4 | 5 | 6 | 7 | -> 3
+---+---+---+---+---+---+---+
                        <pool(6)
rnd(6) returns 1
+---+---+---+---+---+---+---+
| 6 | 2 | 7 | 4 | 5 | 6 | 7 | -> 1
+---+---+---+---+---+---+---+
                    <pool(5)
rnd(5) returns 5
+---+---+---+---+---+---+---+
| 6 | 2 | 7 | 4 | 5 | 6 | 7 | -> 5
+---+---+---+---+---+---+---+
                <pool(4)
rnd(4) returns 2
+---+---+---+---+---+---+---+
| 6 | 4 | 7 | 4 | 5 | 6 | 7 | -> 2
+---+---+---+---+---+---+---+
            <pool(3)
rnd(3) returns 1
+---+---+---+---+---+---+---+
| 7 | 4 | 7 | 4 | 5 | 6 | 7 | -> 6
+---+---+---+---+---+---+---+
        <pool(2)

你有它,从 7 中提取 5 个数字 (3,1,5,2,6),没有重复的可能性,并且是一种有效的 O(n) 方法来获取它们。任何仅依赖于获取随机数并检查它们是否已被使用的解决方案都会降低效率。

【讨论】:

  • 如果您打算随机选择一个要删除的数字,我认为您不需要洗牌。感觉有点像带吊带的腰带,会起作用,但有点多余。
  • 改组是必要的,以确保进一步的选择是 O(1) 操作。如果您不洗牌并且 rnd(7) 返回 6 两次,这与必须保留以前使用的数字列表进行检查没有什么不同。改组保证了两次选择相同的数字是不可能的,而仍然只需要调用一次 rnd() 。这是从池中取出使用过的号码的最佳方式。
  • 您可能会误解洗牌意味着大量的互换。在这种情况下,洗牌只是将顶部元素移动到您刚刚提取的元素中并减少池大小。这意味着您可以使用简单的快速数组而不是集合类型来实现它。
  • 哦,我以为你是从你的收藏中物理删除使用过的号码。当然,这不是 O(1) 操作,但考虑到输入的大小,我认为这并不重要。
【解决方案3】:

如果您有 VB2008,这里还有一个使用 LINQ 的选项:

Dim rnd As New Random()

Dim randomNumbers = From n in Enumerable.Range(1, 49) _
                    Order By rnd.Next() _
                    Select n _
                    Take 6

'Do something with the numbers here

这是一种简单的方法。如果使用 Random 类不够随机,那么您可能不得不选择另一种方法。

【讨论】:

    【解决方案4】:

    您必须将我正在使用的文本框的名称更改为您正在使用的文本框。

        Dim rand As New Random
        Dim winnum As New List(Of Integer)
        Dim num, counter As Integer
        Dim result As String = ""
    
        Do
            num = rand.Next(1, 49)
            If winnum.Contains(num) Then
                Do
                    num = rand.Next(1, 49)
                Loop Until winnum.Contains(num) = False
            End If
            winnum.Add(num)
            counter += 1
        Loop Until counter = 6
    
        'Extracting and displaying the numbers from the array
    
        For n As Integer = 0 To 5
            result = winnum(n) & " " & result
        Next
    
        'The textbox I'm using to display the result is result.text
    
        result.Text = result
    

    【讨论】:

      【解决方案5】:

      您也可以使用上面其他人建议的代码。在下面的代码中,数字是随机生成的,并从数字池中删除,直到达到所需数量。然后显示剩下的数字。然而,这并不是生成彩票号码的好方法,因为号码序列在某种程度上是可预测的,但它们是独一无二的。代码如下:

          Dim rand As New Random, winnum As New List(Of Integer)
          Dim num As Integer, result As String = ""
      
          For n As Integer = 1 To 49
              winnum.Add(n)
          Next
      
          Do
              num = rand.Next(1, 49)
      
              If winnum.Contains(num) Then
                  winnum.Remove(num)
              End If
      
          Loop Until winnum.Count = 7
      
          For n As Integer = 0 To 5
              result = winnum(n) & " " & result
          Next
      
          a.Text = result
      

      【讨论】:

        【解决方案6】:

        我会选择类似(在 C# 中)

        public static IEnumerable<int> Lotto(int max)
            {
                var random = new Random((int)DateTime.Now.Ticks);
                var numbers = new List<int>(Enumerable.Range(1, max));
                while(numbers.Count > 0)
                {
                    int index = random.Next(1, numbers.Count) - 1;
                    yield return numbers[index];
                    numbers.RemoveAt(index);
                }
            }
        
            static void Main(string[] args)
            {
        
        
                var lotto = Lotto(49).GetEnumerator();
                lotto.MoveNext();
                int r1 = lotto.Current;
                lotto.MoveNext();
                int r2 = lotto.Current;
                lotto.MoveNext();
                int r3 = lotto.Current; 
        
                Console.WriteLine("{0} {1} {2}", r1, r2, r3 );
        
            }
        

        【讨论】:

          【解决方案7】:

          您无需选择随机数并检查重复项,您可以简单地遍历数字并检查每个数字与随机数的概率:

          Dim count As Integer = 6 ' How many numbers to pick
          Dim pos As Integer = 1 ' Lowest value to pick from
          Dim items As Integer = 49 ' Number of items in the range
          
          Dim rnd As New Random()
          Dim result As New List(Of Integer)()
          
          While count > 0
            If rnd.Next(items) < count Then
              result.Add(pos)
              count -= 1
            End If
            pos += 1
            items -= 1
          End While
          

          列表result 现在包含六个不重复的数字,从 1-49 范围内随机选取。作为额外奖励,列表中的数字已经排序。

          【讨论】:

            【解决方案8】:

            我认为洗牌也是最快的选择。但更易于阅读的是您的方法与集合的 contains 函数相结合:

            Dim numbers As New List(Of Int32)
            For i As Int32 = 1 To 6
                Dim containsNextNumber As Boolean = False
                While Not containsNextNumber
                    Dim rnd As New Random(Date.Now.Millisecond)
                    Dim nextNumber As Int32 = rnd.Next(1, 50)
                    If Not numbers.Contains(nextNumber) Then
                        numbers.Add(nextNumber)
                        containsNextNumber = True
                    End If
                End While
            Next
            numbers.Sort() 'sort the numbers from low to high
            

            【讨论】:

              猜你喜欢
              • 2023-01-03
              • 2013-05-20
              • 2019-01-12
              • 2011-09-18
              • 1970-01-01
              • 2018-05-03
              • 1970-01-01
              • 2018-04-21
              • 2015-06-26
              相关资源
              最近更新 更多