Como fazer exponenciação em clojure?

106

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.

Peter
fonte
18
Como alguém que não conhece clojure, mas está predisposto a gostar (sendo um fã de lisps, programação funcional e ter muitas bibliotecas úteis), estou desapontado que esta pergunta simples tenha tantas respostas - ou que teve que ser perguntado em tudo. Eu teria pensado que a exponenciação seria apenas uma das funções básicas fornecidas sem ter que fazer nada de especial. Estou feliz que tenha sido perguntado, no entanto.
Marte,
1
bem, sim, provavelmente alguma versão dele deveria estar no núcleo ... mas acho que muitas respostas ainda é um bom sinal. os "vários caminhos para a implementação" parecem ser a razão pela qual muitas dessas coisas não são fornecidas - o usuário deve saber os detalhes da função que está usando por uma questão de eficiência. por exemplo (como é apontado na resposta escolhida) algumas maneiras podem explodir a pilha, outras menos prováveis ​​de fazê-lo. talvez alguns sejam preguiçosos, alguns ansiosos ... todos os detalhes que precisam de atenção no Clojure, é por isso que sinto que a maioria das bibliotecas não triviais não são fornecidas devido à filosofia
jm0
3
Acho que a razão de não haver apenas uma função exp no núcleo é porque a torre numérica do clojure está muito quebrada por razões de eficiência. Portanto, há todos os tipos de coisas diferentes que você poderia entender por exponenciação. O que deve ser (exp 2 (exp 2 200))? Um erro ou um número inteiro enorme que leva uma eternidade para ser calculado? Se você quiser apenas o exp de ponto flutuante usual, então o java está embutido. Se você quiser uma linguagem em que os números façam o seu melhor para agir como os reais e pendure o custo, use o esquema em vez de clojure.
John Lawrence Aspden,

Respostas:

141

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)
John Lawrence Aspden
fonte
11
Resposta divertida!
Matt Fenwick
veja a versão totalmente iterativa da solução sorrateira abaixo stackoverflow.com/a/22977674/231589
Karl Rosaen
5
Clojure.contrib.math agora está obsoleto, consulte Math.Numeric-Tower
alvaro g
A segunda sugestão (cauda recursiva) tem estouro de número inteiro para n <0.
Pointo Senshi
1
Resposta fantástica. Veja como fazer isso com uma macro: (defmacro exp [xn] `(* ~ @ (take n (repetir x))))
Daniel Szmulewicz
78

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 exptde exponenciação em vez de powerou o powque talvez explique por que é um pouco difícil de encontrar ... de qualquer maneira, aqui está um pequeno exemplo (observe que usefunciona, 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

Lembrete sobre a instalação do pacote

Você deve primeiro instalar o pacote Java org.clojure.math.numeric-towerpara tornar o namespace Clojure clojure.math.numeric-toweracessível!

Na linha de comando:

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

Em seguida, edite project.clje 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
Mikera
fonte
1
Provavelmente desconhecido porque não parece fazer parte do Clojure padrão. 1.3.0 lança erros sobre não ser capaz de localizar o math.clj quando tento fazer isso dessa maneira.
Brian Knoblauch
9
Acho que agora está em "clojure.math.numeric-tower" a partir de 1.3 (já que clojure.contrib foi dividido em bibliotecas individuais)
mikera
Adicionado "lembrete sobre a instalação do pacote" para diminuir a frustração do usuário.
David Tonhofer
60

Você pode usar métodos java Math.powou BigInteger.pow:

(Math/pow base exponent)

(.pow (bigint base) exponent)
sepp2k
fonte
1, embora eu saiba que você pode interoperar com bibliotecas java; entretanto, 1) Math.pow funciona com duplos, preciso de números inteiros, você pode dar um exemplo? 2) Você realmente precisa usar interoperabilidade para sth. simples como poderes?
Peter
Clojure é construído em torno das bibliotecas java, ele não tenta consertar o que não está quebrado e Math / pow funciona muito bem. Por que você precisa se preocupar com duplos ou inteiros? Você também pode usar este richhickey.github.com/clojure-contrib/…
DaVinci de
1
@Peter: 1) A menos que seus poderes sejam tão grandes que não possam mais ser representados com precisão por duplos, realmente não há problema em apenas lançar o resultado em int. 2) Não vejo como escrever Math/powé mais complicado do que math-powou 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.
sepp2k de
3
@Da vinci: observação estranha, é uma linguagem própria e tem muitas funções que estão em Java (como stringreverse)
Peter
4
Acho que é melhor usar clojure.contrib.math / expt de Clojure se quiser poderes biginteger precisos. Provavelmente faz o mesmo nos bastidores, mas muito mais agradável do que via interoperabilidade Java ...
mikera
13

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

amalloy
fonte
1 para esta resposta, uma vez que lida com toda a aritmética exata de Clojure (ou seja, BigDecimal / BigInteger) corretamente.
Mikera
"Nota - as bibliotecas contrib foram movidas para repositórios individuais em Clojure org" ; esta resposta apenas com o link agora é enganosa.
Brad Koch
8
user=> (.pow (BigInteger. "2") 10)
1024
user=> (.pow (BigInteger. "2") 100)
1267650600228229401496703205376
KIM Taegyoon
fonte
6
você também pode usar a notação literal para isso:(.pow 2M 100)
noisesmith
1
Seus tipos são diferentes. usuário => (tipo 2M) java.math.BigDecimal usuário => (tipo (BigInteger. "2"))
java.math.BigInteger
imo best solution, showr, usando bibliotecas existentes, e incluindo manipulação de bigint. +1
Daniel Gruszczyk
Para mim (Math/pow Math/E x)faz o truque (substituindo Math/Ecom a base de sua escolha).
Zaz de
6

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 intou 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, mapvem à 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 BigIntegervez 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.

Goran Jovic
fonte
4

Acho que isso também funcionaria:

(defn expt [x pow] (apply * (repeat pow x)))
TJ Trapp
fonte
mas parece estar no máximo em 2 ^ 63 ... def não é capaz de 2 ^ 200
TJ Trapp
4

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)))
Karl Rosaen
fonte
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)))))))
Tolstoyevsky
fonte
2

Um one-liner simples usando reduzir:

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

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.

Retief
fonte
0

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.

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

fido
fonte