我该如何在Clojure中进行指数运算?目前我只需要整数指数,但这个问题也适用于分数。
我该如何在Clojure中进行指数运算?目前我只需要整数指数,但这个问题也适用于分数。
经典递归(观察一下,它会引发堆栈溢出)
(defn exp [x n]
(if (zero? n) 1
(* x (exp x (dec n)))))
尾递归(defn exp [x n]
(loop [acc 1 n n]
(if (zero? n) acc
(recur (* x acc) (dec n)))))
函数式的
(defn exp [x n]
(reduce * (repeat n x)))
sneaky(也称为blows stack,但不那么容易)
(defn exp-s [x n]
(let [square (fn[x] (* x x))]
(cond (zero? n) 1
(even? n) (square (exp-s x (/ n 2)))
:else (* x (exp-s x (dec n))))))
库
(require 'clojure.contrib.math)
(require 'clojure.contrib.math)
)。但是(defn exp [x n] (reduce * (repeat n x)))
非常好用,而且易于理解! - Andrew KosterClojure具有一个功能强大的幂函数:我建议使用它,而不是通过Java互操作性去处理所有Clojure任意精度数字类型。它在命名空间clojure.math.numeric-tower中。
它被称为expt
,表示指数运算,而不是power
或pow
,这可能解释了为什么有点难找…无论如何,这里有一个小例子(请注意,use
可以工作,但最好使用require
):
(require '[clojure.math.numeric-tower :as math :refer [expt]]) ; as of Clojure 1.3
;; (use 'clojure.contrib.math) ; before Clojure 1.3
(expt 2 200)
=> 1606938044258990275541962092341162602522202993782792835301376
您必须首先安装Java软件包org.clojure.math.numeric-tower
才能使Clojure命名空间clojure.math.numeric-tower
可访问!
在命令行上:
$ lein new my-example-project
$ cd lein new my-example-project
然后编辑 project.clj
文件,在依赖向量中添加 [org.clojure/math.numeric-tower "0.0.4"]
。
启动 lein REPL(不是 clojure REPL)。
$ lein repl
现在:
(require '[clojure.math.numeric-tower :as math])
(math/expt 4 2)
;=> 16
或者
(require '[clojure.math.numeric-tower :as math :refer [expt]])
(expt 4 2)
;=> 16
你可以使用Java的Math.pow
或BigInteger.pow
方法:
(Math/pow base exponent)
(.pow (bigdec base) exponent)
Math/pow
比写 math-pow
或者如果有clojure等效方法的话它的名字会更复杂。如果已经有一个简单的Java方法可以实现您想要的功能,那么没有必要在Clojure中重新创建该功能。Java互操作并不是本质上有害的。 - sepp2kNo matching method pow ... for class clojure.lang.BigInt
-- 难道不应该是 (.pow (biginteger base) exponent)
吗? - johncip当最初提出这个问题时,clojure.contrib.math/expt是官方的库函数来执行此操作。自那时以来,它已经移动到clojure.math.numeric-tower。
user=> (.pow (BigInteger. "2") 10)
1024
user=> (.pow (BigInteger. "2") 100)
1267650600228229401496703205376
(.pow 2M 100)
- noisesmith(Math/pow Math/E x)
就可以解决问题(将 Math/E
替换为您选择的底数)。 - Zaz如果您确实需要一个函数而不是方法,可以简单地将其包装:
(defn pow [b e] (Math/pow b e))
在这个函数中,你可以将它转换为int
或类似的类型。函数通常比方法更有用,因为你可以将它们作为参数传递给另一个函数 - 在这种情况下,我想到了map
。
如果你真的需要避免Java互操作,你可以编写自己的幂函数。例如,这是一个简单的函数:
(defn pow [n p] (let [result (apply * (take (abs p) (cycle [n])))]
(if (neg? p) (/ 1 result) result)))
这个函数计算整数指数的幂(即没有根号)。
此外,如果你要处理大量数字,你可能想使用BigInteger
而不是int
。
如果你要处理非常大的数字,你可以将它们表示为数字列表,并编写自己的算术函数来流式传输它们,以计算结果并将结果输出到其他流中。
(defn expt [x pow] (apply * (repeat pow x)))
SICP启发的完整迭代快速版本的“狡猾”实现如上所示。
(defn fast-expt-iter [b n]
(let [inner (fn [a b n]
(cond
(= n 0) a
(even? n) (recur a (* b b) (/ n 2))
:else (recur (* a b) b (- n 1))))
]
(inner 1 b n)))
使用尾递归实现“巧妙”的方法,并支持负数指数:
(defn exp
"exponent of x^n (int n only), with tail recursion and O(logn)"
[x n]
(if (< n 0)
(/ 1 (exp x (- n)))
(loop [acc 1
base x
pow n]
(if (= pow 0)
acc
(if (even? pow)
(recur acc (* base base) (/ pow 2))
(recur (* acc base) base (dec pow)))))))