¿Cómo hacer exponenciación en clojure?

106

¿Cómo puedo hacer exponenciación en clojure? Por ahora, solo necesito una exponenciación de números enteros, pero la pregunta también se aplica a las fracciones.

Pedro
fuente
18
Como alguien que no conoce clojure, pero está predispuesto a que le guste (siendo un fanático de lisps, programación funcional y tener muchas bibliotecas útiles), me decepciona que esta simple pregunta tenga tantas respuestas, o que tenía que ser preguntado en absoluto. Hubiera pensado que la exponenciación sería solo una de las funciones básicas proporcionadas sin tener que hacer nada especial. Sin embargo, me alegro de que me lo pidieran.
Marte
1
bueno, sí, probablemente alguna versión debería estar en el núcleo ... pero creo que muchas respuestas siguen siendo una buena señal. las "múltiples rutas de implementación" parecen ser la razón por la que muchas de estas cosas no se proporcionan; el usuario debe conocer los detalles de la función que está utilizando por motivos de eficiencia. por ejemplo (como se señala en la respuesta elegida) algunas formas pueden potencialmente arruinar la pila, otras es menos probable que lo hagan. tal vez algunos son perezosos, otros ansiosos ... todos los detalles a los que se debe prestar atención en Clojure, por lo que siento que la mayoría de las bibliotecas no triviales no se proporcionan debido a la filosofía
jm0
3
Creo que la razón por la que no hay solo una función exp en el núcleo es porque la torre numérica de clojure está muy rota por razones de eficiencia. Así que hay todo tipo de cosas diferentes a las que podría referirse con exponenciación. ¿Qué debería ser (exp 2 (exp 2 200))? ¿Un error o un entero enorme que requiere una edad para calcularlo? Si solo desea el exp de punto flotante habitual, entonces el de java está integrado. Si desea un lenguaje donde los números hagan todo lo posible para actuar como los reales y colgar el costo, use el esquema en lugar de clojure.
John Lawrence Aspden

Respuestas:

141

recursividad clásica (mira esto, sopla pila)

(defn exp [x n]
     (if (zero? n) 1
         (* x (exp x (dec n)))))

recursividad de cola

(defn exp [x n]
  (loop [acc 1 n n]
    (if (zero? n) acc
        (recur (* x acc) (dec n)))))

funcional

(defn exp [x n]
  (reduce * (repeat n x)))

furtivo (también se acumulan golpes, pero no tan fácilmente)

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

biblioteca

(require 'clojure.contrib.math)
John Lawrence Aspden
fuente
11
¡Entretenida respuesta!
Matt Fenwick
vea la versión completamente iterativa de la solución furtiva a continuación stackoverflow.com/a/22977674/231589
Karl Rosaen
5
Clojure.contrib.math ahora está en desuso, consulte Math.Numeric-Tower
alvaro g
La segunda sugerencia (cola recursiva) tiene un desbordamiento de enteros para n <0.
Pointo Senshi
1
Fantástica respuesta. Aquí se explica cómo hacerlo con una macro: (defmacro exp [xn] `(* ~ @ (take n (repeat x))))
Daniel Szmulewicz
78

Clojure tiene una función de potencia que funciona bien: recomendaría usar esto en lugar de usar la interoperabilidad de Java, ya que maneja todos los tipos de números de precisión arbitraria de Clojure correctamente. Está en el espacio de nombres clojure.math.numeric-tower .

Se llama exptpara exponenciación en lugar de power, o powlo que quizá explica por qué es un poco difícil de encontrar ... de todos modos aquí está un pequeño ejemplo (nota que usefunciona, pero un mejor usorequire ):

(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

Recordatorio sobre la instalación del paquete

Primero debe instalar el paquete de Java org.clojure.math.numeric-tower para que el espacio de nombres de Clojure sea clojure.math.numeric-toweraccesible!

En la línea de comando:

$ lein new my-example-project
$ cd lein new my-example-project

Entonces edita project.clj y agregue [org.clojure/math.numeric-tower "0.0.4"]al vector de dependencias.

Inicie un REPL lein (no un REPL clojure)

$ lein repl

Ahora:

(require '[clojure.math.numeric-tower :as math])
(math/expt 4 2)
;=> 16

o

(require '[clojure.math.numeric-tower :as math :refer [expt]])
(expt 4 2)
;=> 16
mikera
fuente
1
Probablemente se desconoce porque no parece ser parte de Clojure estándar. 1.3.0 arroja errores sobre no poder ubicar el math.clj cuando trato de hacerlo de esta manera.
Brian Knoblauch
9
Creo que ahora está en "clojure.math.numeric-tower" a partir de 1.3 (desde que clojure.contrib se dividió en bibliotecas individuales)
mikera
Se agregó un "recordatorio sobre la instalación del paquete" para aliviar la frustración del usuario.
David Tonhofer
60

Puede utilizar los métodos Math.powo de Java BigInteger.pow:

(Math/pow base exponent)

(.pow (bigint base) exponent)
sepp2k
fuente
+1, aunque sé que puede interoperar con Java Libs; sin embargo, 1) Math.pow funciona con dobles, necesito enteros, ¿puedes dar un ejemplo? 2) ¿Realmente tienes que usar interop para algo. simple como poderes?
Peter
Clojure está construido alrededor de las bibliotecas de Java, no intenta arreglar lo que no está roto y Math / pow funciona bien. ¿Por qué necesitas cuidar los dobles o los enteros? También puede usar este richhickey.github.com/clojure-contrib/…
DaVinci
1
@Peter: 1) A menos que tus poderes sean tan grandes que ya no puedan ser representados con precisión por dobles, realmente no hay ningún problema con solo lanzar el resultado a int. 2) No veo cómo la escritura Math/powes más complicada math-powo cualquiera que sea ​​el nombre si hubiera un equivalente de clojure. Si ya existe un método java simple que hace lo que desea, no hay razón para recrear la funcionalidad en clojure. La interoperabilidad de Java no es intrínsecamente dañina.
sepp2k
3
@Da vinci: comentario extraño, es un lenguaje propio y tiene muchas funciones que están en Java (como stringreverse)
Peter
4
Creo que es mejor usar clojure.contrib.math / expt de Clojure si desea poderes precisos de biginteger. Probablemente haga lo mismo bajo el capó, pero mucho mejor que ir a través de la interoperabilidad de Java .....
mikera
8
user=> (.pow (BigInteger. "2") 10)
1024
user=> (.pow (BigInteger. "2") 100)
1267650600228229401496703205376
KIM Taegyoon
fuente
6
también puede usar la notación literal para esto:(.pow 2M 100)
noisesmith
1
Sus tipos son diferentes. usuario => (tipo 2M) java.math.BigDecimal usuario => (tipo (BigInteger. "2")) java.math.BigInteger
KIM Taegyoon
En mi opinión, la mejor solución, showr, usando bibliotecas existentes e incluyendo el manejo de bigint. +1
Daniel Gruszczyk
Para mí (Math/pow Math/E x)hace el truco (reemplazando Math/Econ la base de su elección).
Zaz
6

Si realmente necesita una función y no un método, simplemente puede ajustarlo:

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

Y en esta función puedes enviarlo a into similar. Las funciones suelen ser más útiles que los métodos porque puede pasarlas como parámetros a otras funciones, en este casomap viene a la mente.

Si realmente necesita evitar la interoperabilidad de Java, puede escribir su propia función de potencia. Por ejemplo, esta es una función simple:

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

Eso calcula la potencia del exponente entero (es decir, sin raíces).

Además, si se trata de números grandes , es posible que desee utilizar en BigIntegerlugar deint .

Y si se trata de números muy grandes , es posible que desee expresarlos como listas de dígitos y escribir sus propias funciones aritméticas para transmitirlas a medida que calculan el resultado y envían el resultado a otra secuencia.

Goran Jovic
fuente
4

Creo que esto también funcionaría:

(defn expt [x pow] (apply * (repeat pow x)))
TJ Trapp
fuente
pero parece alcanzar un máximo de 2 ^ 63 ... definitivamente no es capaz de 2 ^ 200
TJ Trapp
4

SICP inspiró la versión rápida iterativa completa de la implementación 'furtiva' anterior.

(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)))
Karl Rosaen
fuente
2

Implementación del método "furtivo" con recursividad de cola y exponente negativo de apoyo:

(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)))))))
Tolstoyevsky
fuente
2

Un simple de una sola línea que usa reduce:

(defn pow [a b] (reduce * 1 (repeat b a)))
Alan R. Soares
fuente
1

Tratar

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

para una solución O (log n) recursiva de cola, si desea implementarla usted mismo (solo admite enteros positivos). Obviamente, la mejor solución es utilizar las funciones de la biblioteca que otros han señalado.

Retief
fuente
0

¿Qué hay de clojure.contrib.genric.math-functions

Hay una función pow en la biblioteca clojure.contrib.generic.math-functions. Es solo una macro para Math.pow y es más una forma "clojureish" de llamar a la función matemática de Java.

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

fido
fuente