+ Reply to Thread
Results 1 to 7 of 7
  1. #1
    Valued Forum Contributor
    Join Date
    03-03-2005
    Posts
    302

    LOOPING: Faster rate of convergence required

    I am developing a UDF which requires some looping to arrive at the result. To keep it simple, part of the routine is akin to solving the following Amount equation for i.

    Find i such that A=1.72846 given A=(1+i)^10

    The following code resolves to the correct answer of i = 5.6248%

    Code:
    Sub nn()
    Time1 = Timer
    i = 0.01
    k = 1
    Do
    A = (1 + i) ^ 10
    i = i + 0.0000001
    k = k + 1
    'Debug.Print k & "........" & i
    Loop Until Application.Round(A, 4) = 1.7285
    MsgBox i & " ... " & Timer - Time1
    End Sub
    But it does so using 462,477 iterations and taking a whooping 90 seconds! (One would expect under 100 iterations to be ideal).

    While Solver can easily tackle this problem, the Solver tool cannot be deployed in my exercise. Solver requires a WorkSheet interface and is hence inappropriate for incorporation in the UDF.

    Can someone kindly come up with a non-linear algorithm that will speed up the rate of convergence? I would be happy with a solution that takes under 5 seconds.
    Last edited by davidm; 03-24-2008 at 11:47 PM.

  2. #2
    Registered User
    Join Date
    11-17-2005
    Location
    Georgia
    Posts
    78

    18 Loops

    Timer does not register ---- 18 loops to solve. Set a Min and Max i for limits and let it go...
    18 Loops was based on Starting i with a min value of 0% and a Max value of 100%



    Code:
    Sub nn()
    Dim i As Double
    Dim A As Double
    Dim B As Double
    Dim Min_i As Double
    Dim Max_i As Double
    Dim Step As Double
    Dim Iterations As Long
    
    Iterations = 0
    Min_i = 0
    Max_i = 1
    Step = Max_i - Min_i
    i = Min_i
    
    Do
        A = (1 + i) ^ 10
        B = Application.Round(A, 4)
        
        If B < 1.7285 Then
            i = i + Step
            Step = Step / 2
        ElseIf B > 1.7285 Then
            i = i - Step
            Step = Step / 2
        Else
            MsgBox i & " ... " & Iterations
            Exit Sub
        End If
        
        Iterations = Iterations + 1
    Loop Until B = 1.7285
    MsgBox i & " ... " & Iterations
    End Sub

  3. #3
    Forum Guru mikerickson's Avatar
    Join Date
    03-30-2007
    Location
    Davis CA
    MS-Off Ver
    Excel 2011
    Posts
    2,929
    If A = (i +1)^10

    then LN(A)=10*LN(i+1)

    i+1 = e ^ (LN(A)/10)

    i = (e ^ (LN(A)/10)) -1

    Code:
    Dim a as Double
    Dim i as Double
    
    a = 1.72846
    
    i = Exp(Log(a) / 10) - 1
    returns 0.0562480820384001 with no loops.

    The spreadsheet function =EXP(LN(A1)/10)-1 gives the same result.
    Last edited by mikerickson; 03-25-2008 at 01:27 AM.
    _
    ...How to Cross-post politely...
    ..Wrap code by selecting the code and clicking the # or read this. Thank you.

  4. #4
    Registered User
    Join Date
    11-17-2005
    Location
    Georgia
    Posts
    78

    Much Better

    I was not even thinking about logs....

    -Lee

  5. #5
    Forum Guru mikerickson's Avatar
    Join Date
    03-30-2007
    Location
    Davis CA
    MS-Off Ver
    Excel 2011
    Posts
    2,929
    Your binary search is nice.
    I adapted it to find roots of any function. It needs some more error checking (are the intial boundries appropriate?, etc.).

    The Sub test solves both the problem in the OP and finds the cube root of 3.

    Code:
    Sub test()
        MsgBox binarySolver("Ftn", 1.72846, 0, 3, 10 ^ -4) & " is the solution to the OPs problem."
        MsgBox binarySolver("Cube", 3, 0, 5, 10 ^ -7) & " is the cube root of 3."
    End Sub
    
    Function binarySolver(thisFunction As String, Goal As Double, Low As Double, High As Double, Accuracy As Double) As Double
    Rem to solve thisFunction(x) = Goal within accuracy
    Rem Low and High are estimates that bracket the solution
    Dim Mid As Double
    Dim FtnLow As Double, FtnMid As Double, FtnHigh As Double
    Dim loopCount As Long
    
    FtnLow = Run(thisFunction, Low)
    FtnHigh = Run(thisFunction, High)
    loopCount = 0
    Do
        loopCount = loopCount + 1
        
        Mid = (Low + High) / 2
        
        FtnMid = Run(thisFunction, Mid)
        
        If Sgn(FtnLow - Goal) = Sgn(FtnMid - Goal) Then
            Low = Mid: FtnLow = FtnMid
        Else
            High = Mid: FtnHigh = FtnMid
        End If
    
    Loop Until Abs(Goal - FtnMid) < Accuracy
    
    binarySolver = Mid
    'MsgBox loopCount
    End Function
    
    Function Ftn(a As Double) As Double
        Ftn = (1 + a) ^ 10
        End Function
    
    Function Cube(a As Double) As Double
        Cube = a ^ 3
        End Function
    Last edited by mikerickson; 03-25-2008 at 02:10 AM.
    _
    ...How to Cross-post politely...
    ..Wrap code by selecting the code and clicking the # or read this. Thank you.

  6. #6
    Registered User
    Join Date
    11-17-2005
    Location
    Georgia
    Posts
    78

    Thanks

    Thanks for the compliment

    I think I will archive your adaptation for future use


    - Lee

  7. #7
    Valued Forum Contributor
    Join Date
    03-03-2005
    Posts
    302
    Thanks Tek and Mik.

    The binary search technique does the trick. My example appeared to be too simplistic to allow for making the RequiredToFind the subject of the equation. In my original, transformation, log or otherwise, is not possible as in "Find x if y = .10 in

    y = x - x*(2/(1+x)^15 ).

    Once again, thanks.

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts

Search Engine Friendly URLs by vBSEO 3.2.0