Author Archive

Euler Problem 83

Euler Problem 83 asks:

NOTE: This problem is a significantly more challenging version of Problem 81.

In the 5 by 5 matrix below, the minimal path sum from the top left to the bottom right, by moving left, right, up, and down, is indicated in red and is equal to 2297.

131 673 234 103 18
201 96  342 965 150
630 803 746 422 111
537 699 497 121 956
805 732 524 37  331

Find the minimal path sum, in matrix.txt (right click and 'Save Link/Target As...'), a 31K text file containing a 80 by 80 matrix, from the top left to the bottom right by moving left, right, up, and down.

In the Problem 123 thread Doug Jenkins provided a spreadsheet solution for Problem 83, as well as suggesting an alternate method to solve the problem by padding the matrix. He thereby relieved a huge mental block of mine, but it's in the wrong thread. So I started this one.

Padding the matrix has its advantage. It allows you to use a common relationship in the area of interest without having to worry about variable subscripts being out of range because you'd otherwise reference a row or column that you haven't dimensioned (akin to trying to reference Row(0) on a spreadsheet.) There's some overhead to do this, but it saves special cases at the corners and borders. Doug recommended using 1000000, and that's as good a choice as any. With that in mind, the above matrix comes to look like this:

1000000 1000000 1000000 1000000 1000000 1000000 1000000
1000000   131     673     234     103     18    1000000
1000000   201     96      342     965     150   1000000
1000000   630     803     746     422     111   1000000
1000000   537     699     497     121     956   1000000
1000000   805     732     524     37      331   1000000
1000000 1000000 1000000 1000000 1000000 1000000 1000000

Since a picture = 1 kiloword, you can see how we have slop all the way around for subscripts, with the added advantage that if you make the matrix zero-based, the action starts at Row(1), Column(1). My mind likes it better that way. I used this same padding trick for Problem 67, where you can turn a triangle into a square. It really simplifies the code. With all that for background, here is my code that turns Doug's spreadsheet solution into VBA. It runs in about 3/10's of a second.

Sub Problem_083()
Dim Matrix(0 To 81) As Variant
   Dim Cell(0 To 81, 0 To 81) As Long
   Dim R As Long, C As Long
   Dim Min     As Long
   Dim Answer As Long, T As Single
   Dim TEMP1 As Long, TEMP2 As Long
   Dim NumRows As Long, NumCols As Long
   Dim IsTest As Boolean, i As Long
   Const text  As String = "D:\Downloads\Euler\matrix.txt"
 
   T = Timer
 
   R = 1
   Open text For Input As #1   '80 lines, comma delimited
   Do While Not EOF(1)
      Line Input #1, Matrix(R)   'fills rows 1 to 80; 0 and 81 come later
      R = R + 1
   Loop
   Close #1
 
   IsTest = False
   If IsTest Then
      NumRows = 6
      NumCols = 6
      Matrix(1) = "131,673,234,103,18"
      Matrix(2) = "201,96,342,965,150"
      Matrix(3) = "630,803,746,422,111"
      Matrix(4) = "537,699,497,121,956"
      Matrix(5) = "805,732,524,37,331"
   Else
      NumRows = 81
      NumCols = 81
   End If
 
   For C = 1 To NumCols - 1
      Matrix(0) = Matrix(0) & "1000000 "   
      'adds top padding @(0), sets up TRIM()
   Next C
   Matrix(0) = Replace(Trim(Matrix(0)), " ", ",")   'makes it comma-delimited
   Matrix(NumRows) = Matrix(0)   ' adds bottom padding @(NumRows)
 
   For R = 0 To NumRows
      Matrix(R) = "1000000," & Matrix(R) & ",1000000"   
      ' pads all rows left and right
      Matrix(R) = Split(Matrix(R), ",")   
      'makes a zero-based NumRows X NumCols matrix
   Next R
 
   For R = 0 To NumRows
      For C = 0 To NumCols
         Cell(R, C) = CLng(Matrix(R)(C))
         If C GT 0 Then Cell(R, C) = Cell(R, C) + Cell(R, C - 1)   
         ' seeds the Cell array
      Next C
   Next R
 
   Do
      TEMP1 = Cell(NumRows - 1, NumCols - 1)   
      'start value of unpadded LR corner
      i = i + 1   'counts iterations
      For R = 1 To NumRows - 1   'inside the padding
         For C = 1 To NumCols - 1   'inside the padding
            If R = 1 And C = 1 Then   'reset Cell(1,1) from above
               Cell(R, C) = CLng(Matrix(R)(C))
            Else   'do the hard work
               Min = Application.WorksheetFunction.Min(Cell(R + 1, C), Cell(R - 1, C), _
                                                       Cell(R, C + 1), Cell(R, C - 1))
               Cell(R, C) = CLng(Matrix(R)(C)) + Min
            End If
         Next C
      Next R
      TEMP2 = Cell(NumRows - 1, NumCols - 1)   
      'finish value of unpadded LR corner
      If i GT NumRows * NumCols Then Exit Do   'escape clause
   Loop Until TEMP1 = TEMP2   'stable when start = finish
 
   Answer = Cell(NumRows - 1, NumCols - 1)
 
   Debug.Print Answer; "  Time:"; Timer - T, i
 
End Sub

Doug mentions seeding the Cell array. This makes a huge difference. It goes through the Do-Loop only 5 times. The answer is known after 4 loops, but it takes 5 for the starting TEMP1 to know it. I couldn't figure out how to avoid that without apriori knowledge of the Answer, which is in the bottom right cell before the padding.

Playing with the spreadsheet solution, I made a third matrix of the array by "pasting special" a copy when all is stable. Then with conditional formatting comparing the two, I could see how the data flows and settles as I stepped through it. It starts from the upper left in kind of a maple-leaf pattern: Strong down the middle, with a spike above and below, and then a weak spike down the left side and the top edge. It takes 11 reps for everything to stabilize.

So, all in all, this is my VBA for Doug's concept. Stephen B and Josh G have other approaches, and hopefully, they'll share. This code is the combination of two half-good ideas I had. Maybe Doug will chime in, too. He's the one who gave me the clue about the whole approach.

The usual angle bracket corrections are in the code. It's interesting that it's Cell(R,C) but Matrix (R)(C) for the syntax.

...mrt

Euler Problem 81

Euler Problem 81 asks:

In the 5 by 5 matrix below, the minimal path Min from the top left to the bottom right,
by only moving to the right and down, is indicated in red and is equal to 2427.

131   673   234   103   18
201   96    342   965   150
630   803   746   422   111
537   699   497   121   956
805   732   524   37    331

Find the minimal path Min, in matrix.txt (right click and 'Save Link/Target As...'),
a 31K text file containing a 80 by 80 matrix, from the top left to the bottom right
by only moving right and down.

This is very similar to problems 18 and 67, except that they ask for the maximum path to the bottom, not the minimum path to the lower right corner. #81 can absolutely be done in a spreadsheet, as Tushar shows here for numbers 18 and 67. I like to solve them in VBA. The difference between this problem and #67 is that we have to get to a specific matrix cell, and by the rules, if we end up at the right edge, we can only go down, and if we end up at the bottom, we can only go right. In other words, on the right, progressively sum upwards from the lower right corner, and on the bottom, progressively sum leftwards from that same corner. The goal is to abstract the problem so the choice at matrix cell(0)(0) is the minimum of all paths to cell(0)(0). The answer will be the sum of cell(0)(0) and that minimum. Here is my code that does this. It runs in a blink (less that a tenth of a second.)

Sub Problem_081()
   Dim Cell(0 To 79) As Variant
   Dim R As Long, C As Long
   Dim NumRows As Long, NumCols As Long
   Dim Min As Long, IsTest As Boolean
   Dim Answer As Long, T As Single
   Const text  As String = "D:\Downloads\Euler\matrix.txt"
 
   T = Timer
 
   R = 0
   Open text For Input As #1   '80 lines, comma delimited
   Do While Not EOF(1)
      Line Input #1, Cell(R)
      R = R + 1
   Loop
   Close #1
 
   IsTest = False
   If IsTest Then
      NumRows = 4
      NumCols = 4
      Cell(0) = "131,673,234,103,18"
      Cell(1) = "201,96,342,965,150"
      Cell(2) = "630,803,746,422,111"
      Cell(3) = "537,699,497,121,956"
      Cell(4) = "805,732,524,37,331"
   Else
      NumRows = 79
      NumCols = 79
   End If
 
   For R = 0 To NumRows
      Cell(R) = Split(Cell(R), ",") ' making a NumRows X NumCols matrix
   Next R
 
   For C = NumCols - 1 To 0 Step -1 'rolling up right and bottom edges
      R = C
      Cell(NumRows)(C) = CLng(Cell(NumRows)(C)) + CLng(Cell(NumRows)(C + 1))
      Cell(R)(NumCols) = CLng(Cell(R)(NumCols)) + CLng(Cell(R + 1)(NumCols))
   Next C
 
   For R = NumRows - 1 To 0 Step -1 'rolling up the minimums
      For C = NumCols - 1 To 0 Step -1
         Min = Application.WorksheetFunction.Min(CLng(Cell(R + 1)(C)), CLng(Cell(R)(C + 1)))
         Cell(R)(C) = CLng(Cell(R)(C)) + Min
      Next C
   Next R
 
   Answer = Cell(0)(0)
 
   Debug.Print Answer; "  Time:"; Timer - T
 
End Sub

Having done #67, this was very straight forward. Problem #83, which uses the same matrix, is similar but harder. It's having its way with me. Here is #83:

NOTE: This problem is a significantly more challenging version of Problem 81.

In the 5 by 5 matrix below, the minimal path sum from the top left to the bottom right, by moving left, right, up, and down, is indicated in red and is equal to 2297.

131 673 234 103 18
201 96  342 965 150
630 803 746 422 111
537 699 497 121 956
805 732 524 37  331

Find the minimal path sum, in matrix.txt (right click and 'Save Link/Target As...'), a 31K text file containing a 80 by 80 matrix, from the top left to the bottom right by moving left, right, up, and down.

Note the NOTE, the rules change, and the snaking path. It takes 12 moves, whereas #81 only takes 8. The minimum on the left depends on the minimum on the right. As Doug J has said, 'tis circular, and I've not grasped it yet. The various code I've written does the example, but either takes more than 6400 moves (visiting every cell several times) or ends up in an endless loop in the lower right corner of matrix.txt.

...mrt

Euler Problem 123

Euler Problem 123 asks:

Let p(n) be the nth prime: 2, 3, 5, 7, 11, ..., and let r be the remainder when (p(n)-1)n + (p(n)+1)n is divided by p(n)2.

For example, when n = 3, p(3) = 5, and 43 + 63 = 280. 280 mod 25 = 5.

The least value of n for which the remainder first exceeds 109 is 7037.

Find the least value of n for which the remainder first exceeds 1010.

This is really the same problem as Euler 120 (posted last week) with p(n) taking the part of a. We are looking for 2*p(n)*n greater than 1010. I built a collection of the first 7037 primes, and then added primes one-by-one to the collection until the remainder was large enough, with the caveat that even n gives a remainder of 2, so we need an odd n.

All prime numbers beyond 3 lie left or right of an even multiple of six so a prime-checking routine need only check whether or not n mod 6 = 1 or 5 has even divisors, and even then, only up to the square root of n.

Here is the code that does that. The routine runs in about 2.5 seconds.

Sub Problem_123()
   Dim Answer As Long, T As Single
   Dim n As Long, i As Long, R As Double
   Dim p       As New Collection
 
   T = Timer
   
   p.Add Item:=2, Key:="2"   '1st Prime
   n = 1
   i = 3
 
   Do
      If IsPrime3(i) Then
         p.Add Item:=i, Key:=CStr(i)
         n = n + 1
      End If
      i = i + 2
   Loop Until n = 7037   'Primes in p
 
   Do
      If n Mod 2 = 0 Then
         R = 2
      Else
         R = 2 * p.Item(n) * n
      End If
      If R GT 10 ^ 10 Then
         Answer = n
         Exit Do
      End If
      i = p.Item(n) + 2
      Do Until IsPrime3(i)
         i = i + 2
      Loop
      n = n + 1
      p.Add Item:=i, Key:=CStr(i)
   Loop
 
   Debug.Print Answer; "  Time:"; Timer - T, p.Item(Answer), R
End Sub
 
Function IsPrime3(Num As Variant) As Boolean
   Dim i       As Long
   
   If Num  != Int(Num) Then
      Exit Function                                      'IsPrime = False
   Else
      Num = CDec(Num)
   End If
   If Num LT 2 Then Exit Function                         'IsPrime = False
   If Num = 2 Then
      IsPrime3 = True
      Exit Function
   End If
   If Num = 3 Then
      IsPrime3 = True
      Exit Function
   End If
   
   Select Case Num Mod 6
      Case 1, 5
         For i = 3 To Sqr(Num) Step 2
            If Num Mod i = 0 Then Exit Function          'IsPrime = False
         Next i
      Case Else
         Exit Function                                   'IsPrime = False
   End Select
   
   IsPrime3 = True
End Function

Dick has more on prime numbers here. The usual angle bracket corrections are in the above.

...mrt

Euler Problem 120

Euler Problem 120 asks:

Let r be the remainder when (a-1)n + (a+1)n is divided by a2.

For example, if a = 7 and n = 3, then r = 42:

  • 63 + 83 = 728
  • 728 mod 49 = 42

And as n varies, so too will r, but for a = 7 it turns out that r_max = 42.

For 3 <= a <= 1000, find Sum(r_max).

All my brute force attempts to solve this problem overflowed whatever variable type I used, from longs through currency through decimal variants through doubles. Another approach was needed, and Isacc Newton had it figured out in his binomial theorem, which gives the expansion of (x+y)n.

Four examples:

  1. (a+1)2 = a2+2a+1
  2. (a-1)2 = a2-2a+1
  3. (a+1)3 = a3+3a2+3a+1
  4. (a-1)3 = a3-3a2+3a-1

Adding equations (1) and (2) together, which have n = 2, gives

  1. (a+1)n + (a-1)n= 2an+2

This is true for all even n.

Adding equations (3) and (4) together, which have n = 3, gives

  1. (a+1)3 + (a-1)3 = 2a3 + 6a
  2. 2a3 + 6a = 2an + 2an

This is true for all odd n.

Within equations (5) and (7), 2an is evenly divisible by a2. The remainders are thus 2 for even n and 2an for odd n respectively. How big can n grow? Such that 2an stays less than a2, or 2n is less than a. Otherwise, a2 divides one more time.

Looping through a = 3 to a = 1000 and summing 2an provides the answer. This simple code does that. It runs in a blink.

Sub Problem_120()
   Dim Answer As Long, T As Single
   Dim a As Long, n As Long
 
   T = Timer
 
   For a = 3 To 1000
      n = 2
      While 2 * n LT a
         n = n + 1
      Wend
      n = n - 1 ' went 1 too far
      Answer = Answer + 2 * a * n
   Next a
 
   Debug.Print Answer; "  Time:"; Timer - T
End Sub

The usual angle bracket adjustment is in the code. Next week, I'll put up Problem 123, which uses this same approach, but with a as prime numbers.

...mrt

Euler Problem 206

Euler Problem 206 asks:

Find the unique positive integer whose square has the form 1_2_3_4_5_6_7_8_9_0,
where each “_” is a single digit.

First thing to note is that Euler wants the integer, not the square that conforms to the pattern. That failure to RTFQ costs me a couple of hours as I kept trying to check in a 19-digit number when the answer is a 10-digit number. I saw immediately how to build the numbers that fit the pattern, and wrote this code:

Sub Problem_206A()
 
   Dim Answer As Double, T As Single
   Dim Answer_Sqrd As String
   Dim i As Long, j As Long
   Dim TEMP    As String
   
   T = Timer
 
   For j = 999999999 To 0 Step -1
      TEMP = Format(j, "000000000")
      For i = 1 To 9
         Answer_Sqrd = Answer_Sqrd &amp; i &amp; Mid(TEMP, i, 1)   ' "1" &amp; "9" &amp; "2" &amp; "9" &amp; "3" ...
      Next i
      Answer_Sqrd = Answer_Sqrd &amp; "0"   ' "192939495969798999" &amp; "0"
      Answer = Sqr(CDbl(Answer_Sqrd))
      If Answer = Int(Answer) Then
         Debug.Print Answer; "  Time:"; Timer - T
         End
      End If
      Answer_Sqrd = ""
   Next j
 
End Sub

It runs in a very uncool 21+ seconds on my home machine. It builds the number squared by successively concatenating a counter with the digit that lies at that counter within a string variable that counts down from 999,999,999; and then it looks for integer square roots. I counted down instead of up from Euler experience--Euler's answers tend to be at the high end of anticipated values. Neat idea, not so neat for performance, and I was surprised that doubles were accurate enough to solve a problem requiring 19 significant figures. Euler often asks for computations beyond their accuracy. The answer is the sole integer square root. End when that number is found.

So I rewrote the code from the square root point of view. The largest possible number is when the underscores are all nines, and the smallest when the underscores are all zeros. Compute those square roots, again as doubles, and check from the top down for the sole square between those numbers squared that conforms to the pattern. Doing it as a decimal variant provides all the precision required.

One thing I learned from that is that CDec() is not commutative. There is a difference between Cdec(A*A) and CDec(A)*CDec(A)

Sub Problem_206B()
   Dim Answer As Double, T As Single
   Dim Answer_Sqrd As Variant, SAT As Boolean
   Dim Min As Double, Max As Double
   Dim i As Long, j As Long
     
   T = Timer
 
   Min = Int(Sqr(CDbl("1020304050607080900")))   'smallest acceptable number
   Max = Int(Sqr(CDbl("1929394959697989990")))   'largest acceptable number
 
   For Answer = Max To Min Step -1
      Answer_Sqrd = CDec(Answer) * CDec(Answer)   'Cdec(Answer*Answer) doesn't work
      j = 1
      For i = 1 To 19 Step 2   'checking every other digit
         If i = 19 Then j = 0
         If Mid(Answer_Sqrd, i, 1) = CStr(j) Then   'pattern matches
            SAT = True
            j = j + 1
         Else   'pattern broken
            SAT = False
            Exit For
         End If
      Next i
      If SAT Then   'every other digit is right wrt 1 to 9, 9 to 0
         Debug.Print Answer; "  Time:"; Timer - T; Answer_Sqrd
         End
      End If
   Next Answer
 
End Sub

This code ran in .04 seconds. Now that was cool. I should have thought harder back at the beginning. The first was way checks about 2.77 billion numbers for every one the second way checks.

...mrt

Euler Problem 100

Euler Problem 100 asks:

If a box contains twenty-one coloured discs, composed of fifteen blue discs and six red discs, and two discs were taken at random, it can be seen that the probability of taking two blue discs, P(BB) = (15/21)×(14/20) = 1/2.

The next such arrangement, for which there is exactly 50% chance of taking two blue discs at random, is a box containing eighty-five blue discs and thirty-five red discs.

By finding the first arrangement to contain over 10^12 = 1,000,000,000,000 discs in total, determine the number of blue discs that the box would contain.

Using R for #Red, B for #Blue and T for total (R+B), the basic equation here is:

          B * B-1 = 0.5
          T   T-1

Since they can neither equal each other nor both be the square root of 0.5 (or 0.707106781...) we want B/T to be a skosh over the square root of 0.5, and (B-1/T-1) a skosh under. In other words, the answer will be the first integer B greater than Sqr(0.5)*10^12.

If we rearrange the equation, we get

  • 2B^2 - 2B = T^2 - T

A single double quadratic equation with two unknowns can be morphed into a Pell equation with either no solutions, or an infinite number of them. Again, Euler wants the first solution with total discs T greater than 10^12. The first hit on a Google search for probability and Pell (here) returns an article exactly on point, with the examples for P=0.5 worked out in the first 11 cases. Dr. Sasser must have written it in a hurry, because it can be confusing. He uses two variables y, and they mean different things. I'm going to try to sort that out. There are also a few typos, but you can figure them out from context. The above equation does morph, as Dr. Sasser shows how, into Pell-form x^2 - Dy^2 = C as

  • x^2 - 2y^2 = -1

with y = 2B-1. B, the number of Blue discs, is then (y+1)/2. Once we have the y solutions, we have the B solutions. The solutions for this Pell equation are x + y*Sqr(2) = (1 + sqr(2))^n, for integer n.

Flashing back to algebra, the product of (aw + bz)(cw + dz) is:

  • acw^2 + (ad + cb)wz + dbz^2

If c = 1, w = 1, d = 1, z = Sqr(2), and z^2 = 2 corresponding to solution 1*1 + 1*Sqr(2) then rearranging the above gives:

  • a + 2b + (a+b)*Sqr(2)

This is of the form x + y*Sqr(2) with x = a + 2b and y = a + b. This gives us a method to take (1 + Sqr(2)) to any integer power n. Recall that we're looking for the y = a + b that gives B = (y+1)/2 greater than Sqr(0.5)*10^12.

This is the code that does that. It runs in a blink.

Sub Problem_100()
 
   Dim A As Double, B As Double
   Dim TEMP    As Double
   Dim Answer As Double, T As Single
 
   T = Timer
 
   A = 1
   B = 1
 
   Do Until Answer&gt; Sqr(0.5) * 10 ^ 12
      TEMP = A
      A = A + 2 * B
      B = TEMP + B
      Answer = (B + 1) / 2   'the number of Blue discs
   Loop
 
   Debug.Print Answer; "  Time:"; Timer - T
 
End Sub

The first time I attempted this problem I incremented B or T as necessary to get just above Sqr(0.5)*10^12. Don't go that way. Floating arithmetic errors give "false positives" for that approach.

There usual angle bracket corrections are in the code.

...mrt

Euler Problem 80

Euler Problem 80 asks:

It is well known that if the square root of a natural number is not an integer, then it is irrational. The decimal expansion of such square roots is infinite without any repeating pattern at all.

The square root of two is 1.41421356237309504880..., and the digital sum of the first one hundred decimal digits is 475.

For the first one hundred natural numbers, find the total of the digital sums of the first one hundred decimal digits for all the irrational square roots.

The first thing I wanted to know to solve this problem was what the square root of 2 was to 100 places. I wasn't getting the right sum. I found it here.

1.4142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727
which is
979291172255376750081652821559640563801792871084643
divided by
692463428657900307544320387485120303323686251920582

When I summed the decimal digits, I got 481, not 475. But if I drop the right-hand 7 and add the left-of-the-decimal 1, that's a down-spot of 6. Euler wants the sum of the first 100 digits, not the first 100 after the decimal.

To make the calulation I wanted an algorithm that computed square roots as strings. I found that here. The author says of his Japanese method

It is an amusing exercise to program a computer to do this algorithm, at least if n is an integer, and because only integer additions and subtractions are used, there are no errors due to floating-point arithmetic. On the other hand, it is necessary to use more complicated storage techniques (strings or arrays) to store the values of a and b as they get larger and larger, and the algorithm will get slower and slower at producing successive digits.

Amusing? I'm not so sure. To implement the Japanese method in strings, it took me a while to find the equivalent of "a greater than or equal to b" where a and b are string representations of numbers. I ended up with:

  • If the length of a is greater than the length of b, then a is greater than b
  • If the length of a is less than the length of b, then a is less than b
  • If the length of a equals the length of b then do a string-comparison

Don't know why that took as long as it did.

I also needed an algorithm for subtracting strings. Rather than write a "borrowing" routine, I used the "method of nines'-complement" found here on Wikipedia.

When it was all done, the code runs in 11 seconds.

Sub Problem_080()
   Dim i As Long, j As Long
   Dim TEMP    As String
   Dim Answer As Long, T As Single
 
   T = Timer
 
   For i = 2 To 99   ' Square Roots of 1, 100 not needed
      If Sqr(i) != Int(Sqr(i)) Then
         TEMP = JSqrRoot(i, 110, False) 'a little padding for convergence
         For j = 1 To 100
            Answer = Answer + CLng(Mid(TEMP, j, 1))
         Next j
      End If
   Next i
 
   Debug.Print Answer; "  Time:"; Timer - T
End Sub
 
Function JSqrRoot(n As Long, NumDigits As Long, Optional Dec) As String
'http://www.shef.ac.uk/~pm1afj/maths/jarvisspec02.pdf
'Long Integer implementation only
'Dec = True returns with decimal point
 
   Dim strA As String, strB As String
   Dim SAT As Boolean, i As Long, Num As Double
 
   strA = CStr(5 * n)
   strB = "5"
   If IsMissing(Dec) Then Dec = True
 
   Num = Sqr(n)
   If Int(Num) = Num Then
      JSqrRoot = CStr(Num)
      Exit Function
   End If
 
   Do While Len(strB)  LT NumDigits
      If Len(strA) GT Len(strB) then 'a GT b
         SAT = True
      ElseIf Len(strA) LT Len(strB) Then ' b GT a
         SAT = False
      ElseIf strA GTE strB Then   'do string comparison for equal lengths
         SAT = True
      Else
         SAT = False
      End If
 
      If SAT Then
         strA = SubAsStrings(strA, strB)
         strB = AddAsStrings(strB, "10")
      Else
         strA = strA &amp; "00"
         strB = Left(strB, Len(strB) - 1) &amp; "05"
      End If
   Loop
 
   JSqrRoot = strB
 
   If Dec Then
      i = InStr(1, CStr(Num), ".")
      JSqrRoot = Left(JSqrRoot, i - 1) &amp; "." &amp; Right(JSqrRoot, Len(JSqrRoot) - i + 1)
   End If
 
End Function
 
Function SubAsStrings(ByVal Term1 As String, ByVal Term2 As String) As String
'Uses "method of nines'-complement"
'http://en.wikipedia.org/wiki/Method_of_complements
'Term1 GT Term2 GT 0...ie. postive integers only
 
   Dim Difference As String
   Dim Complement As String
   Dim Sum     As String
   Dim c       As Long
 
   If Len(Term1) GT Len(Term2) Then
      Term2 = String(Len(Term1) - Len(Term2), "0") &amp; Term2
   End If
 
   For c = 1 To Len(Term2)
      Complement = Complement &amp; (9 - CLng(Mid(Term2, c, 1)))
   Next c
 
   Sum = AddAsStrings(Term1, Complement)
   Sum = AddAsStrings(Sum, "1")
   Difference = Right(Sum, Len(Sum) - 1)
 
   While Left(Difference, 1) = "0"
      Difference = Right(Difference, Len(Difference) - 1)
   Wend
 
   SubAsStrings = Difference
 
End Function

The usual angle bracket adjustments have been made above. The AddAsStrings function has been put up before. Dr Jervis' Japanese method requires you to independently place the decimal point in the square root. I made that optional, and set it to false to accomodate Euler's way of counting.

...mrt

Euler Problem 104

Euler Problem 104 asks:

The Fibonacci sequence is defined by the recurrence relation:

F(n) = F(n-1) + F(n-2), where F(1) = 1 and F(2) = 1.

It turns out that F(541), which contains 113 digits, is the first Fibonacci number for which the last nine digits are 1-9 pandigital (contain all the digits 1 to 9, but not necessarily in order). And F(2749), which contains 575 digits, is the first Fibonacci number for which the first nine digits are 1-9 pandigital.

Given that F(k) is the first Fibonacci number for which the first nine digits AND the last nine digits are 1-9 pandigital, find k.

The title really deserves an exclamation point. As discussed in Large Number Arithmetic I've been trying to solve this problem since before January. This problem breaks my little AddAsStrings function, and for reasons you'll see, overflows Tushar's LargeAdd function. We're looking for a big number, longer in length then Excel can handle as a string. And I was stuck. Doug, in Large Number Arithmetic, showed how to keep track of the back end of a variable. How to keep track of the front end had me stumped until it came to me last Sunday morning when I woke up (any one else code in their sleep?): Separate variables. Use a long to track the backend and a double to track the front end. An immediate trouble with that is that doubles only work to E+308, and one of the test cases, Fib(2749) is E+575. Solution there was to knock the Fibonacci values down by 10^2 when ever they get over 10^9, and keep track of the exponent in a separate variable. Here is my code, with a helper function Ends() that tests the ends for "pan-digitality." It's a little faster than most I've seen, since it breaks as soon as there is a failure, rather that testing on, and I only test the left side when the right side is pandigital. The code runs in six-tenths of a second:

Sub Problem_104()
Dim i_lng As Long, j_lng As Long, k_lng As Long
   Dim i_dbl As Double, j_dbl As Double, k_dbl As Double
   Dim k_str   As String
   Dim LeftSAT As Boolean
   Dim RightSAT As Boolean
   Dim Answer As Long, T As Single
   Dim exp     As Long
   Dim Fib_ans As String
   Dim PlusMark As Long
 
   T = Timer
 
   i_lng = 1
   j_lng = 1
   i_dbl = 1
   j_dbl = 1
   Answer = 2
 
   Do
      Answer = Answer + 1
 
      k_lng = i_lng + j_lng
      k_str = Right(k_lng, 9)
      i_lng = j_lng
      j_lng = CLng(k_str)
 
      k_dbl = i_dbl + j_dbl
      i_dbl = j_dbl
      j_dbl = k_dbl
 
      If k_dbl GT 1000000000 Then ' knock it down by 10^2
         i_dbl = i_dbl / 100
         j_dbl = j_dbl / 100
         exp = exp + 2 ' tracking the exponent
      End If
 
      LeftSAT = False
      RightSAT = False
      RightSAT = Ends(k_str)
      If RightSAT Then
         LeftSAT = Ends(CStr(k_dbl))
      End If
   Loop Until LeftSAT = True And RightSAT = True
 
   Fib_ans = Format(k_dbl, "0.00000000000000E+")
   PlusMark = InStr(1, Fib_ans, "+")
   Fib_ans = Left(Fib_ans, PlusMark) &amp; (CLng(Right(Fib_ans, Len(Fib_ans) - PlusMark)) + exp)
 
   Debug.Print Answer, Left(k_dbl, 9), k_str
   Debug.Print "Fib(" &amp; Answer &amp; ") = "; Fib_ans; "  Time:"; Timer - T;
 
  End Sub
 
Function Ends(k_str As String) As Boolean
   Dim E       As Long
   Dim E_str   As String
   E_str = Left(k_str, 9)
   For E = 1 To 9
      If InStr(1, E_str, CStr(E)) Then
         Ends = True
      Else
         Ends = False
         Exit For
      End If
   Next E
End Function

Compared to the multi-precision answer given by the Foxes Team addin mentioned in Large Number Arithemtic, this answer drifts off in the 13 decimal place. The xnumbers add-in has been helpful. It did the Euler totient problems, for instance. It's been a useful addition to the toolbox, and since any improvement has stopped, I'd recommend grabbing before it disappears.

...mrt