6
\$\begingroup\$

Self explanatory, and all the functions contain descriptions of their specific problem statements.

Since this is project Euler, I would be particularly interested in any analysis of the functions' computational efficiency.

Option Explicit

Public Function ProjectEuler1() As Long

    '/ Problem statement: sum all multiples of 3 and 5 from 1 to 999

    Dim total As Long
    Dim i As Long

        For i = 1 To 999

            If i Mod 3 = 0 Or i Mod 5 = 0 Then

                total = total + i

            End If

        Next i

    ProjectEuler1 = total

End Function

Public Function ProjectEuler2() As Long

    '/ Problem statement: sum all even Fibonacci numbers below 4 Million

    Dim fiboA As Long, fiboB As Long, currentFibo As Long '/ alternating fibonacci numbers
    Dim bIsHigher As Boolean

        fiboA = 1
        fiboB = 1
        currentFibo = 2

    Dim total As Long

        Do While currentFibo < 4000000

            If currentFibo Mod 2 = 0 Then total = total + currentFibo

            If bIsHigher Then
                    fiboA = currentFibo
                Else
                    fiboB = currentFibo
            End If

            bIsHigher = Not bIsHigher

            currentFibo = fiboA + fiboB

        Loop

    ProjectEuler2 = total

End Function

Public Function ProjectEuler3(ByVal numToFactorise As Long) As Long

    '/ Problem statement: find the largest prime factor of <big number>
    '/ I decided to generalise it

    Dim i As Long
    Dim sqrtLimit As Long
    Dim partiallyFactorisedNum As Long
    Dim largestPrimeDivisor As Long

        sqrtLimit = Application.Floor(numToFactorise ^ 0.5, 1)
        partiallyFactorisedNum = numToFactorise

        i = 2
        If partiallyFactorisedNum Mod i = 0 Then
            largestPrimeDivisor = i
            partiallyFactorisedNum = KeepDividingWhileInt(partiallyFactorisedNum, i)
        End If

        i = 3
        Do While i <= sqrtLimit

            If isPrime(i) Then

                If partiallyFactorisedNum Mod i = 0 Then
                    largestPrimeDivisor = i
                    partiallyFactorisedNum = KeepDividingWhileInt(partiallyFactorisedNum, i)
                End If

            End If

            i = i + 2
            sqrtLimit = Application.Floor(partiallyFactorisedNum ^ 0.5, 1)
        Loop

    If partiallyFactorisedNum > largestPrimeDivisor Then largestPrimeDivisor = partiallyFactorisedNum
    ProjectEuler3 = largestPrimeDivisor

End Function

Public Function isPrime(ByVal numTocheck As Long)

    Dim i As Long
    Dim sqrtLimit As Long
        sqrtLimit = Application.Floor(numTocheck ^ 0.5, 1)

        isPrime = True

        If numTocheck Mod 2 = 0 And numTocheck <> 2 Then isPrime = False

        i = 3
        Do While i <= sqrtLimit And isPrime
            If numTocheck Mod i = 0 Then isPrime = False
            i = i + 2
        Loop

End Function

Public Function KeepDividingWhileInt(ByVal numToDivide As Long, ByVal divisor As Long) As Long

    Dim isDivisible As Boolean

        isDivisible = True
        Do While isDivisible
            If numToDivide Mod divisor = 0 Then
                    numToDivide = numToDivide / divisor
                Else
                    isDivisible = False
            End If
        Loop

    KeepDividingWhileInt = numToDivide

End Function
\$\endgroup\$
4
\$\begingroup\$

While quickly running over your code, my eyes stopped on your function KeepDividingWhileInt.

It seemed over-complicated.

Let's write what it does:

  1. Creates isDivisible
  2. Sets isDivisible to true
  3. Loops while isDividible is true:
    1. If numToDivide Mod divisor is 0
      1. Divides numToDivide by divisor
    2. Else
      1. Sets isDivisible to false
  4. Returns numToDivide

Well, there is a subtle bug on step 2: You assume that the number is divisible by divisor, which may not be true (e.g.: KeepDividingWhileInt(3,2)).

Also, the whole code could be extremely simplified, by moving the condition inside that if into the while.

Also, you could try to use While ... Wend instead of Do While ... Loop, as suggested by @Mat's Mug. This point actually applies everywhere on your code.


The final result:

Public Function KeepDividingWhileInt(ByVal numToDivide As Long, ByVal divisor As Long) As Long

    While numToDivide Mod divisor = 0
        numToDivide = numToDivide / divisor
    Wend

    KeepDividingWhileInt = numToDivide

End Function

Oh my! The function is so simple that you can read it in a single sentence:

Divide numToDivide by divisor while the reminder is 0, returning the final result.

So simple, right?

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Nice answer, except I don't know what Do While...Loop is doing in the language - it's just one of the several redundant-yet-different-because-why-not ways of looping available in VBA. A simpler variant would be While...Wend. \$\endgroup\$ Dec 4 '15 at 2:43
  • \$\begingroup\$ @Mat'sMug Thanks a lot for that tip! Has been a few years since I've written any serous VB code. It is indeed simpler and to the point. It really looks a lot more natural. Once again, thank you. \$\endgroup\$ Dec 4 '15 at 2:49
  • \$\begingroup\$ Just a case of Do While ... Loop being what I initially encountered when learning the language. \$\endgroup\$
    – Kaz
    Dec 4 '15 at 4:00
4
\$\begingroup\$

Euler2

bIsHigher is a confusing variable name. It has the disadvantage of resembling Hungarian notation, so those exposed to that may read it as booleanIsHigher which makes no sense.

Rather than trying to find a better name, you can actually factor out the need for the variable. You get the same result from the following:

    Do While currentFibo < 4000000
        If currentFibo Mod 2 = 0 Then total = total + currentFibo
        fiboA = fiboB
        fiboB = currentFibo
        currentFibo = fiboA + fiboB
    Loop

which I would argue is closer to how the fibonacci sequence is actually derived as fiboA will always be (n-2) and fiboB will always be (n-1) instead of repeatedly trading places.

\$\endgroup\$
1
  • \$\begingroup\$ Awesome, that's exactly the logic I was grasping at but couldn't quite articulate :) \$\endgroup\$
    – Kaz
    Dec 4 '15 at 14:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.