weird exception occurs with negative numbers - vb.net

here is my code:
Dim index As Integer
do
index = find difference(board1,board2)
if index = - 1 then
exit do
end if
loop
find difference is a function that returns an integer, I have set it to return -1 if no difference is found so then the loop exits, however this gives me an outofbounds exception. i have put a try statement around the line index = find difference(board1,board2) and it catches -1 as an exception with the message:
index was out of range.
must be non-negative and less than the size of the collection.
parameter name: index.
I am at a loss as to what can be causing this, any help would be much appreciated.
EDIT:
find_difference:
dim indy as integer
dim indexes as list(of integer)
dim info as integer = 0
indexes.add(-1)
for each cell in cells
if cell.info > info then
indexes.clear
indexes.add(cell.index)
else if cell.info = info then
indexes.add(cell.index)
end if
next
indy = Math.Floor((indexes.Count + 1) * Rnd())
return indexes(indy)
end function

There is only one statement in the find_difference function which uses an index:
return indexes(indy)
Which indicates the value of indy is computed as -1 or above the size of the collection:
indy = Math.Floor((indexes.Count + 1) * Rnd())
If indexes.Count is 1 and Rnd() is greater than 0.5 then indy will be computed as 1 which would be outside the range of the collection. The + 1 should be removed.

Related

VBA. "Type mismatch: array or user-defined type expected"

I am completely new to VBA. I need to write a program, which will generate an array of integer and will find an index of the minimal element. I got this error
"Type mismatch: array or user-defined type expected." I looked into many similar questions, but couldn't figure out what is wrong.
Function random_integer(Min As Integer, Max As Integer)
random_integer = Int((Max - Min + 1) * Rnd + Min)
End Function
Function generate_array(Size As Integer)
Dim Arr(Size)
For i = 0 To UBound(Arr)
Arr(i) = random_integer(i - 10, i + 10)
Next
generate_array = Arr
End Function
Function find_index_of_min_elem(ByRef Arr() As Variant)
Dim Min As Integer
Min = Arr(0)
Dim MinIndex As Integer
MinIndex = 0
For i = 1 To UBound(Arr)
If Arr(i) < Min Then
Min = Arr(i)
MinIndex = i
End If
Next
find_index_of_min_elem = MinIndex
End Function
Sub task6()
A = generate_array(20)
IndexOfMinElemInA = find_index_of_min_elem(A)
MsgBox IndexOfMinElemInA
End Sub
The problem is the function Function find_index_of_min_elem(ByRef Arr() As Integer) is expecting an Integer as a parameter and you are passing a as a Variant
a = generate_array(20)
IndexOfMinElemInA = find_index_of_min_elem(a)
The next error that you will get is on Dim Arr(Size) As Integer. You cannot dimension an array like that.
I would recommend reading up on arrays.
There may be other errors but I have not checked those.
What about this? Make a second column of index numbers (in sequence from small to large) and then order the two rows by your original column. I've included the example below to illustrate:
A = the original column of numbers,
B = the column of index numbers
D & E are the result of sorting A & B by column A
The answer is then: the lowest number "0" was at index 7

Random numbers in array without any duplicates

I'm trying to randomize an array from numbers 0 to 51 using loops but I just cannot seem to pull it off. My idea was that
Generate a Random Number
Check if this random number has been used by storing the previous in an array
If this random number has been used, generate new random number until it is not a duplicate
If it's not a duplicate, store it
My attempt:
Dim list(51) As Integer
Dim templist(51) As Integer
For i As Integer = 0 To 51 Step 1
list(i) = i
Next i
Do While counter <= 51
p = rand.Next(0, 52)
templist(counter) = p
For n As Integer = 0 To 51 Step 1
p = rand.Next(0, 52)
If templist(n) = p Then
Do While templist(n) = p
p = rand.Next(0, 52)
Loop
templist(n) = p
Else
templist(n) = p
End If
Next
counter += 1
Loop
For n As Integer = 0 To 51 Step 1
ListBox1.Items.Add(templist(n))
Next
It will be a lot easier if you just have a list of all of the possible numbers (0 to 51 in your case), then remove the number from the list so it can't be picked again. Try something like this:
Dim allNumbers As New List (Of Integer)
Dim randomNumbers As New List (Of Integer)
Dim rand as New Random
' Fill the list of all numbers
For i As Integer = 0 To 51 Step 1
allNumbers.Add(i)
Next i
' Grab a random entry from the list of all numbers
For i As Integer = 0 To 51 Step 1
Dim selectedIndex as Integer = rand.Next(0, (allNumbers.Count - 1) )
Dim selectedNumber as Integer = allNumbers(selectedIndex)
randomNumbers.Add(selectedNumber)
allNumbers.Remove(selectedNumber)
' Might as well just add the number to ListBox1 here, too
ListBox1.Items.Add(selectedNumber)
Next i
If your goal is to get the numbers into ListBox1, then you don't even need the "randomNumbers" list.
EDIT:
If you must have an array, try something like this:
Function RandomArray(min As Integer, max As Integer) As Integer()
If min >= max Then
Throw New Exception("Min. must be less than Max.)")
End If
Dim count As Integer = (max - min)
Dim randomNumbers(count) As Integer
Dim rand As New Random()
' Since an array of integers sets every number to zero, and zero is possibly within our min/max range (0-51 here),
' we have to initialize every number in the array to something that is outside our min/max range.
If min <= 0 AndAlso max >= 0 Then
For i As Integer = 0 To count
randomNumbers(i) = (min - 1) ' Could also be max + 1
Next i
End If
Dim counter As Integer = 0
' Loop until the array has count # of elements (so counter will be equal to count + 1, since it is incremented AFTER we place a number in the array)
Do Until counter = count + 1
Dim someNumber As Integer = rand.Next(min, max + 1)
' Only add the number if it is not already in the array
If Not randomNumbers.Contains(someNumber) Then
randomNumbers(counter) = someNumber
counter += 1
End If
Loop
Return randomNumbers
End Function
This is good enough for your assignment, but the computer scientist in my hates this algorithm.
Here's why this algorithm is much less desirable. If zero is in your range of numbers, you will have to loop through the array at least 2N times (so 104+ times if you are going from 0 to 51). This is a best case scenario; the time complexity of this algorithm actually gets worse as the range of numbers scales higher. If you try running it from 0 to 100,000 for example, it will fill the first few thousand numbers very quickly, but as it goes on, it will take longer and longer to find a number that isn't already in the list. By the time you get to the last few numbers, you could potentially have randomly generated a few trillion different numbers before you find those last few numbers. If you assume an average complexity of 100000! (100,000 factorial), then the loop is going to execute almost ten to the half-a-millionth power times.
An array is more difficult to "shuffle" because it is a fixed size, so you can't really add and remove items like you can with a list or collection. What you CAN do, though, is fill the array with your numbers in order, then go through a random number of iterations where you randomly swap the positions of two numbers.
Do While counter <= 51
p = rand.Next(0, 52)
While Array.IndexOf(list, p) = -1
p = rand.Next(0, 52)
End While
counter += 1
Loop
Haven't written VB in about 5 years, but try this out:
Function GetRandomUniqueNumbersList(ByVal fromNumber As Integer, ByVal toNumber As Integer) As List(Of Integer)
If (toNumber <= fromNumber) Then
Throw New ArgumentException("toNumber must be greater than fromNumber", toNumber)
End If
Dim random As New Random
Dim randomNumbers As New HashSet(Of Integer)()
Do
randomNumbers.Add(random.Next(fromNumber, toNumber))
Loop While (randomNumbers.Count < toNumber - fromNumber)
Return randomNumbers.ToList()
End Function
Ok, that was painful. Please someone correct it if I made any mistakes. Should be very quick because it's using a HashSet.
First response to forum on stackoverflow - be gentle.
I was looking for a way to do this but couldn't find a suitable example online.
I've had a go myself and eventually got this to work:
Sub addUnique(ByRef tempList, ByVal n, ByRef s)
Dim rand = CInt(Rnd() * 15) + 1
For j = 0 To n
If tempList(j) = rand Then
s = True
End If
Next
If s = False Then
tempList(n) = rand
Else
s = False
addUnique(tempList, n, s)
End If
End Sub
Then call the sub using:
Dim values(15) As Byte
Dim valueSeen As Boolean = False
For i = 0 To 15
addUnique(values, i, valueSeen)
Next
This will randomly add the numbers 1 to 16 into an array. Each time a value is added, the previous values in the array are checked and if any of them are the same as the randomly generated value, s is set to true. If a value is not found (s=false), then the randomly generated value is added. The sub is recursively called again if s is still true at the end of the 'For' loop. Probably need 'Randomize()' in there somewhere.
Apologies if layout is a bit wobbly.

VBA Macro Run time error 6: overflow- coding inside a loop

Having a problem with this Error. I am creating a GA and the loop is to assign my fitness value to an array.
some of the variables
Dim Chromolength as integer
Chromolength = varchromolength * aVariables
Dim i as integer, j as integer, counter as integer
Dim Poparr() As Integer
Dim FitValarr() As Integer
the code:
ReDim Poparr(1 To PopSize, 1 To Chromolength)
For i = 1 To PopSize
For j = 1 To Chromolength
If Rnd < 0.5 Then
Poparr(i, j) = 0
Else
Poparr(i, j) = 1
End If
Next j
Next i
For i = 1 To PopSize
j = 1
counter = Chromolength
Do While counter > 0
FitValarr(i) = FitValarr(i) + Poparr(i, counter) * 2 ^ (j - 1)
j = j + 1
counter = counter - 1
Loop
Next i
I am having problems with:
FitValarr(i) = FitValarr(i) + Poparr(i, counter) * 2 ^ (j - 1)
I apologize, I am fairly new to VBA.
An overflow condition arises when you create an integer expression that evaluates to a value larger than can be expressed in a 16-bit signed integer. Given the expression, either the contents of FitValarr(i), or the expression 2^(j-1) could be overflowing. Suggest all the the variables presently declared as Int be changed to Long. Long integers are 32-bit signed values and provide a correspondingly larger range of possible values.
I had the same run time error 6. After much investigation l discovered that mine was a simple 'divide by zero' error.
I set up an integer value to hold Zip codes, and Error 6 events plagued me - until I realized that a zip code of 85338 exceeded the capacity of an int...
While I didn't think of a zip code as a "value" it was nonetheless certainly interpreted as one. I suspect the same could happen with addresses as well as other "non-numeric" numeric values. Changing the variable to a string resolved the problem.
It just didn't occur to me that a zip code was a "numeric value." Lesson learned.

Parallel.For VS For. Why there is this difference?

I have an array (i), and I want to do some math calculations based on the i value with a Parallel.For().
But the problem is, after running the Parallel.For(), the values on my array are still 0.
This happens when my for is from 0 to 0.
This is my code :
Dim a(10) As Double
Parallel.For(0, 0, Sub(i)
a(i) = i + 2
'There is some calculations based on instead of previous line!
'But anyway, the result will be on a(i).
End Sub)
MessageBox.Show(a(0)) 'This returns 0!
For i As Integer = 0 To 0
a(i) = i + 2
Next
MessageBox.Show(a(0)) 'But this returns 2!
What is the problem?
From Microsoft's documentation
If fromInclusive is greater than or equal to toExclusive, then the method returns immediately without performing any iterations.
Therefore nothing will happen when you use Parallel.For(0,0,etc).
Try Parallel.For(0,1) and see if you get a result.
Your correct code should look like this
Dim a(10) As Double
Parallel.For(0, 1, Sub(i)
a(i) = i + 2
'There is some calculations based on instead of previous line!
'But anyway, the result will be on a(i).
End Sub)
MessageBox.Show(a(0)) '2!
For i As Integer = 0 To 0
a(i) = i + 2
Next
MessageBox.Show(a(0)) '2

Why one over another: UBound or Length

Could there be any specific reason why one can choose UBound over Length?
Here is the code and 1-dimension is passed as second parameter.
For iIndex = 0 To UBound(myList)
If Left(Request.ServerVariables("REMOTE_ADDR"), Len(myList(iIndex))) = saIPList(iIndex) Then
bAuth = True
Exit For
End If
Next
Any performance gain against Length
They do different things! UBound gives you the last index in the array, while Length gives you the length. Those are not the same, because usually UBound will be Length - 1.
Ubound exists mainly for backwards compatibility to old code. I haven't seen anything saying it's deprecated just yet, but at the same time I recognize it's not really aligned with the way they have been taking the language in recent years. The same is true for the Len() and Left() functions in that code; they are the way of the past, not the future. The sooner you adapt, the happier you will be.
For what it's worth, the argument is largely moot. More "modern" ways to write that code look entirely different. Here's one example:
bAuth = myList.Zip(saIPList, Function(a,b) New With {.Length = a.Length, .saIP = b} ) _
.Any(Function(i) Request.ServerVariables("REMOTE_ADDR").ToString().SubString(0,i.Length) = i.saIP)
For performance gain I was also interested in what function has the best performance.
It seems that the length -1 is much faster than the UBound. I expected UBound to be faster somehow.
After 100.000.000 times it seems the time for length -1 is 952ms and for UBound: 5844ms.
(length -1) is ~6 times faster than UBound
Code used for testing
Private Sub UboundlengthToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles UboundlengthToolStripMenuItem.Click
ListBox1.Items.Clear()
ListBox1.Items.Add("BEGIN")
'set required vars
Dim ints() As Integer = {1, 2, 3, 4, 5}
'end vars setting
Dim t As New Stopwatch
Dim gt As New Stopwatch
Dim time1 As Integer
Dim temp As Integer
Dim d As Double = GC.GetTotalMemory(False)
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.GetTotalMemory(False)
ListBox1.Items.Add("Free Memory: " & d)
gt.Start()
t.Reset()
'starting test---------------------------------------
'single test---------------------------------------
t.Start()
For i As Integer = 0 To TextBox1.Text
temp = ints(ints.Length - 1)
Next
t.Stop()
time1 = t.ElapsedMilliseconds
ListBox1.Items.Add("arr.length - 1")
ListBox1.Items.Add("Func1 total time: " & time1)
ListBox1.Items.Add("Func1 single time: " & time1 / TextBox1.Text)
t.Reset()
'single test---------------------------------------
'single test---------------------------------------
t.Start()
For i As Integer = 0 To TextBox1.Text
temp = ints(UBound(ints))
Next
t.Stop()
time1 = t.ElapsedMilliseconds
ListBox1.Items.Add("UBound:")
ListBox1.Items.Add("Func1 total time: " & time1)
ListBox1.Items.Add("Func1 single time: " & time1 / TextBox1.Text)
t.Reset()
'single test---------------------------------------
'Finishing test--------------------------------------
gt.Stop()
ListBox1.Items.Add("Total time " & gt.ElapsedMilliseconds)
d = GC.GetTotalMemory(True) - d
ListBox1.Items.Add("Total Memory Heap consuming (bytes)" & d)
ListBox1.Items.Add("END")
End Sub
Tried different things to eliminate possible optimalisations of the compiler, all with the same result as stated above.
It's carried over from earlier VB days. UBound can give you the highest index of any single dimension in a multi-dimensional array. Length only gives you the total number of elements.
If you declare:
' A 5x10 array:
Dim array(4, 9) As Integer
The values are:
array.Length = 50 ' Total number of elements.
array.Rank = 2 ' Total number of dimensions.
array.LBound(0) = 0 ' Minimum index of first dimension.
array.LBound(1) = 0 ' Minimum index of second dimension.
array.UBound(0) = 4 ' Maximum index of first dimension.
array.UBound(1) = 9 ' Maximum index of second dimension.
Interesting UBounds is quite a bit faster in this example
Dim startTick As Long
For j As Integer = 1 To 5
Dim rnd As New Random(Now.Subtract(Now.Date).TotalSeconds)
Dim RandomMax As Integer = rnd.Next(10000, 100000)
Dim cumbersome() As Integer = New Integer() {rnd.Next}
'Ubound is better than Length. Using Length takes as much time as redimensioning the entire array.
startTick = Environment.TickCount
For i As Integer = 1 To RandomMax - 1
ReDim Preserve cumbersome(UBound(cumbersome) + 1)
cumbersome(UBound(cumbersome)) = Rnd.Next
Next
Debug.Print("{0}) Array Method UBound: {1} Ticks to construct {2} integers", j, Environment.TickCount - startTick, RandomMax)
'Length is slow don't use it
startTick = Environment.TickCount
For i As Integer = 1 To RandomMax - 1
ReDim Preserve cumbersome(cumbersome.Length)
cumbersome(cumbersome.Length - 1) = Rnd.Next
Next
Debug.Print("{0}) Array Method Length: {1} Ticks to construct {2} integers", j, Environment.TickCount - startTick, RandomMax)
Next