Reputation: 284
I'm trying to decompose the square of a number into a sum of squares: In Ruby:
def decompose(n)
def decompose_aux(nb, rac)
return [] if (nb == 0)
i, l = rac, nil
while (i >= Math.sqrt(nb / 2.0).to_i + 1) do
diff = nb - i * i
rac = Math.sqrt(diff).to_i
l = decompose_aux(diff, rac);
if l != nil then
l << i; return l
end
i -= 1
end
l
end
l = decompose_aux(n * n, Math.sqrt(n * n - 1).to_i);
if l then return l else return nil end
end
In Clojure:
(defn decompAux [nb rac]
(if (= 0 nb)
()
(loop [i rac l nil]
(let [diff (- nb (* i i)) rac (int (Math/sqrt diff)) l (decompAux diff rac)]
(if (not= l nil)
(conj l i)
(if (>= i 1)
(recur (dec i) nil)
nil))))))
(defn decompose [n]
(let [l (decompAux (* n n) (- n 1))]
(if l
(reverse l)
nil)))
In Ruby I get
decompose(7654321) --> [6, 10, 69, 3912, 7654320].
In Clojure:
(decompose 7654321) --> (1 1 2 3 11 69 3912 7654320)
The Clojure one is a tracing of the Ruby one but the results are different. Where is the default?
Upvotes: 3
Views: 158
Reputation: 84361
The two algorithms are not actually the same.
The difference is that in Clojure you have a different loop condition (as pointed out by Tassilo Horn in the comments) and you check it at a different point in your computation. Consequently, the Clojure version can basically add as many 1s as necessary on to any candidate result which is not "complete" (that is, where the squares of the numbers add up to a number less than your target).
Actually this phenomenon is not limited to 1s – decompose_aux(8, 2)
returns nil
, whereas (decompAux 8 2)
returns (2 2)
– but the fact that you can always count down to the point where i
is 1
means that decompAux
is guaranteed to return an answer for any positive nb
and rac
inputs (an answer which might involve many 1s).
Here's a fairly direct translation of your Ruby to Clojure:
(defn decompose [n]
(letfn [(decompose-aux [nb rac]
(if (zero? nb)
[]
(loop [i rac l nil]
(if (>= i (inc (int (Math/sqrt (/ nb 2.0)))))
(let [diff (- nb (* i i))
rac (int (Math/sqrt diff))
l (decompose-aux diff rac)]
(if (some? l)
(conj l i)
(recur (dec i) l)))
l))))] ; unnecessary line
(decomp-aux (* n n) (int (Math/sqrt (dec (* n n)))))))
It matches the Ruby version on your example:
(decompose 7654321)
;= [6 10 69 3912 7654320]
Upvotes: 5