Visual Basic, new to coding. Need to make a list of 100 integers create a shuffled list of 70 results - vb.net

I'm new to coding and I have got firmly stuck on this.
I've created a list in Visual Basic with
Dim integerStable As New List(Of Integer)()
integerStable.Add(0)
integerStable.Add(1)
integerStable.Add(2)
'through to integerStable.Add(99)
I'm trying to keep this list (it can be shuffled as long as all numbers stay in the list in general) and create a 2nd list where it only has 70 results from that shuffle.
I need that list, so I can call on it to perform some tasks for me later.
Can anyone help me work out how to do this? Remember I'm new to coding, but I'll try to follow along.

One of the most efficient ways to create your list would be as follows:
Dim integerStable As New List(Of Integer)
For i = 1 To 100
integerStable.Add(i)
Next
That at least should save you a lot of typing!!
You could also do the following:
Dim integerStable As New List(Of Integer)
Dim i As Integer
While i <= 100
integerStable.Add(i)
i += 1
End While
**Note though that the latter example will give you 101 items as integer is initially set to 0 **
You also need to remember that the list will be 'indexed' from 0 NOT 1 which is an important thing to remember when it comes to manipulating the items with it.

It can be much simpler.
Private Shared PRNG As New Random
' 100 numbers starting at zero, in random order
Private listOnum As List(Of Integer) = Enumerable.Range(0, 100).OrderBy(Function(x) PRNG.Next).ToList
' list of 70 numbers from list of 100
Private list70 As List(Of Integer) = listOnum.Take(70).ToList
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'did it work?
Dim ct As Integer = 1
For Each n As Integer In list70
Debug.WriteLine("{0}. {1,3}", ct, n)
ct += 1
Next
End Sub
Enumerable.Range takes two arguments. The first is a start number and the second is a count, so in the example it created a list that started at 0 and ended with 99, 100 items. The OrderBy just sorted that list by random numbers.
list70 is created by taking the first 70 items from listOnum.
The Random, PRNG, is created that way so that there is only ONE random that is only initialized once. You can find many problems associated with the incorrect initialization of Random.
edit: Slightly different approach.
Private Shared PRNG As New Random
' 100 numbers starting at zero
Private listOnum As List(Of Integer) = Enumerable.Range(0, 100).ToList
' list of 70 random numbers from list of 100
Private list70 As List(Of Integer) = listOnum.OrderBy(Function(x) PRNG.Next).Take(70).ToList
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'did it work?
Dim ct As Integer = 1
For Each n As Integer In list70
Debug.WriteLine("{0}. {1,3}", ct, n)
ct += 1
Next
'recreate list of 70
list70 = listOnum.OrderBy(Function(x) PRNG.Next).Take(70).ToList
End Sub

Related

MAX & MIN Value on GridView Column VB.NET

I am developing a small program to get the maximum of a specific column in a gridview (DevExpress), but I could not execute it as I wanted.
Can you support me in seeing where I have the error?
Dim cells() As GridCell = GridView2.GetSelectedCells()
Dim values As New List(Of Decimal)()
For i As Integer = 0 To GridView2.RowCount - 1
Dim value As Decimal = Convert.ToDecimal(GridView2.GetRowCellValue(cells(i).RowHandle, cells(i).Column))
values.Add(value)
Next i
values.Sort()
MsgBox(values.Max().ToString())
Regards.
With the built in DataGridView, the number of rows can be Rows.Count -2 because there is an extra row for the user to enter a new record. I have no idea if DevExpress works that way but it is worth a try.
For i As Integer = 0 To GridView2.RowCount - 2
If your GridView uses a DataTable as a DataSource, then the following code might help. If the DataTable is still available then just start with that. Otherwise extract it from the grid.
DataTable does not implement IEnumerable but there is an extension method to get the interface (.AsEnumerable).
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim dt = DirectCast(DataGridView1.DataSource, DataTable)
Dim maxValue = Aggregate r In dt.AsEnumerable
Into MaxID = Max(r("ID")) '"ID" is the name of a column
MessageBox.Show(maxValue.ToString)
End Sub
Same thing for Min just change Max to Min.
Calculate Total Summary for Grid Column
gridView1.Columns("UnitsInStock").Summary.Add(DevExpress.Data.SummaryItemType.Average, "UnitsInStock", "Avg={0:n2}")
gridView1.Columns("UnitsInStock").Summary.Add(DevExpress.Data.SummaryItemType.Sum, "UnitsInStock", "Sum={0}")
Dim item As GridColumnSummaryItem = New GridColumnSummaryItem(DevExpress.Data.SummaryItemType.Max, "UnitsInStock", "Max={0}")
gridView1.Columns("UnitsInStock").Summary.Add(item)
Devexpress Documentation:
https://documentation.devexpress.com/WindowsForms/DevExpress.XtraGrid.Columns.GridColumn.Summary.property
https://documentation.devexpress.com/WindowsForms/9677/Controls-and-Libraries/Data-Grid/Examples/Summaries/How-to-Calculate-Single-Total-Summary-for-Grid-s-Column

Generate a sequence of 5 random numbers

this code generates a single number, how could I generate more?
Dim randomNumber As Integer = rnd.Next(0, 81)
I would like to generate more random numbers using this code, but how could I do that?
As you generate your 5 random numbers, you need a place to store each number. I chose a List(Of T) because I don't have to know how many items I want to add in advance. You could also use an array.
When you instantiate the Random class without a seed then the class uses the system clock as the seed. This is the usual way to do it.
Private Sub OPCode(URL As String)
Dim Rnd As New Random() 'No seed!
Dim lst As New List(Of Integer)
For i = 0 To 4
lst.Add(Rnd.Next(0, 81))
Next
For Each i In lst
TextBox1.Text &= i.ToString & vbCrLf
Next
End Sub

Visual Basic: loaded parallel list boxes with text file substrings, but now items other than lstBox(0) "out of bounds"

The text file contains lines with the year followed by population like:
2016, 322690000
2015, 320220000
etc.
I separated the lines substrings to get all the years in a list box, and all the population amounts in a separate listbox, using the following code:
Dim strYearPop As String
Dim intYear As Integer
Dim intPop As Integer
strYearPop = popFile.ReadLine()
intYear = CInt(strYearPop.Substring(0, 4))
intPop = CInt(strYearPop.Substring(5))
lstYear.Items.Add(intYear)
lstPop.Items.Add(intPop)
Now I want to add the population amounts together, using the .Items to act as an array.
Dim intPop1 As Integer
intPop1 = lstPop.Items(0) + lstPop.Items(1)
But I get an error on lstPop.Items(1) and any item other than lstPop.Items(0), due to out of range. I understand the concept of out of range, but I thought that I create an index of several items (about 117 lines in the file, so the items indices should go up to 116) when I populated the list box.
How do i populate the list box in a way that creates an index of list box items (similar to an array)?
[I will treat this as an XY problem - please consider reading that after reading this answer.]
What you are missing is the separation of the data from the presentation of the data.
It is not a good idea to use controls to store data: they are meant to show the underlying data.
You could use two arrays for the data, one for the year and one for the population count, or you could use a Class which has properties of the year and the count. The latter is more sensible, as it ties the year and count together in one entity. You can then have a List of that Class to make a collection of the data, like this:
Option Infer On
Option Strict On
Imports System.IO
Public Class Form1
Public Class PopulationDatum
Property Year As Integer
Property Count As Integer
End Class
Function GetData(srcFile As String) As List(Of PopulationDatum)
Dim data As New List(Of PopulationDatum)
Using sr As New StreamReader(srcFile)
While Not sr.EndOfStream
Dim thisLine = sr.ReadLine
Dim parts = thisLine.Split(","c)
If parts.Count = 2 Then
data.Add(New PopulationDatum With {.Year = CInt(parts(0).Trim()), .Count = CInt(parts(1).Trim)})
End If
End While
End Using
Return data
End Function
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim srcFile = "C:\temp\PopulationData.txt"
Dim popData = GetData(srcFile)
Dim popTotal = 0
For Each p In popData
lstYear.Items.Add(p.Year)
lstPop.Items.Add(p.Count)
popTotal = popTotal + p.Count
Next
' popTotal now has the value of the sum of the populations
End Sub
End Class
If using a List(Of T) is too much, then just use the idea of separating the data from the user interface. It makes processing the data much simpler.

Generating Three Unique Random Numbers in Visual Basic?

I have been working on an app lately that displays three random photos. The form consists of three pictureboxes and a button. When the user clicks a button, three different images are shown. The problem is, however, these three images are not always unique, most of the time there will be doubles and often triples too. I tried to implement a function to catch this but all it succeeded at was lowering the chances of identical images. There are over 50 images to choose from, so it's not like there isn't enough. Here is the code for the failed solution I came up with:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
RandomImageOne()
RandomImageTwo()
RandomImageThree()
If imagenumber1.Text or imagenumber2.Text = imagenumber3.Text Then
RandomImageThree()
End If
If imagenumber1.Text or imagenumber3.Text = imagenumber2.Text Then
RandomImageTwo()
End If
If imagenumber3.Text or imagenumber2.Text = imagenumber1.Text Then
RandomImageOne()
End If
End Sub
The 'RandomImage' functions generate a random number in a label (eg. imagenumber1), these numbers correlate with the number of one of the 50 images. I realize that this is probably not the smartest method, but I am not familiar with any other way.
I need to be able to either generate three unique numbers, so that I don't have to worry about programming in something to stop double and triple images, or create a solution that works 100% of the time to catch the double or triple images.
Any help would be very much appreciated, especially if it were explained simply. Thank you.
I would generate random image 1 and image 2, testing that image 2 is distinct using a while loop. Only once that that is done would I then move on to generating image three.
Information on the while loop is here.
So in rough code (it's been a while since I used VBA properly):
RandomImageOne()
RandomImageTwo()
do while imagenumber1.text = imagenumber2.text
RandomImageTwo()
loop
RandomImageThree()
do while imagenumber3.text = imagenumber2.text or imagenumber3.text = imagenumber1.text
RandomImageThree()
loop
It might not be the most efficient way but it works...
First create a function that returns a list of three items:
Public Function ProvideUniqueNumbers(NoList As List(Of Integer), _
HowManyToReturn As Integer) As List(Of Integer)
Dim Generator As System.Random = New System.Random()
Dim n As Integer = NoList.Count
Dim index As Integer = Generator.Next(1, n)
Dim ReturnList As List(Of Integer) = New List(Of Integer)
For i = 1 To HowManyToReturn
n = NoList.Count
index = Generator.Next(1, n)
ReturnList.Add(NoList(index))
NoList.RemoveAt(index)
'NoList.Dump()
Next
Return ReturnList
End Function
Then create a list of integers for your collection. For instance:
List(Of Integer) MyList = New List(Of Integer)
For i As Integer = 0 To YourImageArray.Count - 1
MyList.Add(i)
Next
Finally Call the function and distribute the results:
Dim result As List(Of Integer) = ProvideUniqueNumbers(MyList,3)
image1 = YourImageArray(result(0))
image2 = YourImageArray(result(1))
image3 = YourImageArray(result(2))
This isn't a solution but a note.
This doesn't do what you think it does
If imagenumber1.Text or imagenumber2.Text = imagenumber3.Text Then
RandomImageThree()
End If
You have to compare each element
If imagenumber1.Text = imagenumber3.Text or imagenumber2.Text = imagenumber3.Text Then
RandomImageThree()
End If

Is there an easy way to randomize a list in VB.NET?

I have a list of type System.IO.FileInfo, and I would like to randomize the list. I thought I remember seeing something like list.randomize() a little while back but I cannot find where I may have seen that.
My first foray into this yielded me with this function:
Private Shared Sub GetRandom(ByVal oMax As Integer, ByRef currentVals As List(Of Integer))
Dim oRand As New Random(Now.Millisecond)
Dim oTemp As Integer = -1
Do Until currentVals.Count = IMG_COUNT
oTemp = oRand.Next(1, oMax)
If Not currentVals.Contains(oTemp) Then currentVals.Add(oTemp)
Loop
End Sub
I send it the max val I want it to iterate up to, and a reference to the list I want the randomized content in. The variable IMG_COUNT is set farther up in the script, designating how many random images I want displayed.
Thanks guys, I appreciate it :D
Check out the Fisher-Yates shuffle algorithm here: http://en.wikipedia.org/wiki/Knuth_shuffle
with a more concise discussion by this site's chief overlord here:
http://www.codinghorror.com/blog/archives/001015.html
There is a simple C# implementation in the blog entry that should be real easy to change to VB.NET
I've extended the List class with the following Randomize() function to use the Fisher-Yates shuffle algorithm:
''' <summary>
''' Randomizes the contents of the list using Fisher–Yates shuffle (a.k.a. Knuth shuffle).
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="list"></param>
''' <returns>Randomized result</returns>
''' <remarks></remarks>
<Extension()>
Function Randomize(Of T)(ByVal list As List(Of T)) As List(Of T)
Dim rand As New Random()
Dim temp As T
Dim indexRand As Integer
Dim indexLast As Integer = list.Count - 1
For index As Integer = 0 To indexLast
indexRand = rand.Next(index, indexLast)
temp = list(indexRand)
list(indexRand) = list(index)
list(index) = temp
Next index
Return list
End Function
Build a Comparer:
Public Class Randomizer(Of T)
Implements IComparer(Of T)
''// Ensures different instances are sorted in different orders
Private Shared Salter As New Random() ''// only as random as your seed
Private Salt As Integer
Public Sub New()
Salt = Salter.Next(Integer.MinValue, Integer.MaxValue)
End Sub
Private Shared sha As New SHA1CryptoServiceProvider()
Private Function HashNSalt(ByVal x As Integer) As Integer
Dim b() As Byte = sha.ComputeHash(BitConverter.GetBytes(x))
Dim r As Integer = 0
For i As Integer = 0 To b.Length - 1 Step 4
r = r Xor BitConverter.ToInt32(b, i)
Next
Return r Xor Salt
End Function
Public Function Compare(x As T, y As T) As Integer _
Implements IComparer(Of T).Compare
Return HashNSalt(x.GetHashCode()).CompareTo(HashNSalt(y.GetHashCode()))
End Function
End Class
Use it like this, assuming you mean a generic List(Of FileInfo):
list.Sort(New Randomizer(Of IO.FileInfo)())
You can also use a closure to make the random value 'sticky' and then just use linq's .OrderBy() on that (C# this time, because the VB lambda syntax is ugly):
list = list.OrderBy(a => Guid.NewGuid()).ToList();
Explained here, along with why it might not even be as fast as real shuffle:
http://www.codinghorror.com/blog/archives/001008.html?r=31644
There are several reasonable methods of shuffling.
One has already been mentioned. (The Knuth Shuffle.)
Another method would be to assign a "weight" to each element and sort the list according to that "weight." This method is possible but would be unweildy because you cannot inherit from FileInfo.
One final method would be to randomly select an element in the original list and add it to a new list. Of course, that is, if you don't mind creating a new list. (Haven't tested this code...)
Dim rnd As New Random
Dim lstOriginal As New List(Of FileInfo)
Dim lstNew As New List(Of FileInfo)
While lstOriginal.Count > 0
Dim idx As Integer = rnd.Next(0, lstOriginal.Count - 1)
lstNew.Add(lstOriginal(idx))
lstOriginal.RemoveAt(idx)
End While
You could also implement a shuffle, many ways to do this, the simplest is randomly pick a item and insert it into a new location a bunch of times.
If you have the number of elements then a pseudo-random method can be used whereby you choose the first element at random (e.g. using the inbuilt random number function) then add a prime and take the remainder after division by the number of values. e.g. for a list of 10 you could do i = (i + prime) % 10 to generated indices i from some starting value. As long as the prime is greater than the number of values in the list then you create a sequence which runs through all of the numbers 0...n where n is the number of values - 1, but in a pseudorandom order.
Dim oRand As New Random() 'do not seed!!!!
Private Sub GetRandom(ByRef currentVals As List(Of Integer))
Dim i As New List(Of Integer), j As Integer
For x As Integer = 0 To currentVals.Count - 1
j = oRand.Next(0, currentVals.Count)
i.Add(currentVals(j))
currentVals.RemoveAt(j)
Next
currentVals = i
End Sub
You could create custom comparer that just returns a random number, then sort the list using this comparer. It could be horribly inefficient and cause an almost infinite loop, but might be worth a try.