I'm having overflow issues in this two-variable optimization program - vba

First off, here is what I have so far:
Option Explicit
Dim y As Variant
Dim yforx As Variant
Dim yfork As Variant
Dim ynew As Variant
Dim ymin As Variant
Dim x As Variant
Dim xmin As Variant
Dim k As Variant
Dim kmin As Variant
Dim s As Variant
Dim Z As Variant
Dim Track As Variant
Sub PracticeProgram()
'Selects the right sheet
Sheets("PracticeProgram").Select
'y = k ^ 2 * (x ^ 2 + 2 * x * k - 6) / (x + k) ^ 2
'these are the bounds we are stepping through
Track = 0
x = 1
xmin = 1
k = 1
kmin = 1
y = 100000000
yforx = 100000
yfork = 1000000000
Do
y = 100000000
For x = 0 To 1000 Step 0.1
ynew = kmin ^ 2 * (x ^ 2 + 2 * x * kmin - 6) / (x + kmin) ^ 2
'This checks the new y-value against an absurdly high y-value we know is wrong. if it is less than this y-value, we keep the x-value that corresponds with it.
If ynew < y Then
xmin = x
y = ynew
yforx = y
xmin = Application.Evaluate("=Round(" & xmin & ", 3)")
Else
End If
Next
MsgBox (yforx)
For k = 0 To 1000 Step 0.1
y = k ^ 2 * (xmin ^ 2 + 2 * xmin * k - 6) / (xmin + k) ^ 2
If ynew < y Then
kmin = k
y = ynew
yfork = y
kmin = Application.Evaluate("=Round(" & kmin & ",3)")
Else
End If
Next
MsgBox (yfork)
Loop Until (Abs(yforx - yfork) < 10)
End Sub
This program is supposed to find the values of x and k in order to minimize the value of y. This is a practice for a much more complicated program that will use this same concept. In my actual program y, k, and x will all be greater than zero no matter what, but since it was hard to think of a simple equation whose results would be in the shape of a parabola opening up, I decided to allow negative answers for this practice program.
Basically, it should bounce back and forth between the equations finding the ideal values for x and k until finally it has a minimal answer for y using ideal answers for both x and k. I'm not sure what the actual answer is, so I'm letting it stop within a range of 10. If it works, I'll make it smaller, but I don't want the program going for forever, just in case.
MY PROBLEM: I keep getting overflow errors! I'm trying to round the values for xmin and kmin to three figures after the decimal, but it doesn't seem to be helping. Am I using them wrong? Can someone help me get this program working?

You're doing a division by zero. xmin = 0, k = 0, (xmin + k) ^ 2 = 0. (I'm not sure why it isn't reporting division by zero.)
A suggestion: use the Locals pane to see the value of local variables. You can also use the Watch pane to see the value of expressions you want to monitor.

Related

I have a trouble with overflow

I don't understand why 2 of my codes have overflow error
Sub varr3_1()
Dim x As Single
Dim y As Single
For x = 1 To 2 Step 0.2
y = Sqr((x - 1) / (x + 1))
Debug.Print x, y
Next x
End Sub
Sub varr3_3()
Dim x As Single
Dim z As Double
For x = 3 To 8 Step 0.9
z = 2
While (z > 1)
z = Log(x) + Tan(2 * x)
Debug.Print z
Wend
Next x
End Sub
I tried to change Single to Integer and so on but there is still a problem
The first sub Var3_1() works fine for me.
The second sub gets stuck in an endless loop on the second iteration of x. The value of z = 19.86... which will always be greater than 1 so the While/Wend loop never exits thus resulting in an eventual overflow.

spacing between two points in 3d cordinate system

i am a bit new to this but I'm trying to create a randomly generated 3d coordinate points with equal spacing, I've tried using for each loop but im confused on how to use in. the purpose is to generate sphere around that point but some sphere are overlapping each other. thanks in advance. the code below is to show how I'm generating the sphere
For i = 0 To noofsp - 1
x = Rnd(1) * maxDist
ws1.Cells(i + 5, 2) = x
y = Rnd(1) * maxDist
ws1.Cells(i + 5, 3) = y
z = Rnd(1) * maxDist
ws1.Cells(i + 5, 4) = z
centers.Add({x, y, z})
Next
You'll need to check the new point against all the other points to make sure that your new point is at a greater distance that the sum of the radii of your new sphere and each sphere you're checking against
You'll need to use pythagoras' theorem to check that the distances and I found the code below from this site. The code on the site is written in c#, but here is the vb.net version.
Public Function Distance3D(x1 As Double, y1 As Double, z1 As Double, x2 As Double, y2 As Double, z2 As Double) As Double
' __________________________________
'd = √ (x2-x1)^2 + (y2-y1)^2 + (z2-z1)^2
'
'Our end result
Dim result As Double
'Take x2-x1, then square it
Dim part1 As Double = Math.Pow((x2 - x1), 2)
'Take y2-y1, then square it
Dim part2 As Double = Math.Pow((y2 - y1), 2)
'Take z2-z1, then square it
Dim part3 As Double = Math.Pow((z2 - z1), 2)
'Add both of the parts together
Dim underRadical As Double = part1 + part2 + part3
'Get the square root of the parts
result = Math.Sqrt(underRadical)
'Return our result
Return result
End Function
To generate the spheres, you would need to expand your code to include checking the new point against all the previously generated points. That code is below with comments.
I have assumed the definition of a variable called minDistance to specify how far apart the centre of the spheres should be. I'm also assuming that all the spheres are the same size. The number should be twice the radius of the spheres
Private Sub GenerateSpheres()
Randomize
For i As Integer = 0 To noofsp - 1
Dim distanceOK As Boolean = False
Dim x, y, z As Integer
'keep generating points until one is found that is
'far enough away. When it is, add it to your data
While distanceOK = False
x = Rnd(1) * maxDist
y = Rnd(1) * maxDist
z = Rnd(1) * maxDist
'If no other points have been generated yet, don't bother
'checking your new point
If centers.Count = 0 Then
distanceOK = True
Else
'If other points exist, loop through the list and check distance
For j As Integer = 0 To centers.Count - 1
'if the point is too close to any other, stop checking,
'exit the For Loop and the While Loop will generate a new
'coordinate for checking, and so on
Dim dist As Integer = Distance3D(centers(j)(0), centers(j)(1), centers(j)(2), x, y, z)
If dist <= minDistance Then
distanceOK = False
'exit the For loop and start the next iteration of the While Loop
Continue While
End If
Next
'If all previous points have been checked none are too close
'flag distanceOK as true
distanceOK = True
End If
End While
'ws1.Cells(i + 5, 2) = x
'ws1.Cells(i + 5, 3) = y
'ws1.Cells(i + 5, 4) = z
centers.Add({x, y, z})
Next
End Sub

Finding the x intercept of a 4th degree polynomial using small increments

I am trying to find the x intercept of a 4th degree function by incrementing the x value. I feel like this way doesnt work always and isnt the most efficient way to do this, is there another way I am missing?
My code is:
Sub Findintercept()
Dim equation As Double, x As Double, A As Double, B As Double, C As Double, D As Double, E As Double
A = 0.000200878
B = -0.002203704
C = 0.0086
D = -0.02333
E = 0.02033
x = 0
equation = A * x ^ 4 + B * x ^ 3 + C * x ^ 2 + D * x + E
While (equation > 0.00001 Or equation < -0.00001)
If (x > 5) Then
MsgBox "Could not find intercept"
equation = 0
Else
x = x + 0.0001
equation = A * x ^ 4 + B * x ^ 3 + C * x ^ 2 + D * x + E
End If
Wend
MsgBox x
End Sub
Sometimes it fails to find the intercept hence the IF condition in the while loop. (Im always expecting the intercept to be less than 5!
Your method suffers from two problems:
You assume a step size to change x. The step could be too large, causing you to "walk past" the value your are looking for. To deal with this, you make a small step size, which can mean an excessively large number of iterations are needed to find the solution.
You always assume the same direction to change x. Even with seemingly small values for your step size, you could "walk past" the solution, and have no means to change direction. Or, your initial guess may be on the wrong side of the solution, and you never find an answer.
The Newton-Raphson method handles both of these issues neatly. You do still need to choose your initial guess somewhat close to the root you are looking for.
This method does have potential problems, but for polynomials such as the one you are dealing with, it is quite good.
Below is a simple VBA sub that implements this method. It solves your problem in 4 iterations. I recommend adjusting the initial guess (xii) a lot to see how it impacts the solution you get.
Sub SimpleNewtonRaphson()
Const Tol As Double = 1E-06
Const MaxIter As Long = 50
Dim xi As Double, xii As Double, deriv As Double
Dim IterCount As Long
' Initialize
xi = 0#
xii = 1#
IterCount = 0
' Method
Do While IterCount < MaxIter And Abs(xii - xi) > Tol
xi = xii
deriv = myDeriv(xi)
If deriv = 0# Then Exit Do
xii = xi - myFunc(xi) / deriv
IterCount = IterCount + 1
Loop
' Results
If deriv = 0 Then MsgBox "Ran into a 0 derivative, modify initial guess"
If IterCount >= MaxIter Then MsgBox "MaxIterations reached"
If Abs(xii - xi) <= Tol Then MsgBox "Solution found #" & vbCrLf & "F(" & xii & ") = " & myFunc(xii)
End Sub
... and two VBA functions for your equation and it's derivative ...
Function myFunc(x As Double) As Double
Const A As Double = 0.000200878
Const B As Double = -0.002203704
Const C As Double = 0.0086
Const D As Double = -0.02333
Const E = 0.02033
myFunc = A * x ^ 4 + B * x ^ 3 + C * x ^ 2 + D * x + E
End Function
Function myDeriv(x As Double) As Double
Const A As Double = 0.000200878
Const B As Double = -0.002203704
Const C As Double = 0.0086
Const D As Double = -0.02333
myDeriv = 4 * A * x ^ 3 + 3 * B * x ^ 2 + 2 * C * x + D
End Function

run time error 5 in VBA excel when working with array

I use vba on excel 2007, OS: windows vista, to make calculation using kinematic wave equation in finite difference scheme. But, when it runs the run-time 5 (invalid procedure call or arguments) message appears. I really don't what is going wrong. Anyone can help?
Sub kwave()
Dim u(500, 500), yy(500, 500), alpha, dt, dx, m, n, so, r, f, X, L, K As Single
Dim i, j As Integer
dx = 0.1
dt = 0.01
L = 10
m = 5 / 3
r = 1
f = 0.5
n = 0.025
so = 0.1 'this is slope
alpha = 1 / n * so ^ 0.5
X = 0
For i = 0 To 100
Cells(i + 1, 1) = X
u(i, 1) = L - so * X
X = X + dx
Cells(i + 1, 2) = u(i, 1)
Next i
For j = 0 To 100
For i = 1 To 100
'predictor step
u(i, j + 1) = u(i, j) - alpha * dt / dx * (u(i + 1, j) ^ m - u(i, j) ^ m) + (r - f) * dt
'corrector step
K = u(i, j + 1) ^ m - u(i - 1, j + 1) ^ m '<<<<----- RUNTIME ERROR 5 HAPPENS AT THIS LINE
yy(i, j + 1) = 0.5 * ((yy(i, j) + u(i, j + 1)) - alpha * dt / dx * K + (r - f) * dt)
Next i
Next j
End Sub
You are declaring the variables wrong- the array should store a double/single but it is defaulting to a variant. See this article.
http://www.cpearson.com/excel/declaringvariables.aspx -
"Pay Attention To Variables Declared With One Dim Statement
VBA allows declaring more than one variable with a single Dim
statement. I don't like this for stylistic reasons, but others do
prefer it. However, it is important to remember how variables will be
typed. Consider the following code:
Dim J, K, L As Long You may think that all three variables are
declared as Long types. This is not the case. Only L is typed as a
Long. The variables J and K are typed as Variant. This declaration is
functionally equivalent to the following:
Dim J As Variant, K As Variant, L As Long You should use the As Type
modifier for each variable declared with the Dim statement:
Dim J As Long, K As Long, L As Long "
Additionally, when i = 99 and j = 10, u(99,11), which is j+1, produces a negative number. Note that this does not fully cause the problem though, because you can raise negative numbers to exponents. Ex, -5^3 = -125

Ignore overflow error when multiplication result is bigger than what a double can hold

During some iterative optimization, the following VBA code for the computation of the bivariate normal CDF sometimes throws an Overflow error on the line with z = hx * hy * c inside the while loop of the upper function.
I debugged the code and the overflow occurs when the numbers being multiplied result in a number bigger than what a double can hold.
Can you show me how to handle the problem by ignoring the iterations of the loop with such high values - I guess that's the only feasible solution (?). I tried myself with a On Error Goto nextiteration line before the multiplication and placing the nextiteration jump point before the Wend, but the error persists.
Function tetrachoric(x As Double, y As Double, rho As Double) As Double
Const FACCURACY As Double = 0.0000000000001
Const MinStopK As Integer = 20
Dim k As Integer
Dim c As Double
Dim z As Double
Dim s As Double
Dim hx As Double
Dim hx1 As Double
Dim hx2 As Double
Dim hy As Double
Dim hy1 As Double
Dim hy2 As Double
Dim CheckPass As Integer
hx = 1
hy = 1
hx1 = 0
hy1 = 0
k = 0
c = rho
z = c
s = z
CheckPass = 0
While CheckPass < MinStopK
k = k + 1
hx2 = hx1
hy2 = hy1
hx1 = hx
hy1 = hy
hx = x * hx1 - (k - 1) * hx2
hy = y * hy1 - (k - 1) * hy2
c = c * rho / (k + 1)
z = hx * hy * c
s = s + z
If Abs(z / s) < FACCURACY Then
CheckPass = CheckPass + 1
Else
CheckPass = 0
End If
Wend
tetrachoric = s
End Function
Public Function bivnor(x As Double, y As Double, rho As Double) As Double
'
' bivnor function
' Calculates bivariat normal CDF F(x,y,rho) for a pair of standard normal
' random variables with correlation RHO
'
If rho = 0 Then
bivnor = Application.WorksheetFunction.NormSDist(x) * _
Application.WorksheetFunction.NormSDist(y)
Else
bivnor = Application.WorksheetFunction.NormSDist(x) * _
Application.WorksheetFunction.NormSDist(y) + _
Application.WorksheetFunction.NormDist(x, 0, 1, False) * _
Application.WorksheetFunction.NormDist(y, 0, 1, False) * _
tetrachoric(x, y, rho)
End If
End Function
Source: Available for download at http://michael.marginalq.com/
you're hitting on the limits of the computer architecture. Many complex algorithms can't be implemented 1:1 with their mathematical representation because of performance reasons and/or erroneous behavior when overflowing. There's an exceptionally good blog about these issues - John D. Cook.
Please take a look here for a better implementation.
You can also try binding an external library, that gives you arbitrary precision number handling, of course implemented using very expensive (in terms of CPU time) software algorithms. More can be found here.
Updated code using On Error Resume Next instead of On Error Goto:
While CheckPass < MinStopK
k = k + 1
hx2 = hx1
hy2 = hy1
hx1 = hx
hy1 = hy
hx = x * hx1 - (k - 1) * hx2
hy = y * hy1 - (k - 1) * hy2
c = c * rho / (k + 1)
On Error Resume Next
z = hx * hy * c
If Err.Number = 0 Then
s = s + z
If Abs(z / s) < FACCURACY Then
CheckPass = CheckPass + 1
Else
CheckPass = 0
End If
Else
Err.Clear
End If
Wend
http://www.codeproject.com/KB/recipes/float_point.aspx treats how to "Use Logarithms to Avoid Overflow and Underflow", which is a simple but quite effective way of working around overflow problems. In fact, it's so simple yet logical, why haven't we thought of that solution ourselves? ;)