CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 12 of 12
  1. #1
    Guest

    tough VB question - see if you can answer it!

    It took me a while to get it, and lots of frustation, but I eventually got it... Here is the question:

    I want any one of you to write code that will solve the equation a ^ x = b for x, if the values of a and b are given. The hard part to it? You can't use any LOG () or EXP () functions. For that matter, the only mathematical function you can use is addition/subtraction, multiplication/division, and ^.

    So far nobody I asked in my neighborhood has gotten it...Usually is is just:


    public Sub Exponent (a as double, b as double)
    x = log (b) / log (a)
    End sub




    I want you to do it without log () functions or exp () functions... Or any built in VB math function besides +/-, *//, ^.



  2. #2
    Join Date
    Jan 2000
    Posts
    20

    Re: tough VB question - see if you can answer it!

    Taking the inverse of a^x = b we come up with x = Log_a(b). This can be rewritten as ln(b)/ln(a)...so now all we need are natural logarithms.

    Assuming f(x)=1/x, then the antiderivative F(x)=ln(x) over the interval 1 to x. We can approximate this integral using Reimann Sums...so all we're really looking for are the definite integrals of 1/x from 1 to a and 1/x from 1 to b. Once we get those we just divide and there's our answer. Not 100%, but we can get pretty darn close.


    public Sub solveForX(a as Double, b as Double)
    Dim answer as string
    Dim i as Long
    Dim reimannSection as Double
    Dim naturalLogOfA as Double
    Dim naturalLogOfB as Double

    ' catch division by zero
    If a = 1 then
    answer = "The answer is: Infinity"
    Exit Sub
    End If

    ' initialize variables
    naturalLogOfA = 0
    naturalLogOfB = 0

    ' integrate on interval a
    reimannSection = (a - 1) / 1000000
    for i = 1 to 1000000
    naturalLogOfA = naturalLogOfA + (reimannSection * (1 / (1 + i * reimannSection)))
    next i
    ' integrate on interval b
    reimannSection = (b - 1) / 1000000
    for i = 1 to 1000000
    naturalLogOfB = naturalLogOfB + (reimannSection * (1 / (1 + i * reimannSection)))
    next i

    ' calculate answer
    answer = "The answer is: " & CStr(naturalLogOfB / naturalLogOfA)
    End Sub




    We can, of course, write in code to round to the nearest significant digit.



  3. #3
    Guest

    Re: tough VB question - see if you can answer it!

    it doesn't appear to work...i can't get an answer. I entered the code:

    Msgbox answer



    after you say what the answer is, yet I get no msgbox response....


  4. #4
    Join Date
    Aug 1999
    Location
    US, Florida
    Posts
    817

    Re: tough VB question - see if you can answer it!

    yes place MsgBox answer right after
    answer = "The answer is: " & CStr(naturalLogOfB / naturalLogOfA)
    so it will look like this
    answer = "The answer is: " & CStr(naturalLogOfB / naturalLogOfA)
    MsgBox answer



    But to tell you the truth ...this is great but really "slick" piece of code....my PC calculates this for like 2 secs...too long, and I entered really basic numbers: a = 5 and b = 125 where x should be 3....I get the answer: 2.99996476858295 but other then that it's great code.



  5. #5
    Join Date
    Aug 1999
    Location
    US, Florida
    Posts
    817

    Re: tough VB question - see if you can answer it!

    I got better solution for this...not sure thos if it'll satisfy you, but it's pretty darn good
    make text1, text2,text3 and text4 and command button
    assume text1 is a, text2 is x (unknown) and text3 is b
    SET all textboxes to "0" except text2, because text2 is our "x" (unknown), so might as well set it to "x"

    private Sub Command1_Click()
    num = Text3.Text / Text1.Text
    Do Until num >= Text1.Text
    num = num / Text1.Text
    Text4.Text = Text4.Text + 1
    Loop
    MsgBox Text4
    End Sub



    Ok now enter 5 into text1 (now a = 5), put 125 into text3 (now b = 125), press command1.....the asnwer should pop-up in msgbox, answer also is stored in text4...it's 3
    I tried many integers and I lways get correct answer, so that's alternative way.....unless i didn't understand something from your problem



  6. #6
    Join Date
    Aug 1999
    Location
    US, Florida
    Posts
    817

    Re: tough VB question - see if you can answer it!

    forgot...don't forget to clear text4 everytime you click command1, or existed number will be added to a new one...giving you wrong answer


  7. #7
    Join Date
    Aug 1999
    Location
    US, Florida
    Posts
    817

    Re: tough VB question - see if you can answer it!

    crap....not clear text4, set it to "2" because before loop you divide 125 by 5, making it as 1st division, and when you get to 5 you get second 1st becouse 5 = 5^1,so text4 should always be 2 before pressing command1.....It's hard to explain the way I think, just look through code and you will get it...hopefully.
    What it does is, take b and keeps on dividing it by a until the answer will be equal to a, with each division it adds 1 to text4 as if what power.....confusing ehh?


  8. #8
    Join Date
    Aug 1999
    Location
    US, Florida
    Posts
    817

    Re: tough VB question - see if you can answer it!

    ok sorry about so many replys....here is the final code:
    1. Make text1,(optionaly text2), text3,text4, and command1
    2. Place this code into form

    private Sub Command1_Click()
    Text4 = "2"
    num = Text3.Text / Text1.Text
    Do Until num = Text1.Text
    num = num / Text1.Text
    Text4.Text = Text4.Text + 1
    Loop
    MsgBox Text4
    End Sub

    private Sub Form_Load()
    Text1 = "5"
    Text3 = "125"
    Text4 = "2"
    Text4.Visible = false
    End Sub





    Note: It works only with real numbers



  9. #9
    Join Date
    Jan 2000
    Posts
    20

    Re: tough VB question - see if you can answer it!

    Yeah, it takes a while 'cause it's approximating with Reimann Sums...the idea is to take the limit as the number of sections goes to infinity...or rather as the value of reimannSection approaches zero. Since we realistically can't do this, I picked 1,000,000 cycles for the loop just 'cause in my mind it's a pretty big number. However, doing it this way, the larger numbers you use the greater the error....much larger magnitude of error dividing 700000 into a million sections than if you did it with just 7.

    Try taking the loop down to 100,000 (and sections down to 1/100,000) and it should calculate instantly. The margin or error will be greater, but then it depends on how many significant digits you need. If you only want a few places after the decimal, this should suffice...you can round up to the nearest significant digit.

    I believe there's a Taylor Series that can do this integral too...and then you just calculate the error and factor it in. But, alas, it's been a while since I had calculus...I really don't remember this stuff anymore.

    xxMariusxx


  10. #10
    Join Date
    Jan 2000
    Posts
    10

    Re: tough VB question - see if you can answer it!

    I did something totally different. The problem with Andy K's code is that if you entered the equation:

    3 ^ x = 5

    It wouldn't be able to solve it...all it works with is if x is an integer. In many cases, it is not. The code I got was something like this. You have text1 and text3, so the equation would be this:

    val(text1) ^ x = val(text3), where it solves for x. The answer is recorded in xres$, and put in a textbox text4.



    xres$ = " "
    'Assign variables y , n2 , n1 , and f
    Y = 1 'Y is equal to 1. It is the exponent being tested
    n2 = Val(Text3.Text) 'The answer of the equation. In 3^x=5, it would be 5.
    n1 = Val(Text1.Text) 'The number. In 3^x=5, it would be 3.
    'F is how many times the process shall be repeated. F is 0 in the start.
    If n2 = 0 then Text4.Text = "": Exit Sub 'Make sure it is not a ^ x = 0, nonreal answer
    If n1 = 1 And n2 <> 1 then Text4.Text = "": Exit Sub 'Make sure it is not 1 ^ x = 2, can't have infinity!
    If n2 < 0 And n1 > 0 then Text4.Text = "": Exit Sub 'Make sure it is not a positive to the power of x is negative - can't have negative infinity!
    f = 0
    go:
    f = f + 1 'Add 1 to f to indicate process has been repeated one more time
    a1 = n1 ^ Y 'First number, is n1 ^ y, where y is the exponent, and n1 is val(text1.text)
    a2 = n1 ^ (Y + 1) 'Second number. N1 ^ (Y+1)
    If a1 = n2 then xres$ = Str(Y): GoTo Done 'If x is a whole number, ends.
    If n2 = 1 And n1 <> 0 then xres$ = Str(0): GoTo Done 'If n1^y = 1, then ends. Answer is 0
    If a2 = n2 then xres$ = Str(Y + 1): GoTo Done 'If n1 ^ (y + 1) = n2, then finish
    If f = 1000 then GoTo Done
    If a1 < n2 And n2 < a2 then 'If n1 ^ y is less than n2, but n1 ^ (y+1) is greater than n2, then add to final answer. In 3^x = 5, if 3^1 < 5 and 3^2 > 5, then 1 is added to the answer.
    xres$ = xres$ + Str(Y) ' Add to answer
    getval n1 ^ Y, n2 'get new number for testing
    n2 = trueanswer 'N2 is that new number
    GoTo go 'Restart process
    End If
    If a2 < n2 then Y = Y + 1: GoTo go 'If n1 ^ (y+1) < n2 then add 1 to y: restart process. In 3^x = 5, if 3^1 < 5, then add 1 to 1 and restart process
    If a1 > n2 then Y = Y - 1: GoTo go 'If n1 ^ y > n2 then subtract 1 from y: restart. In 3^x = 5, if 3 ^ 2 > 5, then subtract 1 from 2 and restart process
    Done:
    Text4.Text = " "
    xres$ = Left(xres$, 32) 'get 16 digits of xres$ - no exponential notation here!
    d = len(xres$) - 1 'get length minus 1
    If d = 1 then GoTo AlmostDone 'If there is only 1 digit, skip this process.
    xres$ = Val(xres$) / (10 ^ d) 'Divide xres$ by 10 ^ d. If it was 146514331, then now it is 1.46514331
    AlmostDone:
    Text4.Text = Val(xres$) 'Show xres$ briefly to the user
    xres$ = Val(Text4.Text) 'Update xres$ to the value of Text4.text, which displays 16 characters of xres$
    finished:
    If Val(Text1.Text) ^ 10 < Val(Text3.Text) then xres$ = Val(xres$) * 10 'If n1 ^ 10 < n2, then multiply xres$ * 10. If 3 ^ 10 < 60,000, then multiply the answer by 10
    If Val(Text1.Text) ^ 100 < Val(Text3.Text) then xres$ = Val(xres$) * 100 'Same thing, but with 100
    Text4.Text = xres$ 'Show final answer to user
    End Sub





    Then you need one more sub:



    public Sub getval(Number1, Number2)
    answer = Number2 / Number1
    trueanswer = answer ^ 10 'Getting new number. Formula is (n2 / n1) ^ 10
    End Sub







  11. #11
    Join Date
    Aug 1999
    Location
    US, Florida
    Posts
    817

    Re: tough VB question - see if you can answer it!

    So was my solution valid for real numbers or is it totally off topic? Logically it should be valid but I'm not sure.


  12. #12
    Join Date
    Jan 2000
    Posts
    20

    Re: tough VB question - see if you can answer it!

    Wow, that was a pretty neat way of looking at it!

    Your method is much more accurate than mine was, but I found a small problem. The way you got rid of the spaces with the Val function didn't work with your method of calculating the location of the decimal place...it ended up with the right answer but off by about -17 powers of 10 (for 3^x = 5). So this needed to be fixed. Also, this aside, playing with powers of two (being a programmer and all this was easiest for me to think with) I decided to see how high I could get and how much the accuracy degraded with larger numbers. For the most part it stayed much more accurate than my own method, going down to the thousandth digit. However, the last two lines you put in there weren't sufficient to error trap for power differences over 10^2 (this wasn't seen if the answer was a whole number..only decimals. 2^x=1024 worked but 2^x=1025 didn't)...I'd get the correct answer but it'd end up being off by several powers of 10.

    So what I did was pull out the spaces in the string with a Replace(xres, " ", ""). Then I started at the left of the string and used a DO WHILE LOOP to move the decimal place through the loop until a^x became larger than b.

    Some other things...good commenting. Try to make a habit of avoiding GoTo statements and make sure to Dim your variables (it's generally a good idea to use Option Explicit in your general declarations section)...this avoids creating spaghetti code. A little of the code was redundant (the getval sub required answer to be global and the result could've been calculated in the main procedure as a more of a literal) but the premise was sound and I thought extremely innovative.

    I rewrote your code to fix the bugs and make it a little easier to follow. Hope you don't mind. All in all I'd say you had a pretty **** good solution there!!!


    public Sub SolveForX()
    Dim xres as string
    Dim testExp as Double
    Dim count as Long
    Dim foundAnswer as Boolean
    Dim n1 as Double
    Dim n2 as Double
    Dim a1 as Double
    Dim a2 as Double

    ' initialize variables
    xres = " "
    testExp = 1
    n1 = Val(Text1.Text)
    n2 = Val(Text3.Text)
    foundAnswer = false
    count = 0

    ' take care of special cases
    If n2 = 0 then
    Text4.Text = "non-real answer"
    Exit Sub
    ElseIf n1 = 1 And n2 <> 1 then
    Text4.Text = "non-real answer"
    Exit Sub
    ElseIf n2 < 0 And n1 > 0 then
    Text4.Text = "non-real answer"
    Exit Sub
    ElseIf n2 = 1 And n1 <> 0 then
    Text4.Text = "0"
    ElseIf n1 = 0 then
    Text4.Text = "Infinity"
    Exit Sub
    End If

    ' determine string containing the numbers contained in our answer
    ' delimited by spaces
    Do While foundAnswer = false
    count = count + 1
    a1 = n1 ^ testExp
    a2 = n1 ^ (testExp + 1)
    If a1 = n2 then
    xres = Str(testExp)
    foundAnswer = true
    ElseIf a2 = n2 then
    xres = Str(testExp + 1)
    foundAnswer = true
    ElseIf count = 1000 then
    foundAnswer = true
    ElseIf a1 < n2 And n2 < a2 then
    xres = xres + Str(testExp)
    n2 = (n2 / n1 ^ testExp) ^ 10
    ElseIf a2 < n2 then
    testExp = testExp + 1
    ElseIf a1 > n2 then
    testExp = testExp - 1
    End If
    Loop

    ' take the spaces out of our string and shorten to 16 characters
    xres = Left(xres, 32)
    xres = Replace(xres, " ", "")

    ' determine where to put the decimal place
    count = 1
    Do While (CDbl(Text1.Text) ^ CDbl(Left(xres, count))) < CDbl(Text3.Text)
    count = count + 1
    Loop
    count = count - 1
    xres = Left(xres, count) & "." & Right(xres, len(xres) - count)

    ' display final answer
    Text4.Text = xres
    End Sub




    xxMariusxx


Posting Permissions

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





Click Here to Expand Forum to Full Width

Featured