Clojure如何求幂?


106

我该如何在Clojure中求幂?现在,我只需要整数幂,但是问题也适用于分数。


18
作为一个不了解clojure但很喜欢它的人(喜欢lisps,函数式编程以及拥有许多方便的库),我很失望这个简单的问题有这么多答案-或它必须被问到。我以为取幂只是所提供的基本功能之一,而不必做任何特别的事情。我很高兴被问到。
火星

1
好吧,可能它的某些版本应该在核心中……但是我认为许多答案仍然是一个好兆头。“实现的多条路径”似乎是未提供许多此类服务的原因-为了提高效率,用户应了解所使用功能的详细信息。例如(如所选答案中指出的那样),某些方法可能会破坏堆栈,而其他方法则不太可能。也许有些是懒惰的,有些是渴望的... Clojure中需要注意的所有细节,这就是为什么我觉得由于哲学而没有提供大多数非平凡的libs
jm0 2013年

3
我认为,在内核中不仅有一个exp函数的原因是因为出于效率原因,clojure的数字塔严重损坏了。因此,求幂可能意味着各种各样的事情。(exp 2(exp 2 200))应该是什么?需要一个年龄才能计算的错误或巨大整数?如果只想使用通常的浮点exp,则内置一个java。如果要使数字尽其所能地发挥作用,并降低成本,请使用scheme而不是clojure。
约翰·劳伦斯·阿斯普登

Answers:


141

经典递归(观看此文章,它会发散堆栈)

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

11
有趣的答案!
马特·芬威克


5
Clojure.contrib.math现在已被弃用,请参见Math.Numeric-Tower
alvaro g

第二个建议(尾递归)具有对于n <0的整数溢出
Pointo战士

1
很棒的答案。这是使用宏的方法:(defmacro exp [xn]`(*〜@(take n(repeat x))))
Daniel Szmulewicz

78

Clojure具有强大的功能,可以很好地发挥作用:我建议您使用此函数,而不要通过Java interop,因为它可以正确处理所有Clojure任意精度数字类型。它在命名空间clojure.math.numeric-tower中

它被称为expt而不是powerpow可能解释了为什么很难找到它的原因……无论如何,这是一个小例子(请注意,它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

1
可能未知,因为它似乎不是标准Clojure的一部分。当我尝试以这种方式执行时,1.3.0会抛出有关无法找到math.clj的错误。
Brian Knoblauch

9
我认为它从1.3开始就在“ clojure.math.numeric-tower”中(因为clojure.contrib被分解成单独的库)
mikera 2011年

添加了“关于软件包安装的提醒”以减轻用户的沮丧感。
David Tonhofer

60

您可以使用Java Math.powBigInteger.pow方法:

(Math/pow base exponent)

(.pow (bigint base) exponent)

+1,尽管我知道您可以与Java库互操作;但是,1)Math.pow可以使用双打,我需要整数,可以举个例子吗?2)您真的必须使用互操作性吗?像权力一样简单?
彼得

Clojure是围绕Java库构建的,它不会尝试修复未损坏的内容,并且Math / pow可以正常工作。为什么需要照顾双精度或整数?您还可以使用此richhickey.github.com/clojure-contrib/…–
DaVinci

1
@Peter:1)除非您的能力如此之大,以至于不能再用double精确地表示它们,否则将结果强制转换为int确实没有问题。2)我看不出写作Math/powmath-pow如何存在clojure等效物更复杂。如果已经有一个简单的java方法可以执行您想要的操作,则没有理由在clojure中重新创建功能。Java互操作并不是天生有害的。
sepp2k 2011年

3
@Da vinci:奇怪的话,这是它自己的语言,并且有很多Java函数(例如stringreverse)
Peter

4
我认为,如果您想要准确的biginteger幂,最好使用Clojure的clojure.contrib.math / expt。可能在
后台


8
user=> (.pow (BigInteger. "2") 10)
1024
user=> (.pow (BigInteger. "2") 100)
1267650600228229401496703205376

6
您也可以为此使用文字符号:(.pow 2M 100)
noisesmith

1
它们的类型不同。用户=>(类型2M)java.math.BigDecimal用户=>(类型(BigInteger。“ 2”))java.math.BigInteger
KIM Taegyoon 2013年

imo最佳解决方案,包括使用现有库的showr,包括处理bigint。+1
Daniel Gruszczyk

对我来说,(Math/pow Math/E x)就可以了(取代Math/E您选择的基础)。
Zaz

6

如果您确实需要一个函数而不是一个方法,则可以将其包装:

 (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

而且,如果要处理非常大的数字,则可能需要将它们表示为数字列表,并编写自己的算术函数以在它们计算结果并将它们输出到其他流时在它们上进行流式处理。


4

我认为这也会起作用:

(defn expt [x pow] (apply * (repeat pow x)))

但似乎最大速度为2 ^ 63 ... def无法达到2 ^ 200
TJ Trapp

4

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


2

具有尾递归并支持负指数的“偷偷摸摸”方法的实现:

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


1

尝试

(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)解决方案,如果您想自己实现(仅支持正整数)。显然,更好的解决方案是使用其他人指出的库函数。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.