如何在 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)))
鬼祟(也吹掉堆栈,但不那么容易)
(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)
Clojure 有一个运行良好的强大功能:我建议使用它而不是通过 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)
最初提出这个问题时,clojure.contrib.math/expt是执行此操作的官方库函数。从那时起,它已转移到 clojure.math.numeric-tower
user=> (.pow (BigInteger. "2") 10)
1024
user=> (.pow (BigInteger. "2") 100)
1267650600228229401496703205376
如果您确实需要一个函数而不是方法,您可以简单地包装它:
(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)))))))
使用reduce的简单单行:
(defn pow [a b] (reduce * 1 (repeat b a)))
clojure.math.numeric-tower
,以前的 clojure.contrib.math
。
(ns user
(:require [clojure.math.numeric-tower :as m]))
(defn- sqr
"Uses the numeric tower expt to square a number"
[x]
(m/expt x 2))
尝试
(defn pow [x n]
(loop [x x n n r 1]
(cond
(= n 0) r
(even? n) (recur (* x x) (/ n 2) r)
:else (recur x (dec n) (* r x)))))
对于尾递归 O(log n) 解决方案,如果您想自己实现(仅支持正整数)。 显然,更好的解决方案是使用其他人指出的库函数。
我个人使用:
(defn pow [x n] (reduce *' (repeat n x)))
注意星号后面的撇号 (')。
适用于所有大小的整数。
注意:对于某些实现来说,这可能有点慢。 (时间(pow 2 200000))在我的系统上花了 1.2 秒来解析。
clojure.contrib.genric.math-functions 怎么样
clojure.contrib.generic.math-functions 库中有一个 pow 函数。 它只是 Math.pow 的一个宏,更像是调用 Java 数学函数的“clojureish”方式。
这个很简洁:
(bigint(.pow 10M 1002))
对于 BigInt
或
(.pow 10M 1002)
对于 BigDecimal