Come eseguire l'elevazione a potenza in clojure?


106

Come posso eseguire l'elevazione a potenza in clojure? Per ora ho solo bisogno di un esponenziale intero, ma la domanda vale anche per le frazioni.


18
Come qualcuno che non conosce clojure, ma è predisposto ad apprezzarlo (essendo un fan di lisps, programmazione funzionale e avendo molte librerie utili), sono deluso dal fatto che questa semplice domanda abbia così tante risposte - o che doveva essere chiesto a tutti. Avrei pensato che l'elevazione a potenza sarebbe solo una delle funzioni di base fornite senza dover fare nulla di speciale. Sono contento che sia stato chiesto, però.
Marte

1
beh, sì, probabilmente qualche versione dovrebbe essere nel nucleo ... ma penso che molte risposte siano ancora un buon segno. i "percorsi multipli per l'implementazione" sembrano essere la ragione per cui molte di queste cose non vengono fornite: l'utente dovrebbe conoscere i dettagli della funzione che sta utilizzando per motivi di efficienza. per esempio (come è sottolineato nella risposta scelta) alcuni modi possono potenzialmente far saltare la pila, altri meno propensi a farlo. forse alcuni sono pigri, altri desiderosi ... tutti i dettagli che devono essere prestati attenzione in Clojure, motivo per cui ritengo che la maggior parte delle librerie non banali non siano fornite a causa della filosofia
jm0

3
Penso che il motivo per cui non c'è solo una funzione exp nel core è perché la torre numerica di clojure è gravemente rotta per motivi di efficienza. Quindi ci sono tutti i tipi di cose diverse che potresti intendere per esponenziale. Cosa dovrebbe essere (exp 2 (exp 2 200))? Un errore o un numero intero enorme che richiede un'età per calcolare? Se vuoi solo il solito exp in virgola mobile, allora quello java è integrato. Se vuoi un linguaggio in cui i numeri fanno del loro meglio per agire come i reali, e appendere il costo, usa lo schema invece di clojure.
John Lawrence Aspden

Risposte:


141

ricorsione classica (guarda questo, fa saltare lo stack)

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

ricorsione della coda

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

funzionale

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

subdolo (fa anche saltare lo stack, ma non così 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)

11
Risposta divertente!
Matt Fenwick

vedi versione completamente iterativa di una soluzione subdolo sotto stackoverflow.com/a/22977674/231589
Karl Rosaen

5
Clojure.contrib.math è ora deprecato, vedere Math.Numeric-Tower
alvaro g

Il secondo suggerimento (coda ricorsiva) ha un intero overflow per n <0.
Pointo Senshi

1
Risposta fantastica. Ecco come farlo con una macro: (defmacro exp [xn] `(* ~ @ (take n (repeat x))))
Daniel Szmulewicz

78

Clojure ha una funzione di alimentazione che funziona bene: consiglierei di usarlo piuttosto che passare tramite l'interoperabilità Java poiché gestisce correttamente tutti i tipi di numeri a precisione arbitraria Clojure. È nello spazio dei nomi clojure.math.numeric-tower .

Si chiama exptper l'elevamento a potenza , piuttosto che powero powche forse spiega il motivo per cui è un po 'difficile da trovare ... in ogni caso, ecco un piccolo esempio (nota che usefunziona ma un uso migliore 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

Promemoria sull'installazione del pacchetto

È necessario prima installare il pacchetto Java org.clojure.math.numeric-towerper rendere clojure.math.numeric-toweraccessibile lo spazio dei nomi Clojure !

Sulla riga di comando:

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

Quindi modifica project.clje aggiungi [org.clojure/math.numeric-tower "0.0.4"]al vettore delle dipendenze.

Avvia un REPL lein (non un REPL clojure)

$ lein repl

Adesso:

(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

1
Probabilmente sconosciuto perché non sembra far parte del Clojure standard. 1.3.0 lancia errori di non essere in grado di individuare math.clj quando provo a farlo in questo modo.
Brian Knoblauch

9
Penso che ora sia in "clojure.math.numeric-tower" a partire dalla 1.3 (poiché clojure.contrib è stato suddiviso in singole biblioteche)
mikera

Aggiunto "promemoria sull'installazione del pacchetto" per alleviare la frustrazione dell'utente.
David Tonhofer

60

Puoi usare java Math.powo BigInteger.powmetodi:

(Math/pow base exponent)

(.pow (bigint base) exponent)

+1, anche se so che puoi interagire con le librerie java; tuttavia, 1) Math.pow funziona con i doppi, ho bisogno di numeri interi, puoi fare un esempio? 2) Devi davvero usare l'interoperabilità per qc. semplice come poteri?
Peter,

Clojure è costruito attorno alle librerie java, non tenta di riparare ciò che non è rotto e Math / pow funziona perfettamente. Perché devi prenderti cura dei doppi o dei numeri interi? Puoi anche usare questo richhickey.github.com/clojure-contrib/…
DaVinci

1
@Peter: 1) A meno che i tuoi poteri non siano così grandi da non poter più essere rappresentati con precisione dai doppi, non c'è davvero alcun problema a lanciare il risultato su int. 2) Non vedo come scrivere Math/powsia più complicato math-powo qualunque sia il nome se ci fosse un equivalente clojure. Se esiste già un semplice metodo java che fa quello che vuoi, non c'è motivo di ricreare la funzionalità in clojure. L'interoperabilità Java non è intrinsecamente dannosa.
sepp2k

3
@ Da vinci: strana osservazione, è un linguaggio tutto suo e ha molte funzioni che sono in Java (come stringreverse)
Peter

4
Penso che tu stia usando meglio clojure.contrib.math / expt di Clojure se vuoi poteri biginteger accurati. Probabilmente fa lo stesso sotto il cofano, ma molto più bello che passare tramite Java Interop .....
mikera

13

Quando questa domanda è stata inizialmente posta, clojure.contrib.math / expt era la funzione di libreria ufficiale per farlo. Da allora, si è spostato in clojure.math.numeric-tower


+1 per questa risposta poiché gestisce correttamente tutta l'aritmetica esatta di Clojure (cioè BigDecimal / BigInteger).
mikera


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

6
puoi anche usare la notazione letterale per questo:(.pow 2M 100)
noisesmith

1
I loro tipi sono diversi. user => (type 2M) java.math.BigDecimal user => (type (BigInteger. "2")) java.math.BigInteger
KIM Taegyoon

imo migliore soluzione, showr, utilizzando le librerie esistenti e inclusa la gestione di bigint. +1
Daniel Gruszczyk

Per me (Math/pow Math/E x)fa il trucco (sostituire Math/Econ la base che preferisci).
Zaz

6

Se hai davvero bisogno di una funzione e non di un metodo puoi semplicemente racchiuderlo:

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

E in questa funzione puoi lanciarlo a into simili. Le funzioni sono spesso più utili dei metodi perché puoi passarle come parametri ad altre funzioni - in questo caso mi mapviene in mente.

Se hai davvero bisogno di evitare l'interoperabilità Java, puoi scrivere la tua funzione di alimentazione. Ad esempio, questa è una semplice funzione:

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

Questo calcola la potenza per l'esponente intero (cioè senza radici).

Inoltre, se hai a che fare con grandi numeri, potresti voler usare al BigIntegerposto di int.

E se hai a che fare con numeri molto grandi , potresti esprimerli come elenchi di cifre e scrivere le tue funzioni aritmetiche per eseguire il flusso su di essi mentre calcolano il risultato e restituiscono il risultato a un altro flusso.


4

Penso che funzionerebbe anche questo:

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

ma sembra al massimo a 2 ^ 63 ... def non capace di 2 ^ 200
TJ Trapp

4

SICP ha ispirato la versione rapida e iterativa completa dell'implementazione "subdola" sopra.

(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

Implementazione del metodo "subdolo" con ricorsione in coda e supporto dell'esponente negativo:

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

2

Un semplice one-liner con ridurre:

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

1

Provare

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

per una soluzione O (log n) ricorsiva in coda, se vuoi implementarla da solo (supporta solo numeri interi positivi). Ovviamente, la soluzione migliore è utilizzare le funzioni di libreria che altri hanno indicato.


0

Che ne dici di clojure.contrib.genric.math-functions

C'è una funzione pow nella libreria clojure.contrib.generic.math-functions. È solo una macro per Math.pow ed è più un modo "clojureish" di chiamare la funzione matematica Java.

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


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.