如何检查Clojure中的数字是否为Fibonacci数?

时间:2014-12-14 09:00:55

标签: clojure

输入:正整数。

输出:基于测试的真/假。

这是我的尝试:

(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))))))

问题是:此代码不适用于大量代码。我认为这可能会导致堆栈溢出。 还有更好的方法吗?

1 个答案:

答案 0 :(得分:3)

Math/powMath/sqrtMath/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))))