I'm looking for some general feedback on my solution to Project Euler challenge 3
The prime factors of 13195 are 5, 7, 13 and 29. What is the largest prime factor of the number 600851475143 ?
let p3 () =
let rec primeFactors n i primes =
if i > n/2L then n::primes else
let quotient, remainder = Math.DivRem(n, i)
if remainder = 0L then primeFactors quotient 2L (i::primes)
else primeFactors n (i + 1L) primes
primeFactors 600851475143L 2L []
2 Answers 2
There's nothing really wrong with your code but here's a slight rewrite using pattern matching which is arguably a more functional style than if ... then ... else
. I also rearranged the args to eliminate the unnecessary n
parameter on the outer function.
let primeFactors =
let rec recPrimeFactors primes i = function
| n when 2L*i > n -> n::primes
| n -> match n % i with
| 0L -> recPrimeFactors (i::primes) 2L (n / i)
| _ -> recPrimeFactors primes (i + 1L) n
recPrimeFactors [] 2L
600851475143L |> primeFactors |> List.head |> printfn "%d"
-
\$\begingroup\$ F# has nested guards? Haskell really needs this :( \$\endgroup\$Carcigenicate– Carcigenicate2014年11月06日 12:29:03 +00:00Commented Nov 6, 2014 at 12:29
-
\$\begingroup\$ A match is just an expression in F#. I don't really know Haskell (on my list to learn!) but I think a match expression is a bit like a case in Haskell. \$\endgroup\$mattnewport– mattnewport2014年11月06日 16:12:48 +00:00Commented Nov 6, 2014 at 16:12
-
\$\begingroup\$ OK then, nevermind. Cases can be nested. And I recommend learning Haskell; it's a very neat language. \$\endgroup\$Carcigenicate– Carcigenicate2014年11月06日 19:34:01 +00:00Commented Nov 6, 2014 at 19:34
-
\$\begingroup\$ It could go till the
sqrt
ofn
\$\endgroup\$ntohl– ntohl2018年03月07日 15:35:29 +00:00Commented Mar 7, 2018 at 15:35
There is inefficiency due to a simple strategic blunder: if remainder = 0L
, then there is no reason to re-test all candidate factors starting from 2 again. You can just continue with primeFactors quotient i (i::primes)
.
The only possible even prime factor is 2, so you only need to test the odd numbers.
I'd also restructure the tests into one pattern match, because your nested if-else is a bit hard to read, especially the way you have placed the line breaks inconsistently.
let p3 =
let rec primeFactors (n: int64) (i: int64) primes =
let quotient, remainder = Math.DivRem(n, i)
match remainder with
| 0L -> primeFactors quotient i (i::primes)
| _ when i + i > n -> n::primes
| _ when i = 2L -> primeFactors n 3L primes
| _ -> primeFactors n (i + 2L) primes
primeFactors 600851475143L 2L []
-
\$\begingroup\$ For me it's failing for test case 8L>
[<TestCase(8L, [|2L; 2L; 2L|])>] | let prime_factors_of_number number expect = | primeFactors number |> should equal expect
\$\endgroup\$ntohl– ntohl2018年03月07日 15:20:58 +00:00Commented Mar 7, 2018 at 15:20
Explore related questions
See similar questions with these tags.
%
) \$\endgroup\$Math.DivRem
will return both the remainder and the quotient as a tuple in F#. \$\endgroup\$