My daughter had a question on her maths homework which was to write a value as a sum of 4 or fewer square numbers.
For example, x = 73 could be 36 + 36 + 1.
I came up with a really brute force algorithm that shows all combinations but when the values get above 1000 it becomes quite slow.
Is there a clever way of achieving this? Here is my algo in F#
let squares = [ 0.. 100 ] |> Seq.map (fun x -> x * x) |> Seq.toList
let rec calc total attempts squares accu results =
match (total, attempts, squares) with
| (0, _, _) -> accu :: results
| (_, 0, _) -> [] :: results
| (x, _, _) when x < 0 -> [] :: results
| (_, _, []) -> [] :: results
| total, attempts, squares ->
let filteredSquares = squares |> Seq.filter ((>=) total )
filteredSquares
|> Seq.collect(fun sq ->
calc (total - sq)
(attempts - 1)
(filteredSquares |> Seq.toList)
(sq :: accu)
results) |> Seq.toList
let res =
calc 8058 4 squares [] []
|> Seq.filter(fun lst -> lst <> [])
|> Seq.sortBy (fun lst -> lst.Length)
|> Seq.take 1
|> Seq.toList
Additional comments on better F# code would be appreciated as well. One thing I was wondering was whether I could make it lazy using sequences.
total - ((int) sq_root(total)) ^ 2
- ie, subtract the largest square available. I'm not sure if this always gets you the fewest number of elements, though. Your big problem is that at every step, you keep trying1
, or other numbers too small for the remaining number of 'attempts'. \$\endgroup\$seq.*
toarray.*
will give a pretty significant perf boost. \$\endgroup\$