Jak mogę potęgować w clojure? Na razie potrzebuję tylko potęgowania liczb całkowitych, ale pytanie dotyczy również ułamków.
Jak mogę potęgować w clojure? Na razie potrzebuję tylko potęgowania liczb całkowitych, ale pytanie dotyczy również ułamków.
Odpowiedzi:
klasyczna rekurencja (patrz, wysadza stos)
(defn exp [x n]
(if (zero? n) 1
(* x (exp x (dec n)))))
rekurencja ogona
(defn exp [x n]
(loop [acc 1 n n]
(if (zero? n) acc
(recur (* x acc) (dec n)))))
funkcjonalny
(defn exp [x n]
(reduce * (repeat n x)))
podstępny (również ciosy stosu, ale nie tak łatwo)
(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))))))
biblioteka
(require 'clojure.contrib.math)
Clojure ma funkcję power, która działa dobrze: zalecam używanie tego zamiast przechodzenia przez interop w Javie, ponieważ poprawnie obsługuje wszystkie typy liczb Clojure o arbitralnej precyzji. Znajduje się w przestrzeni nazw clojure.math.numeric-tower .
To się nazywa expt
dla potęgowania zamiast power
lub pow
które być może wyjaśnia, dlaczego jest to trochę trudne do znalezienia ... i tak oto mały przykład (uwaga, że use
prace, ale lepsze wykorzystanie 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
Musisz najpierw zainstalować pakiet Java, org.clojure.math.numeric-tower
aby udostępnić przestrzeń nazw Clojure clojure.math.numeric-tower
!
W linii poleceń:
$ lein new my-example-project
$ cd lein new my-example-project
Następnie edytuj project.clj
i dodaj [org.clojure/math.numeric-tower "0.0.4"]
do wektora zależności.
Rozpocznij lein REPL (nie clojure REPL)
$ lein repl
Teraz:
(require '[clojure.math.numeric-tower :as math])
(math/expt 4 2)
;=> 16
lub
(require '[clojure.math.numeric-tower :as math :refer [expt]])
(expt 4 2)
;=> 16
Możesz użyć java Math.pow
lub BigInteger.pow
metod:
(Math/pow base exponent)
(.pow (bigint base) exponent)
Math/pow
jest bardziej skomplikowane niż math-pow
czy jakakolwiek byłaby nazwa, gdyby istniał odpowiednik clojure. Jeśli istnieje już prosta metoda java, która robi to, co chcesz, nie ma powodu, aby odtwarzać tę funkcjonalność w clojure. Współpraca w języku Java nie jest z natury szkodliwa.
Kiedy pierwotnie zadano to pytanie, clojure.contrib.math / expt był oficjalną funkcją biblioteki, która to zrobiła. Od tego czasu przeniósł się do clojure.math.numeric-tower
user=> (.pow (BigInteger. "2") 10)
1024
user=> (.pow (BigInteger. "2") 100)
1267650600228229401496703205376
(.pow 2M 100)
(Math/pow Math/E x)
załatwia sprawę (wymiana Math/E
na wybraną przez Ciebie podstawę).
Jeśli naprawdę potrzebujesz funkcji, a nie metody, możesz ją po prostu opakować:
(defn pow [b e] (Math/pow b e))
W tej funkcji możesz przesłać go do int
lub podobnego. Funkcje są często bardziej przydatne niż metody, ponieważ można je przekazywać jako parametry do innych funkcji - w tym przypadku map
przychodzi mi na myśl.
Jeśli naprawdę chcesz uniknąć współdziałania Java, możesz napisać własną funkcję Power. Na przykład jest to prosta funkcja:
(defn pow [n p] (let [result (apply * (take (abs p) (cycle [n])))]
(if (neg? p) (/ 1 result) result)))
To oblicza moc dla wykładnika będącego liczbą całkowitą (tj. Bez pierwiastków).
Ponadto, jeśli masz do czynienia z dużymi liczbami, możesz użyć BigInteger
zamiast int
.
A jeśli masz do czynienia bardzo dużymi liczbami, możesz chcieć wyrazić je jako listy cyfr i napisać własne funkcje arytmetyczne, aby przesyłać je strumieniowo podczas obliczania wyniku i wyprowadzania wyniku do innego strumienia.
SICP zainspirował pełną iteracyjną szybką wersję „podstępnej” implementacji powyżej.
(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)))
Użyj clojure.math.numeric-tower
, dawniej 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))
Implementacja metody „podstępnej” z rekurencją ogonową i obsługą wykładnika ujemnego:
(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)))))))
Prosta jedna linijka wykorzystująca redukuje:
(defn pow [a b] (reduce * 1 (repeat b a)))
Próbować
(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)))))
dla rozwiązania ogonowo-rekurencyjnego O (log n), jeśli chcesz je zaimplementować samodzielnie (obsługuje tylko dodatnie liczby całkowite). Oczywiście lepszym rozwiązaniem jest użycie funkcji biblioteki, które wskazali inni.
A co z clojure.contrib.genric.math-functions
W bibliotece clojure.contrib.generic.math-functions znajduje się funkcja pow. Jest to po prostu makro dla Math.pow i bardziej "clojureish" sposób wywoływania funkcji matematycznej Java.