Como posso fazer exponenciação em clojure? Por enquanto, estou precisando apenas da exponenciação inteira, mas a questão vale para as frações também.
Como posso fazer exponenciação em clojure? Por enquanto, estou precisando apenas da exponenciação inteira, mas a questão vale para as frações também.
Respostas:
recursão clássica (observe isso, explode pilha)
(defn exp [x n]
(if (zero? n) 1
(* x (exp x (dec n)))))
recursão da cauda
(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 (também sopra pilha, mas não tão facilmente)
(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)
Clojure tem uma função de potência que funciona bem: eu recomendo usar isso em vez de usar a interoperabilidade Java, uma vez que lida com todos os tipos de número de precisão arbitrária do Clojure corretamente. Ele está no namespace clojure.math.numeric-tower .
É chamado expt
de exponenciação em vez de power
ou o pow
que talvez explique por que é um pouco difícil de encontrar ... de qualquer maneira, aqui está um pequeno exemplo (observe que use
funciona, mas é melhor usar 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
Você deve primeiro instalar o pacote Java org.clojure.math.numeric-tower
para tornar o namespace Clojure clojure.math.numeric-tower
acessível!
Na linha de comando:
$ lein new my-example-project
$ cd lein new my-example-project
Em seguida, edite project.clj
e adicione [org.clojure/math.numeric-tower "0.0.4"]
ao vetor de dependências.
Inicie um REPL lein (não um REPL clojure)
$ lein repl
Agora:
(require '[clojure.math.numeric-tower :as math])
(math/expt 4 2)
;=> 16
ou
(require '[clojure.math.numeric-tower :as math :refer [expt]])
(expt 4 2)
;=> 16
Você pode usar métodos java Math.pow
ou BigInteger.pow
:
(Math/pow base exponent)
(.pow (bigint base) exponent)
Math/pow
é mais complicado do que math-pow
ou qualquer que seja o nome se houvesse um equivalente de clojure. Se já existe um método java simples que faz o que você deseja, não há razão para recriar a funcionalidade no clojure. A interoperabilidade Java não é inerentemente prejudicial.
Quando essa pergunta foi feita originalmente, clojure.contrib.math / expt era a função oficial da biblioteca para fazer isso. Desde então, mudou para clojure.math.numeric-tower
user=> (.pow (BigInteger. "2") 10)
1024
user=> (.pow (BigInteger. "2") 100)
1267650600228229401496703205376
(.pow 2M 100)
(Math/pow Math/E x)
faz o truque (substituindo Math/E
com a base de sua escolha).
Se você realmente precisa de uma função e não de um método, pode simplesmente envolvê-la:
(defn pow [b e] (Math/pow b e))
E nesta função você pode lançá-lo para int
ou similar. As funções geralmente são mais úteis do que os métodos porque você pode passá-los como parâmetros para outras funções - neste caso, map
vem à minha mente.
Se você realmente precisa evitar a interoperabilidade Java, pode escrever sua própria função de energia. Por exemplo, esta é uma função simples:
(defn pow [n p] (let [result (apply * (take (abs p) (cycle [n])))]
(if (neg? p) (/ 1 result) result)))
Isso calcula a potência do expoente inteiro (ou seja, sem raízes).
Além disso, se estiver lidando com números grandes , você pode usar em BigInteger
vez de int
.
E se você estiver lidando com números muito grandes , pode querer expressá-los como listas de dígitos e escrever suas próprias funções aritméticas para transmitir sobre eles enquanto calculam o resultado e emitem o resultado para algum outro fluxo.
SICP inspirou a versão rápida iterativa completa da implementação 'sorrateira' acima.
(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)))
Use clojure.math.numeric-tower
, anteriormente 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))
Implementação do método "sorrateiro" com recursão de cauda e expoente negativo de suporte:
(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)))))))
Um one-liner simples usando reduzir:
(defn pow [a b] (reduce * 1 (repeat b a)))
Experimentar
(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 uma solução O (log n) recursiva na cauda, se você quiser implementá-la você mesmo (suporta apenas inteiros positivos). Obviamente, a melhor solução é usar as funções de biblioteca que outros apontaram.
Que tal funções de clojure.contrib.genric.math
Há uma função pow na biblioteca de funções clojure.contrib.generic.math. É apenas uma macro para Math.pow e é mais uma maneira "clojure" de chamar a função matemática Java.