Fill in missing numbers in different lists twice. ArgumentOutOfRangeException - vb.net

I need your help to prepare data. I am reading a byte array. I make bytes to unsigned integers. I read in different blocks of that array and write the UInt32s in 5 lists in total. The data has been stored compressed; that is, some spaces are missing and I need to fill them up. To make it clear, I made a compilable test project for you and wrote the data into an excel file.
This is the original data. From the left to the right: Sizes, Addresses, Indexes, Number_of_items, Description
You can see that in column C the 2, 3, and 4 are missing. So I select columns C through E, and move them down 3 rows. I fill the gaps with 2, 3, 4 in column C and 1, 1, 1 in the other two columns.
I do this until I reach the end of column B. Columns B, C, D, and E must have the same length.
Where I have a little problem
I fail because a While or For loop evaluates the List.Count property only once. That is, if I add something to a list within the loop, the loop doesn't run often enough. I've provisionally worked around this by writing While True and catching an OutOfRangeException. Maybe someone has a better idea; or even an idea that completely replaces my approach :D
Step № 2
If a row has a 2 in column D, I select columns B through E below the 2, and move the contents down one row (only one, because the difference is 1).
I want to do this until I get to the bottom of the table. This will make all columns the same length.
Again, I have the problem that I use While True and go out using an exception. Does anyone have a better idea?
FormMain.vb
Public NotInheritable Class FormMain
Private Sizes As New List(Of UInt32) From {
58_355UI,
20_270UI,
4_830UI,
4_443UI,
25_177UI,
8_844UI,
4_101UI,
4_200UI,
14_991UI,
12_639UI,
12_894UI,
14_165UI,
12_954UI,
26_670UI,
7_388UI}
Private Addresses As New List(Of UInt32) From {4_323UI, 62_706UI, 83_646UI, 88_935UI, 93_883UI, 128_259UI, 132_718UI,
137_254UI, 152_590UI, 178_485UI, 193_022UI, 206_718UI}
Private Indexes As New List(Of UInt32) From {1UI, 5UI, 6UI, 9UI, 10UI, 12UI}
Private NumberOfItems As New List(Of UInt32) From {1UI, 2UI, 1UI, 2UI, 1UI, 2UI}
Private Description As New List(Of UInt32) From {1UI, 1UI, 1UI, 1UI, 1UI, 1UI}
Private Sub ButtonStart_Click(sender As Object, e As EventArgs) Handles ButtonStart.Click
Dim RopD As New Reprocessing_of_parsed_data(Sizes, Addresses, Indexes, NumberOfItems, Description)
RopD.Fill_gaps()
End Sub
End Class
Reprocessing_of_parsed_data.vb
Public NotInheritable Class Reprocessing_of_parsed_data
Public Property Sizes As New List(Of UInteger)
Public Property Addresses As New List(Of UInteger)
Public Property Indexes As New List(Of UInteger)
Public Property Number_of_items As New List(Of UInteger)
Public Property Description As New List(Of UInteger)
Public Sub New(sizes As List(Of UInt32), addresses As List(Of UInt32), indexes As List(Of UInt32), number_of_items As List(Of UInt32), description As List(Of UInt32))
Me.Sizes = sizes
Me.Addresses = addresses
Me.Indexes = indexes
Me.Number_of_items = number_of_items
Me.Description = description
End Sub
Public Sub Fill_gaps()
Dim counterForAddressesList As Integer = 0
'Dim ListCount As Integer = Indexes.Count - 2
Dim i As Integer = 0
While True 'i < ListCount - 2
Try
Dim delta As Integer = CInt(Indexes(i + 1) - Indexes(i)) - 1
Dim number As UInt32 = Indexes(i)
While delta > 0
number += 1UI
counterForAddressesList += 1
Indexes.Insert(CInt(number) - 1, number)
Number_of_items.Insert(CInt(number) - 1, 1UI)
Description.Insert(CInt(number) - 1, 1UI)
delta -= 1
'ListCount += 1
End While
counterForAddressesList += 1
i += 1
Catch ex As ArgumentOutOfRangeException
Exit While
End Try
End While
' Step 2
Dim j As Integer = 0
While True
Try
If Number_of_items(j) > 1UI Then
Dim delta As Integer = CInt(Number_of_items(j)) - 1
While delta > 0
Addresses.Insert(j + 1, UInteger.MaxValue)
Indexes.Insert(j + 1, UInteger.MaxValue)
Number_of_items.Insert(j + 1, UInteger.MaxValue)
Description.Insert(j + 1, UInteger.MaxValue)
delta -= 1
j += 1
End While
End If
j += 1
Catch ex As ArgumentOutOfRangeException
Exit While
End Try
End While
End Sub
End Class

It is never a good idea to catch an index out of bounds exception in a Try-Catch-statement. Only conditions you are not in control of (often I/O errors) should be handled at runtime. An index being out of bounds is a design error and must be fixed at design time.
I extracted the two steps from Sub Fill_gaps into two new methods to make the code easier to read and test.
Public Sub Fill_gaps() ' A better name would be "Decompress"
PrintTable() 'For testing
FillGaps()
PrintTable() 'For testing
AddMissingNumberOfItems()
PrintTable() 'For testing
End Sub
I also added a method PrintTable for testing
Private Sub PrintTable()
Console.WriteLine()
Console.WriteLine($" A B C D E")
For i = 0 To Sizes.Count - 1
Dim A = Sizes(i)
Dim B = If(i < Addresses.Count, Addresses(i), 0UI)
Dim C = If(i < Indexes.Count, Indexes(i), 0UI)
Dim D = If(i < NumberOfItems.Count, NumberOfItems(i), 0UI)
Dim E = If(i < Description.Count, Description(i), 0UI)
Console.WriteLine($"{A,10}{B,10}{C,10}{D,10}{E,10}")
Next
End Sub
Step 1: fill the gaps (the method is self-explanatory):
Private Sub FillGaps()
' Fill gaps in columns C, D and E.
' The number of Addresses B indicates the total number of indexes.
' Append empty items to C, D and E until the list counts matches the
' expected total number of indexes.
Dim originalIndexCount = Indexes.Count 'Save original count
Do While Indexes.Count < Addresses.Count
Indexes.Add(CUInt(Indexes.Count + 1)) ' Make index 1-based
NumberOfItems.Add(1)
Description.Add(1)
Loop
'Move the rows to where the index indicates.
'We do it backwards to not overwrite existing items.
For i As Integer = originalIndexCount - 1 To 0 Step -1
Dim targetIndex = CInt(Indexes(i)) - 1 ' Subtract 1, indexes are 0-based
If targetIndex <> i Then
' Copy to target position
Indexes(targetIndex) = Indexes(i)
NumberOfItems(targetIndex) = NumberOfItems(i)
Description(targetIndex) = Description(i)
'Clear resp. initialize old row
Indexes(i) = CUInt(i + 1) ' Make index 1-based
NumberOfItems(i) = 1
Description(i) = 1
End If
Next
End Sub
Step 2:
Private Sub AddMissingNumberOfItems()
' Insert empty rows after items with NumberOfItems > 1.
' We do it backwards to not mess up our indexes.
For i As Integer = Indexes.Count - 1 To 0 Step -1
For k As UInteger = 2 To NumberOfItems(i)
Addresses.Insert(i + 1, 0)
Indexes.Insert(i + 1, 0)
NumberOfItems.Insert(i + 1, 0)
Description.Insert(i + 1, 0)
Next
Next
End Sub
If you use the following test list for the descriptions, you will better see which rows have been moved or added
Private Description As New List(Of UInt32) From {2UI, 3UI, 4UI, 5UI, 6UI, 7UI}

Related

Sorted List in Array Visual Basic [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 7 years ago.
I have a program that creates a list of 25 random numbers from 0 to 1,000. I have to buttons the first button will load a list box with the random numbers and the second button will sort the list of numbers from the smallest to largest which is where I implemented bubble sort code. Now the other list box that is supposed to hold the sorted numbers doesn't work properly it only shows one number instead of all of them.
Here is my code:
Option Strict On
Public Class Form1
Dim rn As Random = New Random
Dim Clicked As Long = 0
Dim numbers, sort As Long
Private Sub GenerateBtn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles GenerateBtn.Click
Clicked += 1
For x = 0 To 25
numbers = rn.Next(0, 1000)
RandomBox.Items.Add(numbers)
If Clicked >= 2 Then
RandomBox.Items.Clear()
Clicked = 1
End If
Next
End Sub
Private Sub SortBtn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SortBtn.Click
Dim Sorted() As Long = {numbers}
Dim Swapped As Boolean
Dim endOfArray As Integer = Sorted.Length - 1
Dim Tmp As Byte
While (Swapped)
Swapped = False
For I = 0 To endOfArray - 1
If Sorted(I) > Sorted(I + 1) Then
Tmp = CByte(Sorted(I))
Sorted(I) = Sorted(I + 1)
Sorted(I + 1) = Tmp
Swapped = True
End If
endOfArray = endOfArray - 1
Next
End While
SortBox.Items.Clear()
For I = 0 To Sorted.Count - 1
SortBox.Items.Add(Sorted(I))
Next
End Sub
End Class
Change your:
Dim Sorted() As Long = {numbers}
to
Sorted(x) = numbers
edit: Since you changed your code. You need to put back in the line that loads the Sorted Array.
For x = 0 To 25
numbers = rn.Next(0, 1000)
RandomBox.Items.Add(numbers)
Sorted(x) = numbers
If Clicked >= 2 Then
RandomBox.Items.Clear()
Clicked = 1
End If
Next
and remove the:
Dim Sorted() As Long = {numbers}
from the second part and put this declaration back in the beginning like you had:
Dim Sorted(26) as Long
The way you have will only show the latest random number. It is not any array but a single entity. Therefore only the latest will be add into the array. You need to load each number into the array as you create each one. Thus the (x) which loads it into position x.
You didn't use any arrays at all in your project...you're using the ListBox as your storage medium and that's a really bad practice.
I recommend you set it up like this instead:
Public Class Form1
Private rn As New Random
Private numbers(24) As Integer ' 0 to 24 = 25 length
Private Sub GenerateBtn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles GenerateBtn.Click
For x As Integer = 0 To numbers.Length - 1
numbers(x) = rn.Next(0, 1000)
Next
' reset the listbox datasource to view the random numbers
RandomBox.DataSource = Nothing
RandomBox.DataSource = numbers
' empty out the sorted listbox
SortBox.DataSource = Nothing
End Sub
Private Sub SortBtn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SortBtn.Click
' make a COPY of the original array that we will sort:
Dim sorted(numbers.Length - 1) As Integer
Array.Copy(numbers, sorted, numbers.Length)
Dim Swapped As Boolean = True
Dim endOfArray As Integer = Sorted.Length - 1
Dim Tmp As Integer
While (Swapped)
Swapped = False
For I As Integer = 0 To endOfArray - 1
If sorted(I) > sorted(I + 1) Then
Tmp = sorted(I)
sorted(I) = sorted(I + 1)
sorted(I + 1) = Tmp
Swapped = True
End If
Next
endOfArray = endOfArray - 1
End While
' reset the listbox datasource to view the sorted numbers
SortBox.DataSource = Nothing
SortBox.DataSource = sorted
End Sub
End Class
Also, note that you were decrementing endOfArray inside your for loop. You should only decrement it after each pass; so outside the for loop, but inside the while loop.
Additionally, you were using a Tmp variable of type Byte, but generating numbers between 0 and 999 (inclusive). The Byte type can only hold values between 0 and 255 (inclusive).
Your Bubble Sort implementation was very close to correct!

Nodes and Children structure mess, confusing

Here is a example how it should look (this is the same example as the code uses)
The code starts to screw up when changing from level 1 to level 2 a huge mess happens I don't understand it at all.
Here is the code
Public Class Node
Public label As Byte
Public visited As Boolean = False
Public level As Integer
Public Sub New(ByVal label As Byte, ByVal level As Integer)
Me.label = label
Me.level = level
End Sub
End Class
Public rootNodes As New List(Of Node)
Public nodes As New List(Of Node)
Dim adjMatrix As Byte(,)
Dim setDimensions As Boolean = False
Public Sub connectNode(ByVal start As Node, ByVal endd As Node)
'This method will be called to make connect two nodes
If setDimensions = False Then
ReDim Preserve adjMatrix(999 - 1, 999)
setDimensions = True
End If
Dim startIndex As Integer = nodes.IndexOf(start)
Dim endIndex As Integer = nodes.IndexOf(endd)
adjMatrix(startIndex, endIndex) = 1
adjMatrix(endIndex, startIndex) = 1
End Sub
Private Function getUnvisitedChildNode(ByVal n As Node) As Node
Dim index As Integer = nodes.IndexOf(n)
Dim j As Integer = 0
Do While j < nodes.Count
If adjMatrix(index, j) = 1 AndAlso nodes(j).visited = False Then
Return nodes(j)
End If
j += 1
Loop
Return Nothing
End Function
Private Sub clearNodes()
For Each n As Node In nodes
n.visited = False
Next
End Sub
Private Sub printNode(ByVal n As Node)
Debug.Print(n.label & " ")
End Sub
Public Enum Operation
LessThan = 0
GreaterThan = 1
End Enum
Public Function getPossibleNumbersForFirstByte(ByVal op As Operation, ByVal operationOf As Byte, ByVal numbersAlreadyUsed As List(Of Byte), ByVal totalSize As Long) As List(Of Byte)
Dim possibleNumbers As New List(Of Byte)
Dim tempNumber As Integer = -1
While True
If op = Operation.LessThan Then
tempNumber = IIf(tempNumber = -1, operationOf - 1, tempNumber - 1)
ElseIf op = Operation.GreaterThan Then
tempNumber = IIf(tempNumber = -1, operationOf + 1, tempNumber + 1)
End If
If tempNumber < 1 OrElse tempNumber > totalSize Then
Exit While
End If
If numbersAlreadyUsed Is Nothing OrElse numbersAlreadyUsed.Contains(tempNumber) = False Then
possibleNumbers.Add(tempNumber)
End If
End While
Return possibleNumbers
End Function
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
test()
End Sub
Sub test()
'Dim actualNumbers() As Byte = {5, 6, 1, 9, 7, 3, 8, 2, 4, 10}
Dim actualNumberList As New List(Of Byte)
actualNumberList.AddRange({5, 6, 1, 9, 7, 3, 8, 2, 4, 10})
Dim ListofGTLT() As Byte = {1, 0, 1, 0, 0, 1, 0, 1, 1}
Dim firstNumber As Byte = actualNumberList(0)
Dim possibleNumbersToUse As New List(Of Byte)
Dim numbersAlreadyUsed As New List(Of Byte)
For currentLevel = 0 To UBound(ListofGTLT)
'clear visited nodes.
clearNodes()
If currentLevel = 0 Then
'Generates Level 0 of all possible values in the first offset.
possibleNumbersToUse = getPossibleNumbersForFirstByte(ListofGTLT(currentLevel), firstNumber, Nothing, 10)
Dim n As Node
'Generates parent nodes of all possible values in the first offset.
For Each possibleNumber As Byte In possibleNumbersToUse
n = New Node(possibleNumber, 0)
rootNodes.Add(n)
nodes.Add(n)
Next
Else
Dim rootNode As Node
For Each rootNode In rootNodes
'Clear numbers already used.
numbersAlreadyUsed.Clear()
'Set already exists first value (known value).
numbersAlreadyUsed.Add(firstNumber)
If currentLevel = 1 Then
'Gets all the numbers which are possible connections.
possibleNumbersToUse = getPossibleNumbersForFirstByte(ListofGTLT(currentLevel), rootNode.label, numbersAlreadyUsed, 10)
Dim n As Node
'Setup connections with new nodes to root node.
For Each value As Byte In possibleNumbersToUse
n = New Node(value, 1)
nodes.Add(n)
'Debug.Print("[Connection]: " & rootNode.label & " = " & n.label)
connectNode(rootNode, n)
Next
Else
numbersAlreadyUsed.Add(rootNode.label)
Dim child = getUnvisitedChildNode(rootNode)
Do While child IsNot Nothing
Debug.Print("------------------------------------------")
Debug.Print("| Root = " & rootNode.label)
Debug.Print("| Child Level = " & child.level)
Debug.Print("| Child = " & child.label)
Debug.Print("------------------------------------------")
'Add to already existing values the child connection node.
If numbersAlreadyUsed.Contains(child.label) = False Then
If child.level = (currentLevel - 1) Then
'Gets next possible numbers which are possible connections.
possibleNumbersToUse = getPossibleNumbersForFirstByte(ListofGTLT(currentLevel), child.label, numbersAlreadyUsed, 10)
Dim n As Node
'Setup connections with new nodes to child node.
For Each value As Byte In possibleNumbersToUse
n = New Node(value, currentLevel)
nodes.Add(n)
connectNode(child, n)
Next
If child.level = 1 Then
'Finish all the level 1 childs for the current Root Node.
child.visited = True
child = getUnvisitedChildNode(rootNode)
Continue Do
Else
numbersAlreadyUsed.Add(child.label)
child.visited = True
child = getUnvisitedChildNode(child)
Continue Do
End If
Else
numbersAlreadyUsed.Add(child.label)
child.visited = True
child = getUnvisitedChildNode(child)
Continue Do
End If
Else
numbersAlreadyUsed.Add(child.label)
child.visited = True
child = getUnvisitedChildNode(child)
Continue Do
End If
Loop
End If
Next
End If
Next
End Sub
Instead of implementing your own class, using the TreeNode class, which is designed for this, makes your code much easier to understand, and has the added advantage of being native to the TreeView, which means all we have to do is add the base node and call ExpandAll and we have a graphical representation of our tree. Also what you want to do is well suited to a recursive routine. Here's a way that will build the tree structure. Whenever the child nodes are being added it skips any with a value already above it in the tree.
Private Sub btnAnswers_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
'Declare initial treenode with the value we want.
Dim Tree As New TreeNode("5")
'Declare the string of bits.
Dim input As String = "1001001001"
'Start the recursive routine
CalculateTreeNodes(Tree, input)
'Show the tree
TreeView1.Nodes.Add(Tree)
TreeView1.ExpandAll()h
End Sub
Private Sub CalculateTreeNodes(CurrentNode As TreeNode, directions As String)
If directions = "" Then Return
'Parse the text of the current node as byte
Dim value As Byte = Byte.Parse(CurrentNode.Text)
'Get the direction we want and truncate the string
Dim direction As Directions = CType([Enum].Parse(GetType(Directions), directions(0).ToString), Directions)
If directions.Length > 0 Then
directions = directions.Substring(1)
End If
'Get a list of the values of all the parent nodes above this one.
Dim Values As New List(Of Byte)
GetPrevValues(CurrentNode, Values)
'build our child nodes based on the direction we want.
Select Case direction
Case WindowsApplication7.Directions.Higher
For i As Byte = value + 1 To 10
If Not Values.Contains(i) Then
Dim node As New TreeNode(i.ToString)
CurrentNode.Nodes.Add(node)
'Calculate the children for each node
CalculateTreeNodes(node, directions)
End If
Next
Case WindowsApplication7.Directions.Lower
For i As Byte = 1 To value - 1
If Not Values.Contains(i) Then
Dim node As New TreeNode(i.ToString)
CurrentNode.Nodes.Add(node)
CalculateTreeNodes(node, directions)
End If
Next
End Select
End Sub
'recursive routine to find the parent values.
Private Sub GetPrevValues(CurrentNode As TreeNode, Values As List(Of Byte))
If Not CurrentNode Is Nothing Then
Values.Add(Byte.Parse(CurrentNode.Text))
GetPrevValues(CurrentNode.Parent, Values)
End If
End Sub
End Class
Public Enum Directions
Higher = 1
Lower = 0
End Enum
To find the longest branches you could use a conditional to check if directions is down to one character, then any children will be in the longest branches.

My program is assigning a value to ALL objects in an array. What's happening and how do I prevent it ? (VB 2008)

I have exhausted all of my options and am very desperate for help since I cannot figure out where the bug in my code is, or if there is something I don't understand.
I'm trying to create a "methinks it is a weasel!" mimmick from Richard Dawkins' late 80s documentary about evolution. The goal is to progress through a genetic algorithm until the algorithm guesses the correct answer through mutation and fitness tournaments.
Now, here's the problem:
Private Function fitnessTourney(ByVal editGuess() As Guess, ByVal popIndex As Integer, ByVal tourneySize As Integer, ByVal popNum As Integer)
Dim randInt(tourneySize - 1) As Integer
Dim loopCount1 As Integer = 0
Dim fitnessWinner As New Guess
fitnessWinner.setFitness(-50)
...
And, this loop is where I am experiencing the critical error
...
For i = 0 To tourneySize - 1
Randomize()
randInt(i) = Int(Rnd() * popNum)
While editGuess(randInt(i)).Used = True
If loopCount1 > tourneySize Then
loopCount1 = 0
For i2 = 0 To popNum - 1
editGuess(i2).setUsed(False)
Next
i = -1
Continue For
End If
loopCount1 += 1
randInt(i) = Int(Rnd() * popNum)
End While
editGuess(randInt(i)).determineFitness(correctPhrase)
editGuess(randInt(i)).setUsed(True)
Next
For i = 0 To popNum - 1
editGuess(i).setUsed(False)
Next
What this loop is trying to do is pick out four random instances of the editGuess array of objects. This loop tries to prevent one from being used multiple times, as the population is competing to one of the 10 members (highest fitness of the 4 chosen candidates is supposed to win).
The critical error is that I mysteriously get an endless loop where any instances of editGuess(randInt(i)).Used will always evaluate to true. I have tried to fix this by resetting all instances to False if it loops too many times.
The stumper is that I'll have all instances evaluate to False in the debugger. Then, when I reach "editGuess(randInt(i)).setUsed(True)" (the exact same thing as "editGuess(randInt(i)).Used = True"), it sets this value for EVERY member of the array.
Is there anyone who can see what is happening? I am so close to completing this!
Here's the Guess class:
Public Class Guess
Dim Fitness As Integer
Dim strLength As Integer
Dim strArray(30) As String
Dim guessStr As String
Dim Used As Boolean
Public Sub New()
Fitness = 0
guessStr = ""
strLength = 0
Used = 0
End Sub
Public Sub determineFitness(ByVal correctPhrase As String)
Dim lowerVal
If guessStr.Length <= correctPhrase.Length Then
lowerVal = guessStr.Length
Else
lowerVal = correctPhrase.Length
End If
strArray = guessStr.Split("")
Fitness = 0 - Math.Abs(correctPhrase.Length - guessStr.Length)
For i = 0 To lowerVal - 1
If correctPhrase(i) = guessStr(i) Then
Fitness = Fitness + 1
End If
Next
End Sub
Public Sub Mutate(ByVal mutatepercentage As Decimal, ByVal goodLetters As String)
If mutatepercentage > 100 Then
mutatepercentage = 100
End If
If mutatepercentage < 0 Then
mutatepercentage = 0
End If
mutatepercentage = mutatepercentage / 100
If Rnd() < mutatepercentage Then
strLength = Int(Rnd() * 25) + 5
If strLength < guessStr.Length Then
guessStr = guessStr.Remove(strLength - 1)
End If
End If
For i = 0 To strLength - 1
If Rnd() < mutatepercentage Then
If i < guessStr.Length Then
guessStr = guessStr.Remove(i, 1).Insert(i, goodLetters(Int(Rnd() * goodLetters.Length)))
Else
guessStr = guessStr & goodLetters(Int(Rnd() * goodLetters.Length))
End If
End If
Next
End Sub
Public Sub setFitness(ByVal num As Integer)
Fitness = num
End Sub
Public Sub setStrLength(ByVal num As Integer)
strLength = num
End Sub
Public Sub initializeText()
End Sub
Public Sub setUsed(ByVal bVal As Boolean)
Used = bVal
End Sub
End Class
And, finally, here's where and how the function is called
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
population1(counter) = fitnessTourney(population1, counter, 4, 10)
population2(counter) = fitnessTourney(population2, counter, 4, 10)
population1(counter).Mutate(2, goodLetters)
population2(counter).Mutate(20, goodLetters)
Label1.Text = population1(counter).guessStr
Label2.Text = population2(counter).guessStr
counter += 1
If counter > 9 Then
counter = 0
End If
End Sub
End Class
EDIT 1:
Thank you guys for your comments.
Here is the custom constructor I use to the form. This is used to populate the population arrays that are passed to the fitnessTourney function with editGuess.
Public Sub New()
InitializeComponent()
Randomize()
For i = 0 To 9
population1(i) = New Guess
population2(i) = New Guess
Next
counter = 0
correctPhrase = "Methinks it is a weasel!"
goodLetters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ !##$%^&*()_+-=?></.,;\|`'~"
goodLettersArr = goodLetters.Split("")
For i = 0 To 9
population1(i).setStrLength(Int(Rnd() * 25) + 5)
population2(i).setStrLength(Int(Rnd() * 25) + 5)
For i2 = 0 To population1(i).strLength
population1(i).guessStr = population1(i).guessStr & goodLetters(Int(Rnd() * goodLetters.Length))
Next
For i2 = 0 To population2(i).strLength
population2(i).guessStr = population2(i).guessStr & goodLetters(Int(Rnd() * goodLetters.Length))
Next
Label1.Text = population1(i).guessStr
Label2.Text = population2(i).guessStr
Next
population1(0).guessStr = correctPhrase
population1(0).determineFitness(correctPhrase)
End Sub
I haven't studied all of your code thoroughly, but one big problem is that you are calling Randomize from within the loop. Every time you call Randomize, it re-seeds the random numbers with the current time. Therefore, if you call it multiple times before the clock changes, you will keep getting the first "random" number in the sequence using that time which will always evaluate to the same number. When generating "random" numbers, you want to re-seed your random number generator as few times as possible. Preferably, you'd only seed it once when the application starts.
As a side note, you shouldn't be using the old VB6 style Randomize and Rnd methods. Those are only provided in VB.NET for backwards compatibility. You should instead be using the Random class. It's easier to use too. With the Random class, you don't even need to call a randomize-like method, since it automatically seeds itself at the point in time when you instantiate the object. So, in the case of the Random class, the thing to be careful is to make sure that you only instantiate the object once before entering any loop where you might be using it. If you create a new Random object inside a loop, it will similarly keep generating the same numbers.

How to get each value in an array and show it in a listbox

Hi I am new to VB and I have problems with using array. My code is like that.
This is class FindFactorsObject.vb
Public Sub FindFactors()
count = 0
temp = Convert.ToInt32(Math.Sqrt(_x))
For i As Integer = 1 To temp
If _x Mod i = 0 Then
ReDim array(count)
array(count) = i
count += 1
End If
Next
So I created an array and stored the results. Now I want to display each value in my array in Form.vb and if it is possible can somebody teach me how to make delay for each of the displayed value. Thanks very much
Always declare your variables, if possible to their exact types, you think they will take care of. When you say 'Now I want to display each value in my array in Form.vb' I understood literally: in the Form so, we will print them on your form
Public Sub FindFactors(_x As Integer)
Dim temp As Integer = Convert.ToInt32(Math.Sqrt(_x))
Dim l As New List(Of Integer)
For i As Integer = 1 To temp
If _x Mod i = 0 Then
l.add(i)
End If
Next
Dim pf As New PointF(20, 20)
For Each i As Integer In l
creategraphics.drawstring(i.ToString, New font(font.fontFamily, 24), brushes.cadetblue, pf)
pf = New PointF(pf.X, pf.Y + 30)
Next
End Sub

Bubble Sort logical error VB.NET

This program suppose to sort records(in arySort) in ascending order by last name(index 1 in aryTemp and aryTemp2) and place the result in the list box over the old, preloaded, unsorted records.
It sorts them strangely, I have to click multiple times the Ascending button to get the actual sort result that I suppose to get from clicking the button once.
Why doesn't it sort items with a single mouse click?
The source:
Public Class Form1
Dim FILE_NAME As String = "Students.txt"
Dim numberOfRecords As Integer 'total number of records
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
If System.IO.File.Exists(FILE_NAME) = True Then
Dim objReader As New System.IO.StreamReader(FILE_NAME)
Do While objReader.Peek() <> -1
lstBox.Items.Add(objReader.ReadLine)
numberOfRecords += 1
Loop
objReader.Close()
End If
End Sub
Private Sub btnAscending_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAscending.Click
'load all students into array
Dim arySort(numberOfRecords - 1) As String
Dim aryTemp() As String 'holds first record's last name
Dim aryTemp2() As String 'holds second record's last name
For i = 0 To numberOfRecords - 1
arySort(i) = lstBox.Items(i)
Next
Dim temp As String 'holds temporary record
Dim k As Integer
For i = 0 To arySort.Length - 2
aryTemp = Split(arySort(i), " ")
For k = i + 1 To arySort.Length - 1
aryTemp2 = Split(arySort(k), " ")
If aryTemp(1) < aryTemp2(1) Then
temp = arySort(k)
arySort(k) = arySort(i)
arySort(i) = temp
End If
Next
Next
lstBox.Items.Clear()
numberOfRecords = 0
For i = 0 To arySort.Length - 1
lstBox.Items.Add(arySort(i))
numberOfRecords += 1
Next
End Sub
End Class
If you just need to sort your list (as you say in the comment), don't implement your own sort mechanism but use the one of .NET:
' Define how we want to compare items '
Function compareByLastName(ByVal item1 As String, ByVal item2 As String) As Integer
Return String.Compare(item1.Split(" ")(1), item2.Split(" ")(1))
End Function
Private Sub btnAscending_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAscending.Click
' load all students into array '
Dim arySort(numberOfRecords - 1) As String
For i = 0 To numberOfRecords - 1
arySort(i) = lstBox.Items(i)
Next
' Use built-in .NET magic '
Array.Sort(arySort, AddressOf compareByLastName)
' Write the values back into your list box '
lstBox.Items.Clear()
numberOfRecords = 0
For i = 0 To arySort.Length - 1
lstBox.Items.Add(arySort(i))
numberOfRecords += 1
Next
End Sub
This uses the built-in quicksort algorithm of the .NET class library. Here's the documentation of the method we are using: Array.Sort(T(), Comparison(Of T)).
compare with my working bubble sort:
Public Sub BubbleSort(ByVal arr() As Integer)
Dim flag As Boolean = False
For i As Integer = 0 To arr.Length - 1
For j As Integer = 0 To arr.Length - 2 - i
If arr(j + 1) < arr(j) Then
flag = True
Dim temp As Integer = arr(j)
arr(j) = arr(j + 1)
arr(j + 1) = temp
End If
Next
If flag = False Then Return ' no swaps =>already sorted
Next
End Sub
I see a two major issues with your algorithm:
It's not bubble sort. Bubble sort swaps adjacent elements, i.e., it swaps i with i+1. You, on the other hand, swap some element i with the first j > i where name(i) < name(j). Maybe you should show us, in pseudo code, which algorithm you are actually trying to implement?
aryTemp contains element i and aryTemp2 contains some element j > i. Why do you swap the elements if aryTemp(1) < aryTemp2(1)? Isn't that already the correct order if you want your elements to be sorted ascending?