stack overflow exception in quicksort - vb.net

I am trying to make a quick sort however i am having trouble with a stack overflow exception in VB.net.
The sort works correctly when x = 3, intermittently when x = 5 and on one occasion sorted half of the list when x = 10.
Please advise, thanks
Dim x As Integer = 10
Dim numArrray(x - 1) As Integer
For i = 0 To x - 1
Randomize()
numArrray(i) = CInt(Rnd() * 9)
Next
Quicksort(numArrray, 0, numArrray.Length - 1)
For i = 0 To x - 1
Console.Write(numArrray(i))
Next
Console.ReadLine()
End Sub
Sub Quicksort(ByVal array() As Integer, ByVal min As Integer, ByVal max As Integer)
Dim pivot As Integer
If min < max Then
pivot = partition(array, min, max)
Quicksort(array, min, pivot - 1)
Quicksort(array, pivot + 1, max)
End If
End Sub
Function partition(ByVal array() As Integer, ByVal min As Integer, ByVal max As Integer) As Integer
Dim pivot As Integer = array(max)
Dim count As Integer = min - 1
Dim placeholder As Integer
For i = min To max - 1
If array(i) < pivot Then
count += 1
placeholder = array(count)
array(count) = array(i)
array(i) = placeholder
End If
Next
If array(max) < array(count + 1) Then
placeholder = array(count + 1)
array(count + 1) = array(max)
array(max) = placeholder
End If
Return count
End Function

Since its a stack overflow it will be in the nested calls to Quicksort, looking at partition I think you have a bounds error, as per this version of the algorithm en.wikipedia.org/wiki/Quicksort you should return count + 1 from partition, on your current implementation count can start below min and stay that way which would be an infinite regression as your nested call would have the same inputs (ie if you return count = min - 1 then you do a recursive call back to Quicksort(array, pivot + 1, max) == Quicksort(array, min, max)

Related

My vb.NET project input doesn't in my primepairs function . how can I fix them?

Sub PrimePair()
Dim n As Integer
Dim count As Integer = 0
Console.WriteLine(count)
End Sub
Public Function PrimePairs(ByVal n As Integer, ByVal n2 As Long) As Integer
Dim count As Integer = 0
Console.ReadLine()
If n Mod 2 = 0 Then
For i = 1 To (n / 2) + 1
n2 = CLng(n - i)
If IsPrime(CLng(i)) And IsPrime(n2) = True Then
count += 1
End If
Next
Else
n2 = n - 2
If IsPrime(n2) = True Then
count = +1
End If
End If
Console.WriteLine(count)
Return n
End Function
End Module>
I can't run my code without sub. I created two functions, but the inputs I entered do not return in functions and do not print on the screen, I hope I can solve it, thanks for your attention. My project calculates how many different ways it prints the entered input value as a sum of prime numbers. About the Goldbach conjecture

How to create an excel VBA function for this particular purpose?

I am trying to create the function for this purpose: I have a year of birth list, I want to take out the last two numbers of that year and make a sum of them. If this sum is less than 10, it will be the result I need. If the result is more than 10 (ex: 9+9 = 18), I will separate this two digits (1 and 8) and make a sum of them (1+8=9. This is less than 10, so it will be my result)
This is my code, but it only give me the result of the first split and sum (18 in this example):
Function test(yob as range) as integer
Dim sum, i, j, i2, j2 as integer
i = mid(yob, 3, 1)
j = right(yob, 1)
sum = i + j
If sum < 10 then
test = sum
Else:
i2 = left(sum, 1)
j2 = right(sum, 1)
test = i2 + j2
End If
End function
Sub test()
Debug.Print getSum(9, 9)
'/ Prints 9
Debug.Print getSumShort(7, 7)
'/ Prints 5
End Sub
Public Function getSum(x As Long, y As Long)
Dim res As Long
res = x + y
Do While res > 10 '/ Keep repeating till its greater than 10
res = (res \ 10) + (res Mod 10)
Loop
getSum = res
End Function
'/ Another way using 9 as divider.
Public Function getSumShort(x As Long, y As Long)
Dim res As Long
res = (x + y) Mod 9
getSum = IIf(res = 0, 9, res)
End Function
Function sum2d(year)
sum2d=IIf(year mod 100, (year - 1) Mod 9 + 1, 0)
End Function
Sub Test
For Each v in Array(1980, 2001, 2017)
Debug.Print v; "=>"; sum2d(v)
Next
Next

Is there a quicksort routine without calling itself / without using recursion

The well known quicksort routine uses two recursive calls at the end. However, using the quicksort routine in Excel-VBA for large unsorted arrays (> 400 thousand elements) may lead to a memory stack overflow because of the many recursive calls.
Public Sub dQsort(List() As Double, ByVal min As Long, ByVal max As Long)
Dim med_value As Double
Dim hi As Long
Dim lo As Long
Dim i As Long
' If min >= max, the list contains 0 or 1 items so it is sorted.
If min >= max Then GoTo ErrorExit
' Pick the dividing value.
i = (max + min + 1) / 2
med_value = List(i)
' Swap it to the front.
List(i) = List(min)
lo = min
hi = max
Do
' Look down from hi for a value < med_value.
Do While List(hi) >= med_value
hi = hi - 1
If hi <= lo Then Exit Do
Loop
If hi <= lo Then
List(lo) = med_value
Exit Do
End If
' Swap the lo and hi values.
List(lo) = List(hi)
' Look up from lo for a value >= med_value.
lo = lo + 1
Do While List(lo) < med_value
lo = lo + 1
If lo >= hi Then Exit Do
Loop
If lo >= hi Then
lo = hi
List(hi) = med_value
Exit Do
End If
' Swap the lo and hi values.
List(hi) = List(lo)
Loop
' Sort the two sublists.
dQsort List(), min, lo - 1 ' Recursive call which I would like to avoid
dQsort List(), lo + 1, max ' Recursive call which I would like to avoid
End Sub
My question is: Who knows a modified quicksort routine with only a small penalty in extra time compared to the traditional quicksort routine (because of the mentioned memory stack overflow, you can only compare between the "old" and "new" routine for relative small unsorted arrays)?
Answers shown for the "Questions that may already have your answer" are not the answers for my question.
Here is a simple sort for doubles:
Public Sub aSort(ByRef InOut)
Dim i As Long, J As Long, Low As Long
Dim Hi As Long, Temp As Variant
Low = LBound(InOut)
Hi = UBound(InOut)
J = (Hi - Low + 1) \ 2
Do While J > 0
For i = Low To Hi - J
If InOut(i) > InOut(i + J) Then
Temp = InOut(i)
InOut(i) = InOut(i + J)
InOut(i + J) = Temp
End If
Next i
For i = Hi - J To Low Step -1
If InOut(i) > InOut(i + J) Then
Temp = InOut(i)
InOut(i) = InOut(i + J)
InOut(i + J) = Temp
End If
Next i
J = J \ 2
Loop
End Sub
Sub MAIN()
Dim ary(1 To 3) As Double, msg As String
Dim i As Long
ary(1) = 0.4
ary(2) = 0.1
ary(3) = 0.5
Call aSort(ary)
msg = ""
For i = 1 To 3
msg = msg & ary(i) & vbCrLf
Next i
MsgBox msg
End Sub
I don't know if it is "quick" enough.:
The mentioned merge sort has the same disadvantage as the traditional Quicksort: it also uses a recursive call (see the code for Excel’s VBA below, adapted from the named Wiki-page). The TopDownMergeSort sorts only the n-1 array value’s. Therefore, you need to insert the n-th value in the sorted array (of course at the correct place).
Sub Test_Method_MergeSort()
'Array adData with Doubles, starting from index = 1
Call TopDownMergeSort(adData)
Call InsertIntoSortedArray(adData, adData(UBound(adData)), 1, False)
End Sub
'// Array A[] has the items to sort; array B[] is a work array.
Sub TopDownMergeSort(ByRef A() As Double)
Dim B() As Double
Dim n As Long
Dim i As Long
'// duplicate array A[] into B[]
n = UBound(A)
ReDim B(n)
For i = 1 To n
B(i) = A(i)
Next i
'// sort data from B[] into A[]
TopDownSplitMerge B, 1, n, A
End Sub
'Sort the given run of array A[] using array B[] as a source.
'iBegin is inclusive; iEnd is exclusive (A[iEnd] is not in the set).
Sub TopDownSplitMerge(ByRef B() As Double, ByVal iBegin As Long, ByVal iEnd As Long, ByRef A() As Double)
Dim iMiddle As Long
Dim dTmp As Double
If (iEnd - iBegin) < 2 Then Exit Sub ' // if run size == 1
'// split the run longer than 1 item into halves
iMiddle = (iEnd + iBegin) / 2 '// iMiddle = mid point
'// recursively sort both runs from array A[] into B[]
TopDownSplitMerge A, iBegin, iMiddle, B '// sort the left run
TopDownSplitMerge A, iMiddle, iEnd, B '// sort the right run
'// merge the resulting runs from array B[] into A[]
TopDownMerge B, iBegin, iMiddle, iEnd, A
End Sub
'// Left source half is A[ iBegin:iMiddle-1].
'// Right source half is A[iMiddle:iEnd-1].
'// Result is B[ iBegin:iEnd-1].
Sub TopDownMerge(ByRef A() As Double, ByVal iBegin As Long, ByVal iMiddle As Long, ByVal iEnd As Long, ByRef B() As Double)
Dim i As Long
Dim j As Long
Dim k As Long
i = iBegin
j = iMiddle
'// While there are elements in the left or right runs...
For k = iBegin To iEnd - 1
'// If left run head exists and is <= existing right run head.
If ((i < iMiddle) And ((j >= iEnd) Or (A(i) <= A(j)))) Then
B(k) = A(i)
i = i + 1
Else
B(k) = A(j)
j = j + 1
End If
Next k
End Sub
Sub InsertIntoSortedArray(ByRef dSortedArray() As Double, ByVal dNewValue As Double, ByVal LowerBound As Long, Optional ByVal RedimNeeded As Boolean = False) ', xi As Long) As Long
Dim n As Long, ii As Long
n = UBound(dSortedArray)
If RedimNeeded Then
ReDim Preserve dSortedArray(n + 1)
Else
n = n - 1
End If
ii = n + 1
Do Until dSortedArray(ii - 1) <= dNewValue Or ii < (LowerBound + 1)
dSortedArray(ii) = dSortedArray(ii - 1)
ii = ii - 1
Loop
dSortedArray(ii) = dNewValue
End Sub
The solution I was looking for is without any recursive calls. With several additional variables for necessary administration purposes during the sorting steps I succeeded in the following Alternative quicksort “IAMWW_QSort”:
' This code belongs to one and the same Excel’s code module
Private Const msMODULE As String = "M_QSort"
Private alMin() As Long
Private alMax() As Long
Private abTopDownReady() As Boolean
Private aiTopDownIndex() As Integer ' 1 (= TopList) or 2 ( = DownList)
Private alParentIndex() As Long
Sub IAMWW_Qsort(ByRef List() As Double, ByVal Min As Long, ByVal Max As Long)
Dim med_value As Double
Dim hi As Long
Dim lo As Long
Dim i As Long
Dim l_List As Long
' If min >= max, the list contains 0 or 1 items so it is sorted.
If Min >= Max Then GoTo ExitPoint
Call Init(l_List, Min, Max)
Start:
If abTopDownReady(l_List, 1) And abTopDownReady(l_List, 2) Then
abTopDownReady(alParentIndex(l_List), aiTopDownIndex(l_List)) = True
l_List = l_List - 1
If l_List >= 0 Then
GoTo Start
Else
' Ready/list is sorted
GoTo ExitPoint
End If
End If
Min = alMin(l_List)
Max = alMax(l_List)
' -----------------------------------
' The traditional part of QuickSort
' Pick the dividing value.
i = (Max + Min + 1) / 2
med_value = List(i)
' Swap it to the front.
List(i) = List(Min)
lo = Min
hi = Max
Do
' Look down from hi for a value < med_value.
Do While List(hi) >= med_value
hi = hi - 1
If hi <= lo Then Exit Do
Loop
If hi <= lo Then
List(lo) = med_value
Exit Do
End If
' Swap the lo and hi values.
List(lo) = List(hi)
' Look up from lo for a value >= med_value.
lo = lo + 1
Do While List(lo) < med_value
lo = lo + 1
If lo >= hi Then Exit Do
Loop
If lo >= hi Then
lo = hi
List(hi) = med_value
Exit Do
End If
' Swap the lo and hi values.
List(hi) = List(lo)
Loop
' End of the traditional part of QuickSort
' -----------------------------------------
If Max > (lo + 1) Then
' top part as a new sublist
l_List = l_List + 1
Init_NewSubList l_List, l_List - 1, 1, lo + 1, Max
If (lo - 1) > Min Then
' down part as a new sublist
l_List = l_List + 1
Init_NewSubList l_List, l_List - 2, 2, Min, lo - 1
Else
' down part (=2) is sorted/ready
abTopDownReady(l_List - 1, 2) = True
End If
ElseIf (lo - 1) > Min Then
' Top part is sorted/ready...
abTopDownReady(l_List, 1) = True
' ... and down part is a new sublist.
l_List = l_List + 1
Init_NewSubList l_List, l_List - 1, 2, Min, lo - 1
Else
' Both the top (=1) and down part (=2) are sorted/ready ...
abTopDownReady(l_List, 1) = True
abTopDownReady(l_List, 2) = True
' ... and therefore, the parent (sub)list is also sorted/ready ...
abTopDownReady(alParentIndex(l_List), aiTopDownIndex(l_List)) = True
' ... and continue with the before last created new sublist.
l_List = l_List - 1
End If
If l_List >= 0 Then GoTo Start
ExitPoint:
End Sub
Private Sub Init_NewSubList(ByVal Nr As Long, ByVal Nr_Parent As Long, ByVal iTopDownIndex As Integer, ByVal Min As Long, ByVal Max As Long)
' Nr = number of new sublist
' Nr_Parent = the parent's list number of the new sublist
' iTopDownIndex = index for top (=1) or down part (=2) sublist
aiTopDownIndex(Nr) = iTopDownIndex '= 2 ' new sub list is a down part sublist
alParentIndex(Nr) = Nr_Parent 'l_List - 2
abTopDownReady(Nr, 1) = False 'The new sublist has a top part sublist, not ready yet
abTopDownReady(Nr, 2) = False 'The new sublist has a down part sublist, not ready yet
' min and max values of the new sublist
alMin(Nr) = Min
alMax(Nr) = Max 'lo - 1
End Sub
Private Sub Init(ByRef Nr As Long, ByVal Min As Long, ByVal Max As Long)
Dim lArraySize As Long
lArraySize = Max - Min + 1
ReDim alMin(lArraySize)
ReDim alMax(lArraySize)
ReDim abTopDownReady(lArraySize, 2)
ReDim aiTopDownIndex(lArraySize)
ReDim alParentIndex(lArraySize)
Nr = 0
alMin(Nr) = Min
alMax(Nr) = Max
aiTopDownIndex(0) = 2 ' Initial list is assumed to be a down part (= 2)
End Sub
The penalty in extra time because of the additional administrative code lines is very small.

Overflow error VBA

I have this code below, and I'm getting an overflow error at the line:
s = s + (x Mod 10) [first line in the Do Loop]
Why? I declared x and s to be of type Double. Adding two doubles, why is this not working?
Thanks for your help.
Public Sub bidon1()
Dim i As Double, x As Double, s As Double, k As Byte, h As Byte
Dim y(1 To 6) As Double
For i = 1 To 1000000
x = i ^ 3
Do
s = s + (x Mod 10)
x = x \ 10
Loop Until x = 0
If s = x Then
k = k + 1
y(k) = x
If y(6) > 0 Then
For h = 1 To 6
Debug.Print y(h)
Next
Exit Sub
End If
End If
Next
End Sub
The problem is that the VBA mod operator coerces its arguments to be integers (if they are not already so). It is this implicit coercion which is causing the overflow. See this question: Mod with Doubles
On Edit:
Based on your comments, you want to be able to add together the digits in a largish integer. The following function might help:
Function DigitSum(num As Variant) As Long
'Takes a variant which represents an integer type
'such as Integer, Long or Decimal
'and returns the sum of its digits
Dim sum As Long, i As Long, s As String
s = CStr(num)
For i = 1 To Len(s)
sum = sum + Val(Mid(s, i, 1))
Next i
DigitSum = sum
End Function
The following test sub shows how it can be used to correctly get the sum of the digits in 999999^3:
Sub test()
Dim x As Variant, y As Variant
Debug.Print "Naive approach: " & DigitSum(999999 ^ 3)
y = CDec(999999)
x = y * y * y
Debug.Print "CDec approach: " & DigitSum(x)
End Sub
Output:
Naive approach: 63
CDec approach: 108
Since 999999^3 = 999997000002999999, only the second result is accurate. The first result is only the sum of the digits in the string representation of the double 999999^3 = 9.99997000003E+17

Randomly divide a whole number m into n parts such that the parts are whole numbers and each part lies between x and y

As an example. I want to randomly hand out 100 chocolates to 25 kids. I cannot give any kid more than 10 chocolates.
So here m = 100, n = 25, x = 1 and y = 12.
I have checked these questions.
Dividing a number into m parts uniformly randomly
Dividing a number into random unequal parts
They do give some idea but in these questions x and y are not specified.
So basically,
1) Total No. of Chocolates = 100
2) I can only give minimum 1 and maximum 12 chocolates to each kid
3) Chocolates should be distributed between 25 kids
4) I do not want any distribution (uniform or normal) - it should be purely random. (I am willing to exclude this condition if all else fails.)
Private Function divideUniformlyRandomly(n As Integer, m As Integer) As Integer()
Dim rRandom As New Random
Dim fences As Integer() = New Integer(m - 2) {}
For i As Integer = 0 To m - 3
fences(i) = rRandom.Next(0, n - 1)
Next
[Array].Sort(fences)
Dim result As Integer() = New Integer(m - 1) {}
result(0) = fences(0)
For i As Integer = 1 To m - 3
result(i) = fences(i + 1) - fences(i)
Next
result(m - 1) = n - 1 - fences(m - 2)
Return result
End Function
This does work but I get 0 and 13 as well. I cannot ensure x and y here.
Give each child x chocolate. This will leave you with m - (n * x) to distribute randomly. Keep distributing to children that have less than y chocolates, until there are no more chocolates.
Private Function divideUniformlyRandomly(n As Integer, m As Integer, x As Integer, y As Integer) As Integer()
Dim rRandom As New Random
Dim aResult As Integer() = New Integer(n - 1) {}
Dim i As Integer = 0
Dim remaining As Integer = m
' Every n must have a min of x.
For i = 0 To n - 1
aResult(i) = x
remaining -= x
Next
' distribute the remaining m over the children randomly
While remaining > 0
' pick a child randomly
i = rRandom.Next(0, n)
' if the child has less than y, give them one
If aResult(i) < y Then
aResult(i) += 1
remaining -= 1
End If
End While
' Debug
Dim sum As Integer = 0
For i = 0 To n - 1
Console.WriteLine("{0}: {1}", i, aResult(i))
sum += aResult(i)
Next
Console.WriteLine("Sum: {0}", sum)
divideUniformlyRandomly = aResult
End Function