Jak potęgować w clojure?


106

Jak mogę potęgować w clojure? Na razie potrzebuję tylko potęgowania liczb całkowitych, ale pytanie dotyczy również ułamków.


18
Jako ktoś, kto nie zna clojure, ale ma predyspozycje do polubienia go (bycie fanem seplenienia, programowania funkcjonalnego i posiadania wielu podręcznych bibliotek), jestem rozczarowany, że to proste pytanie ma tak wiele odpowiedzi - lub że w ogóle trzeba było zapytać. Pomyślałbym, że potęgowanie byłoby po prostu jedną z podstawowych funkcji bez konieczności robienia czegokolwiek specjalnego. Cieszę się jednak, że o to zapytano.
Mars

1
no tak, pewnie jakaś wersja powinna być w rdzeniu ... ale myślę, że wiele odpowiedzi to nadal dobry znak. Wydaje się, że „wiele ścieżek do implementacji” jest powodem, dla którego wiele z tych rzeczy nie jest dostarczanych - użytkownik powinien znać szczegóły funkcji, której używa, ze względu na wydajność. na przykład (jak wskazano w wybranej odpowiedzi) niektóre sposoby mogą potencjalnie zniszczyć stos, a inne rzadziej. może niektórzy są leniwi, inni chętni ... wszystkie szczegóły, na które należy zwrócić uwagę w Clojure, dlatego uważam, że większość nietrywialnych bibliotek nie jest dostarczana z powodu filozofii
jm0

3
Myślę, że powodem, dla którego nie ma tylko funkcji exp w rdzeniu, jest to, że wieża numeryczna Clojure jest poważnie zepsuta ze względu na wydajność. Jest więc wiele różnych rzeczy, które można rozumieć przez potęgowanie. Czym powinno być (exp 2 (exp 2 200))? Błąd lub duża liczba całkowita, której obliczenie wymaga wieku? Jeśli chcesz tylko zwykłego expa zmiennoprzecinkowego, to java jest wbudowany. Jeśli chcesz, aby język, w którym liczby starały się zachowywać jak liczby rzeczywiste i zawieszał koszt, użyj schematu zamiast clojure.
John Lawrence Aspden,

Odpowiedzi:


141

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)

11
Zabawna odpowiedź!
Matt Fenwick

zobacz w pełni iteracyjną wersję podstępnego rozwiązania poniżej stackoverflow.com/a/22977674/231589
Karl Rosaen

5
Clojure.contrib.math jest teraz przestarzały, zobacz Math.Numeric-Tower
alvaro g

Druga sugestia (rekurencyjny ogon) ma przepełnienie liczb całkowitych dla n <0
Pointo Senshi

1
Fantastyczna odpowiedź. Oto jak to zrobić za pomocą makra: (defmacro exp [xn] `(* ~ @ (take n (repeat x))))
Daniel Szmulewicz

78

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 exptdla potęgowania zamiast powerlub powktóre być może wyjaśnia, dlaczego jest to trochę trudne do znalezienia ... i tak oto mały przykład (uwaga, że useprace, 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

Przypomnienie o instalacji pakietu

Musisz najpierw zainstalować pakiet Java, org.clojure.math.numeric-toweraby 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.clji 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

1
Prawdopodobnie nieznane, ponieważ nie wydaje się być częścią standardowego Clojure. 1.3.0 wyświetla błędy dotyczące niemożności zlokalizowania pliku math.clj, gdy próbuję to zrobić w ten sposób.
Brian Knoblauch

9
Myślę, że jest teraz w "clojure.math.numeric-tower" od 1.3 (ponieważ clojure.contrib został podzielony na poszczególne biblioteki)
mikera

Dodano „przypomnienie o instalacji pakietu”, aby złagodzić frustrację użytkowników.
David Tonhofer

60

Możesz użyć java Math.powlub BigInteger.powmetod:

(Math/pow base exponent)

(.pow (bigint base) exponent)

+1, chociaż wiem, że możesz współpracować z bibliotekami java; jednak 1) Math.pow działa z wersjami podwójnymi, potrzebuję liczb całkowitych, czy możesz podać przykład? 2) Czy naprawdę musisz używać interop dla czegoś? proste jak uprawnienia?
Peter,

Clojure jest zbudowany wokół bibliotek java, nie próbuje naprawiać tego, co nie jest zepsute, a Math / pow działa dobrze. Dlaczego musisz dbać o pary lub liczby całkowite? Możesz również użyć tego richhickey.github.com/clojure-contrib/…
DaVinci,

1
@Peter: 1) Jeśli twoje moce nie są tak duże, że nie mogą być już dokładnie reprezentowane przez dublety, naprawdę nie ma problemu z rzuceniem wyniku na int. 2) Nie rozumiem, jak pisanie Math/powjest bardziej skomplikowane niż math-powczy 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.
wrzesień

3
@Da vinci: dziwna uwaga, to własny język i ma wiele funkcji, które są w Javie (takie jak stringreverse)
Peter,

4
Myślę, że lepiej jest używać Clojure.contrib.math / expt, jeśli chcesz uzyskać dokładne moce biginteger. Prawdopodobnie robi to samo pod maską, ale znacznie przyjemniej niż przechodzenie przez interop w Javie .....
mikera

13

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


+1 dla tej odpowiedzi, ponieważ poprawnie obsługuje wszystkie dokładne arytmetyki Clojure (tj. BigDecimal / BigInteger).
mikera


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

6
możesz również użyć do tego dosłownej notacji:(.pow 2M 100)
noisesmith

1
Ich typy są różne. user => (type 2M) java.math.BigDecimal user => (type (BigInteger. "2")) java.math.BigInteger
KIM Taegyoon

najlepsze rozwiązanie imo, showr, korzystanie z istniejących bibliotek, w tym obsługa biginta. +1
Daniel Gruszczyk

U mnie (Math/pow Math/E x)załatwia sprawę (wymiana Math/Ena wybraną przez Ciebie podstawę).
Zaz,

6

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 intlub podobnego. Funkcje są często bardziej przydatne niż metody, ponieważ można je przekazywać jako parametry do innych funkcji - w tym przypadku mapprzychodzi 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ć BigIntegerzamiast 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.


4

Myślę, że to też by działało:

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

ale wydaje się, że osiąga maksimum przy 2 ^ 63 ... zdecydowanie nie jest w stanie 2 ^ 200
TJ Trapp

4

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


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


1

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.


0

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.

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


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.