【问题标题】:How to do exponentiation in clojure?如何在clojure中进行幂运算?
【发布时间】:2011-06-30 17:35:48
【问题描述】:

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

【问题讨论】:

  • 作为一个不了解 clojure 但倾向于喜欢它的人(喜欢 lisps、函数式编程并拥有许多方便的库),我很失望这个简单的问题这么多的答案——或者说它必须被问到。我原以为求幂只是提供的基本功能之一,而无需做任何特别的事情。不过,我很高兴有人问。
  • 嗯,是的,它的某些版本可能应该在核心中......但我认为许多答案仍然是一个好兆头。 “实现的多条路径”似乎是没有提供很多这些东西的原因——为了提高效率,用户应该知道他们正在使用的功能的细节。例如(正如所选答案中所指出的那样)某些方式可能会破坏堆栈,而其他方式则不太可能这样做。也许有些是懒惰的,有些是急切的……所有细节都需要在 Clojure 中引起注意,这就是为什么我觉得由于哲学原因,大多数非平凡的库都没有提供
  • 我认为核心中不仅有 exp 函数的原因是因为 clojure 的数字塔因效率原因而严重损坏。所以你可以用求幂来表示各种不同的东西。 (exp 2 (exp 2 200)) 应该是什么?一个错误或一个巨大的整数,需要一个年龄来计算?如果您只想要通常的浮点 exp,那么 Java 是内置的。如果您想要一种语言,其中数字尽最大努力表现得像实数,并降低成本,请使用方案而不是 clojure。

标签: clojure exponentiation


【解决方案1】:

我个人使用:

(defn pow [x n] (reduce *' (repeat n x)))

注意星号后面的撇号 (')。

适用于所有大小的整数。

注意:对于某些实现,这可能会有点慢。 (time (pow 2 200000)) 在我的系统上解决了 1.2 秒。

【讨论】:

    【解决方案2】:

    你可以使用java的Math.powBigInteger.pow方法:

    (Math/pow base exponent)
    
    (.pow (bigdec base) exponent)
    

    【讨论】:

    • +1,虽然我知道你可以与 java 库互操作;但是,1)Math.pow 适用于双打,我需要整数,你能举个例子吗? 2)你真的必须使用互操作吗?像权力一样简单?
    • @Peter: 1) 除非你的能力如此之大以至于它们不能用双精度来准确表示,否则将结果转换为 int 确实没有问题。 2)我看不出写Math/powmath-pow 更复杂,或者如果有一个clojure 等价物的话。如果已经有一个简单的 java 方法可以满足您的需求,那么没有理由在 clojure 中重新创建该功能。 Java 互操作本身并不有害。
    • @Da vinci : 奇怪的话,它是它自己的语言,并且有很多 Java 中的函数(如 stringreverse)
    • 如果你想要精确的大整数幂,我认为你最好使用 Clojure 的 clojure.contrib.math/expt。可能在引擎盖下做同样的事情,但比通过 Java 互操作要好得多.....
    • 我收到了No matching method pow ... for class clojure.lang.BigInt——不应该是(.pow (biginteger base) exponent)吗?
    【解决方案3】:

    Clojure 有一个运行良好的幂函数:我建议使用它而不是通过 Java 互操作,因为它可以正确处理所有 Clojure 任意精度数字类型。它位于命名空间clojure.math.numeric-tower

    它被称为 expt 用于 exponentiation 而不是 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
    

    【讨论】:

    • 可能未知,因为它似乎不是标准 Clojure 的一部分。当我尝试这样做时,1.3.0 会抛出关于无法找到 math.clj 的错误。
    • 我认为它现在在 1.3 的“clojure.math.numeric-tower”中(因为 clojure.contrib 被分解为单独的库)
    • 添加了“软件包安装提醒”以减轻用户的挫败感。
    【解决方案4】:

    最初提出这个问题时,clojure.contrib.math/expt 是执行此操作的官方库函数。此后移至clojure.math.numeric-tower

    【讨论】:

    【解决方案5】:

    使用reduce的简单单行:

    (defn pow [a b] (reduce * 1 (repeat b a)))
    

    【讨论】:

      【解决方案6】:

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

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

      【讨论】:

        【解决方案7】:

        使用clojure.math.numeric-tower,以前是clojure.contrib.math


        API Documentation


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

        【讨论】:

          【解决方案8】:

          SICP inspired 上面“偷偷摸摸”实现的完整迭代快速版本。

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

          【讨论】:

            【解决方案9】:
            user=> (.pow (BigInteger. "2") 10)
            1024
            user=> (.pow (BigInteger. "2") 100)
            1267650600228229401496703205376
            

            【讨论】:

            • 您也可以使用文字表示法:(.pow 2M 100)
            • 它们的类型不同。 user=> (type 2M) java.math.BigDecimal user=> (type (BigInteger."2")) java.math.BigInteger
            • imo 最佳解决方案,showr,使用现有库,包括处理 bigint。 +1
            • 对我来说 (Math/pow Math/E x) 可以解决问题(用您选择的基础替换 Math/E)。
            【解决方案10】:

            试试

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

            【讨论】:

              【解决方案11】:

              我认为这也可以:

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

              【讨论】:

              • 但似乎在 2^63 处达到最大值... def 无法达到 2^200
              【解决方案12】:

              clojure.contrib.genric.math-functions 怎么样

              clojure.contrib.generic.math-functions 库中有一个 pow 函数。它只是 Math.pow 的一个宏,更像是一种调用 Java 数学函数的“clojureish”方式。

              http://clojure.github.com/clojure-contrib/generic.math-functions-api.html#clojure.contrib.generic.math-functions/pow

              【解决方案13】:

              经典递归(看这个,它会破坏堆栈)

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

              【讨论】:

              • stackoverflow.com/a/22977674/231589下方查看偷偷摸摸的解决方案的完全迭代版本
              • Clojure.contrib.math 现已弃用,请参阅 Math.Numeric-Tower
              • 第二个建议(尾递归)对于 n
              • 很棒的答案。以下是如何使用宏来处理它: (defmacro exp [x n] `(* ~@(take n (repeat x))))
              • 快速求幂算法的终端递归:(def exp (letfn [(rexp [x n r] (cond (zero? n) r (even? n) (recur (*' x x) (/ n 2) r) :else (recur (*' x x) (/ (dec n) 2) (*' x r))))] (fn [x n] (rexp x n 1))))。它永远不会破坏堆栈。测试(exp 1 (exp 2 30000))
              【解决方案14】:

              如果你真的需要一个函数而不是一个方法,你可以简单地包装它:

               (defn pow [b e] (Math/pow b e))
              

              在此函数中,您可以将其转换为 int 或类似的。函数通常比方法更有用,因为您可以将它们作为参数传递给另一个函数 - 在这种情况下,我想到了 map

              如果您确实需要避免 Java 互操作,您可以编写自己的 power 函数。例如,这是一个简单的函数:

               (defn pow [n p] (let [result (apply * (take (abs p) (cycle [n])))]
                 (if (neg? p) (/ 1 result) result)))
              

              计算整数指数的幂(即没有根)。

              此外,如果您处理的是 数字,您可能希望使用BigInteger 而不是int

              如果您正在处理非常大的数字,您可能希望将它们表示为数字列表,并编写自己的算术函数以在它们计算结果并输出结果时对它们进行流式传输到其他流。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2015-07-20
                • 2011-04-04
                • 2018-07-18
                • 2010-09-27
                • 2018-07-22
                • 1970-01-01
                • 1970-01-01
                • 2011-09-18
                相关资源
                最近更新 更多