views:

448

answers:

8

I am trying to create a lottery simulator. The lottery has 6 numbers, the number generated must be between 1 - 49 and cannot be in the next number generated. I have tried using the OR function but I'm not entirely sure if I am using it properly. Any help would be great. Thanks.

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
+5  A: 

Instead of "If", use "While" - in other words, keep generating random numbers until you find a new one. Currently if you get a duplicate and then get a duplicate on the second attempt, you'll keep going.

Also, while I'm no VB expert, I believe you'll need to specify each comparison in full, so instead of this:

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

you need:

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

There are alternatives here - such as generating the numbers 1-49, shuffling them, and then fetching the first 6 results... or keeping to the "pick until there's a new one" but keep the results in a set. Either way you could avoid having quite so much code duplication.

Jon Skeet
Definitely go with the shuffle approach--think of it more like a deck of cards. It's not exactly how lotteries typically work, but mathematically, the outcome should be exactly the same.
Michael Haren
Actually it is exactly how lotteries work (discounting the P in PRNG). Once a number is selected (1-from-49), that number has zero chance of being selected again and the odds of any other specific number is now 1-in-48. Shuffling does that.
paxdiablo
@pax-- I know... I just mean that with ping pong balls, you take one, then shuffle the rest, then take another, and so on. It's procedural. With this, you just shuffle once (more would be ok, but unnecessary) and you have all the numbers at once
Michael Haren
You should also use `OrElse` instead of `Or` because `OrElse` will short circuit when it finds a clause the returns true (instead of evaluating all of them).
R0MANARMY
This is pretty much what I needed. It was great to see what everyone else thought up but abit complex for what I'm trying to do. Thanks guys !
Alex
+3  A: 

You don't just need a random number generator here, you need one in conjunction with a shuffling algorithm.

Create an array of N items (we'll use seven for our example), each containing the integer relating to its position:

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

and set the pool size to 7.

Then generate your random number, based on the pool size (i.e., get a number from 1 to 7). Let's say your generator returns 3.

Pull out the value at position 3 then replace that with the top value, then reduce the pool size:

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

Then you just keep doing this until you've gotten the quantity of values required. If our lotto was 5 from 7:

+---+---+---+---+---+---+---+
| 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)

and there you have it, 5-from-7 numbers (3,1,5,2,6) extracted with no possibilities of duplicates and an efficient O(n) method for getting them. Any solution that relies on just getting random numbers and checking if they've already been used will be less efficient.

paxdiablo
I don't think you need to shuffle the numbers if you plan to randomly select one to remove. Feels a little bit like a belt with suspenders, it'll work, but a little redundant.
R0MANARMY
The shuffling is necessary to ensure that further selection is an O(1) operation. If you don't shuffle and rnd(7) returns 6 twice, that's no different to having to keep a list of previously used numbers to check against. The shuffling guarantees that selection of the same number twice is impossible, while still only having to call rnd() once. It's the best way of taking used numbers out of the pool.
paxdiablo
You may be suffering under the misapprehension that shuffling means a lot of swaps. Shuffling in this case is simply moving the top element into the one you've just extracted and reducing the pool size. It means you can implement it with a simple fast array instead of a collection type.
paxdiablo
Oh, I thought you were physically removing used numbers out of your collection. Granted that wouldn't be an O(1) operation, but given the size of the input I don't think it'll matter.
R0MANARMY
A: 

I'd go for something like (in 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 );

    }
Keith Nicholas
A: 

Instead of picking random numbers and check for duplicates, you can simply loop through the numbers and check the odds for each number to be picked against a random number:

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

The list result now contains six numbers without duplicates, randomly picked from the range 1-49. As an extra bonus the numbers in the list are already sorted.

Guffa
+1  A: 

Here's another option using LINQ if you have VB2008:

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

This is a simple way to do it. If using the Random class is not random enough, then you may have to choose an alternative method.

Chris Dunaway
A: 

I think shuffling is the fastest alternative too. But easier to read is your approach in combination with a collections's contains function:

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
Tim Schmelter
A: 

You have to change the name of the textbox I'm using to the one you're using.

    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
Medwatt
A: 

You can also use code as another fellow suggested above. In the code below, numbers are generated randomly and are removed from a pool of numbers until the required quantity is reached. Then the numbers left are then displayed. However this is not such a good way for generating numbers for lottery as the sequence of numbers are somehow predictable, but they are unique. Here is the code:

    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
Medwatt