VB.NET - Removing a number from a random number generator - vb.net

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

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.

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.

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.

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

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

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 );
}

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.

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

Related

How to count a Number in textbox

I have the following number strings:
Textbox1.text / or Textbox1.Line= 1,2,3,19,29,78,48,39,40,51,53,54,69,70,71,73
Textbox2.text= / or Textbox2.Line= 1,9,3,31,29,78,45,39,40,51,59,54,69,70,71,73
textbox3.text= / or TextBox3.Line= 11,4,3,31,29,78,45,39,40,53,59,54,6974,75,76
and Others ...
How can I make a Count that shows how many numbers from 1 to 10 are in the Textbox, how many numbers from 11-20, how many numbers from 31-40, and so on. Example: On line 1 - we will have 3 small numbers from 1 to 10 (1,2,3).
To do this You will have to split the string into an array or list then compare the values in the array or list to the numbers you want. like this
Dim arr1 As New List (Of String)
arr1.AddRange(Split (TextBox1.Text, ","))
Dim final As String
Dim count As Integer = 0
For Each item As String In arr1
If CInt(item) >= 1 And CInt(item) <= 10 Then
count+=1
'replace 10 with the maximum number you want and 1 with the minimum number.
final&=item & " "
End If
Next
Msgbox("There are " & count & "numbers" & final)
You can easily convert a string containing a comma-separated list of integers into a string array like this
Dim s = "1,2,3,19,29,78,48,39,40,51,53,54,69,70,71,73"
Dim parts = s.Split(","c)
Then convert the string array into a list of integers
Dim numbers = New List(Of Integer)
For Each p As String In parts
Dim i As Integer
If Integer.TryParse(p, i) Then
numbers.Add(i)
End If
Next
Now comes the counting part. With LINQ you can write
Dim tens = From n In numbers
Group n By Key = (n - 1) \ 10 Into Group
Order By Key
Select Text = $"{ 10 * Key + 1} - {10 * Key + 10}", Count = Group.Count()
This
For Each x In tens
Console.WriteLine($"{x.Text} --> {x.Count}")
Next
Prints
1 - 10 --> 3
11 - 20 --> 1
21 - 30 --> 1
31 - 40 --> 2
41 - 50 --> 1
51 - 60 --> 3
61 - 70 --> 2
71 - 80 --> 3

Adding to every other array position MS Access

I'm needing to take one array (firstArray) and input into a second array (secondArray). However, the first four fields are the same value. After the first four positions, it begins to alternate in values.
Example:
firstArray
+---------+
| ID# |
| Name |
| month1 |
| month2 |
| month3 |
| etc... |
+---------+
secondArray
+----------+
| ID# |
| Name |
| month1 |
| month2 |
| NewField |
| month3 |
| NewField |
| month4 |
| etc... |
+----------+
I'm fairly new to VBA, so I apologize for the awful code.
Code so far:
Dim i As Integer
i = 0
Dim j As Integer
ReDim secondArray(0 To (fieldCount - 4) * 2)
Dim finalCountDown As Integer
finalCountDown = (fieldCount - 4) * 2
secondArray(0) = firstArray(0)
secondArray(1) = firstArray(1)
secondArray(2) = firstArray(2)
secondArray(3) = firstArray(3)
i = 3
j = 3
Do Until i > finalCountDown
i = i + 1
secondArray(i) = "NewField"
i = i + 1
j = j + 1
secondArray(i) = firstArray(j)
Loop
I also have a MsgBox to iterate through and output my fields:
'//------ testing output
i = 0
For i = 0 To finalCountDown
MsgBox secondArray(i)
Next i
I appreciate any help! If there's any confusion, I'll gladly try to explain some more!
EDIT:
The two arrays are of different size but are dynamic. firstArray is firstly set to 20 positions while secondArray is originally set to 32 positions. (These are calculated each time this process is ran with the archived data being pulled. This allows my users to add data and not have to worry about having to manually add in the values to my arrays.)
EDIT2:
I've added Erik's portion to my code with a few alterations. I also added a separate counter for my firstArray in order to make sure it's inputting the correct rows into the correct positions of my secondArray.
EDIT3:
Here is the code that ended up working for me:
Dim i As Integer
i = 0
Dim j As Integer
'removed the " - 4"
ReDim secondArray(0 To (fieldCount * 2))
Dim finalCountDown As Integer
'removed the " - 4"
finalCountDown = (fieldCount * 2)
secondArray(0) = firstArray(0)
secondArray(1) = firstArray(1)
secondArray(2) = firstArray(2)
secondArray(3) = firstArray(3)
i = 3
'created own counter for firstArray
j = 3
Do Until i > finalCountDown
i = i + 1
secondArray(i) = "NewField"
i = i + 1
j = j + 1
secondArray(i) = firstArray(j)
Loop
The error I was getting was due "Subscript not in Range" due to the fact that my finalCountDown variable was less than my array needed to be. Allowing the variable to become larger than my array allowed my array to finish iterating through itself and now inputs the proper fields in the proper order.
I'm accepting Erik's answer as it was the stepping stone to answering the question!
For the adjusted code, you can do a simple check to check if the j value is valid:
Dim i As Integer
i = 0
Dim j As Integer
ReDim secondArray(0 To (fieldCount - 4) * 2)
Dim finalCountDown As Integer
finalCountDown = (fieldCount - 4) * 2
secondArray(0) = firstArray(0)
secondArray(1) = firstArray(1)
secondArray(2) = firstArray(2)
secondArray(3) = firstArray(3)
i = 3
j = 3
Do Until i > finalCountDown
i = i + 1
finalArray(i) = "NewField"
i = i + 1
j = j + 1
If j => LBound(colheaders) And j <= UBound(colHeaders) Then
finalArray(i) = colHeaders(j)
End If
Loop

Combinations of 4-digit numbers whose individual digits sum to 5

I'm working on a scorecard at work which has columns representing 4 possible outcomes, for example:
Successful,
unsuccessful,
Exceptional,
Other
Each staff member is assessed 5 times in the month against those ratings. So 1 person might have 3 successful, 2 exceptional, 0 unsuccessful, 0 other.
So the max instances of each outcome is 5 but the total sum of instances can't be more than 5.
I could try to type out all the combinations (and get them wrong) - is there any function/formula/VBA that anyone knows of that will list out all of the possible combinations of outcomes for me?
e.g. 5000,4100,4010,4001,3200,3020,3002,3110,3011, etc...
Since your numbers can range from 0005 to 5000, you could just write a simple loop that tests each number to see if the digits total 5:
Sub GetPermutations()
Dim i As Long
For i = 5 To 5000
If SumDigits(i) = 5 Then Debug.Print Format$(i, "0000")
Next
End Sub
Function SumDigits(s As Variant) As Long
Dim i As Long
For i = 1 To Len(s)
SumDigits = SumDigits + CLng(Mid$(s, i, 1))
Next
End Function
Alternatively:
Dim w As Long, x As Long, y As Long, z As Long
For w = 0 To 5
For x = 0 To 5
For y = 0 To 5
For z = 0 To 5
If w + x + y + z = 5 Then Debug.Print w & x & y & z
Next
Next
Next
Next
c#:
// numbering 1 to 4 - i.e.
// 1 sucessful, 2 unsucessful, 3 exception, 4 other
for(int i=1;i<4;i++)
{
for(int n=1;n<4;n++)
{
for(int d=1;d<4;d++)
{
for(int q=1;q<4;q++)
{
if(i+d+n+q<=5)
Console.WriteLine(i+n+d+q+", ");
}
}
}
}
EDIT: just saw you asked for vba:
For number1 As Integer = 1 To 4 Step 1
For number2 As Integer = 1 To 4 Step 1
For number3 As Integer = 1 To 4 Step 1
For number4 As Integer = 1 To 4 Step 1
if(number1+number2+number3+number4<=5) Then
Debug.WriteLine(number1.ToString & number2.toString &number3.toString &number4.ToString & ",");
End If
Next number4
Next number3
Next number2
Next number1
The most simple solution I could think of, there's probably better though.
Thanks for your help everyone - i managed to use a combination of suggestions by starting with the number 1, auto-filling down to 5000, then "text to columns", fixed width to separate the digits, sum to 5, filter and hey presto! Seems to have produced the right number of possible combinations adding up to 5.
Thanks again for your help!

How to count the number of spaces and characters in vb.net?

I want to count the number of spaces and characters inputted in a textbox field.
Example
textbox1.text - " 1 2 3 4 5 6 7 a b c"
I want to count the number of spaces and the characters
Then the result must be..
Dim spaces as integer, char as integer
spaces = 10 , char = 10
Dim spaceCount, lettercount As Integer
spaceCount= 0
lettercount = 0
Dim s As String = " 1 2 3 4 5 6 7 a b c"
For Each c As Char In s
If c = " " Then
spaceCount+= 1
Else
lettercount += 1
End If
Next
MsgBox(charcount)
MsgBox(lettercount)
For Each will iterate each character within the string then it will check whether c is a space or not. if it is a space then increment the spaceCount else increment the lettrtCount
You can do as you want in a sort way.
Try it
Dim count As Integer = 0
textbox1.text = " 1 2 3 4 5 6 7 a b c"
count = textbox1.text.Split(" ").Length -1
Dim longstring As String = "aab c d e"
Dim TotalLength As Integer = longstring.Length
Dim TotalSpaces() As String = Split(longstring, " "
Dim TotalChars As Integer = TotalLength - (TotalSpaces.Length - 1)

How to generate a 26-character hex string that equals to 106 bits and ((53 Ones - 53 Zeros) in binary)

I am looking for a way to generate a hexadecimal string that equals out to 106 bits, more specifically fifty three 1's and fifty three 0's after each hex char is converted to binary and added together. I'd like to keep it as random as possible considering the parameters of the request. How would I go about keeping an eye on the construction of the string so that it equals out the way I want?
For example:
(a8c05779f8934b14ce96f8aa93) =
(1010 1000 1100 0000 0101 0111 0111 1001 1111 1000 1001 0011 0100
1011 0001 0100 1100 1110 1001 0110 1111 1000 1010 1010 1001 0011)
One option is to create a list with an equal number of 0s and 1s and then sort it with an array of random keys:
Sub Main()
' Start with a list of 53 0's and 1's
Dim bitsList = New List(Of Integer)
For i = 1 To 53
bitsList.Add(1)
bitsList.Add(0)
Next
Dim bits = bitsList.ToArray()
' Create list of random keys
Dim keys = New List(Of Integer)
Dim rand = New Random()
For i = 1 To bits.Count
keys.Add(rand.Next())
Next
' Sort bits by random keys
Array.Sort(keys.ToArray(), bits)
' Create hex string
Dim s = ""
For i = 1 To bits.Length - 4 Step 4
Dim digit = bits(i + 3) * 8 + bits(i + 2) * 4 + bits(i + 1) * 2 + bits(i)
s = s + Hex(digit)
Next
Console.WriteLine(s)
End Sub
You can place 52 ones randomly in a 104 bit number by keeping track of how many ones has been placed already and calculate the probability that the next digit should be one. The first digit always has 1/2 probability (52/104), then the second digit has 51/103 or 52/103 probability depending on what the first digit was, and so on.
Put the bits in a buffer, and when it is full (four bits), that makes a hexadecimal digit that you can add to the string:
Dim rnd As New Random()
Dim bin As New StringBuilder()
Dim buf As Integer = 0, bufLen As Integer = 0, left As Integer = 52
For i As Integer = 104 To 1 Step -1
buf <<= 1
If rnd.Next(i) < left Then
buf += 1
left -= 1
End If
bufLen += 1
If bufLen = 4 Then
bin.Append("0123456789abcdef"(buf))
bufLen = 0
buf = 0
End If
Next
Dim b As String = bin.ToString()
To make a 106 bit value, change these lines:
Dim buf As Integer = 0, bufLen As Integer = 0, left As Integer = 53
For i As Integer = 106 To 1 Step -1
The resulting string is still 26 characters, the two extra bits are in the buf variable. It has a value between 0 and 3 that you can use to create the 27th character, however that is done.
To add a 22 bit hash to the string, you can use code like this:
bin.Append("048c"(buf))
Dim b As String = bin.ToString()
Dim m As New System.Security.Cryptography.SHA1Managed
Dim hash As Byte() = m.ComputeHash(Encoding.UTF8.GetBytes(b))
'replace first two bits in hash with bits from buf
hash(0) = CByte(hash(0) And &H3F Or (buf * 64))
'append 24 bits from hash
b = b.Substring(0, 26) + BitConverter.ToString(hash, 0, 3).Replace("-", String.Empty)