输入:正整数。
输出:基于测试的真/假。
这是我的尝试:
(defn is-a-fib? [x]
"Check whether x is a fibonacci number.
Algorithm: test whether 5x^2+4 or 5x^2-4 is a perfect square."
(let [a (+' (*' (Math/pow x 2) 5) 4) ; 5x^2+4
b (-' (*' (Math/pow x 2) 5) 4) ; 5x^2-4
sqrt-a (Math/sqrt a)
sqrt-b (Math/sqrt b)]
(or (== (*' sqrt-a sqrt-a)
(*' (Math/floor sqrt-a) (Math/floor sqrt-a))) ; Test whether n is a perfect square
(== (*' sqrt-b sqrt-b)
(*' (Math/floor sqrt-b) (Math/floor sqrt-b))))))
问题是:此代码不适用于大量代码。我认为这可能会导致堆栈溢出。 还有更好的方法吗?
答案 0 :(得分:3)
Math/pow
,Math/sqrt
和Math/floor
操作适用于精度范围有限的double
,对它们的操作会出现舍入错误。
如果你从这个角度看待它,事情可能会因为四舍五入而脱轨,但是当你已经用尽了精度(15-17位小数)时它们真的会出错。
此第一个 n 的Fibonnacci,其中此算法为后续整数提供误报,是与 n = 74相关联的16位整数。
(is-a-fib? 1304969544928657)
=> true
(is-a-fib? 1304969544928658)
=> true
修改:添加避免double
的任意精度解决方案:
主要困难是缺少整数平方根算法。
This Java implementation可以翻译成Clojure:
(defn integer-sqrt [n]
(let [n (biginteger n)]
(loop [a BigInteger/ONE
b (-> n (.shiftRight 5) (.add (biginteger 8)))]
(if (>= (.compareTo b a) 0)
(let [mid (-> a (.add b) (.shiftRight 1))]
(if (pos? (-> mid (.multiply mid) (.compareTo n)))
(recur a (.subtract mid BigInteger/ONE))
(recur (.add mid BigInteger/ONE) b)))
(dec a)))))
有了这个,你可以定义一个任意精度的完美平方测试:
(defn perfect-square? [n]
(let [x (integer-sqrt n)]
(= (*' x x) n)))
并更新您的实施以使用它:
(defn is-a-fib? [x]
"Check whether x is a fibonacci number.
Algorithm: test whether 5x^2+4 or 5x^2-4 is a perfect square."
(let [a (+' (*' (*' x x) 5) 4) ; 5x^2+4
b (-' (*' (*' x x) 5) 4)] ; 5x^2-4
(or (perfect-square? a)
(perfect-square? b))))