Як зробити експоненцію в клоджурі?


106

Як я можу зробити експоненцію в кложурі? Наразі мені потрібна лише ціла експоненція, але питання стосується і дробів.


18
Як людина, яка не знає клоджура, але схильна до вподобання (будучи шанувальником ліпс, функціонального програмування та маючи багато зручних бібліотек), я розчарований, що на це просте запитання існує стільки відповідей - або що він довелося взагалі просити. Я б подумав, що експоненціація буде просто однією з основних функцій, що надаються, без того, щоб робити щось особливе. Я радий, що його запитали.
Марс

1
ну так, мабуть, якась його версія повинна бути в основі ... але я думаю, що багато відповідей все ще є хорошим знаком. "здається, що" кілька шляхів до реалізації "є причиною того, що багато цих речей не передбачено - користувач повинен знати деталі функції, яку він використовує задля ефективності. наприклад (як вказується в обраній відповіді) деякі способи можуть потенційно підірвати стек, інші менш ймовірні. Можливо, деякі ледачі, якісь нетерплячі ... всі деталі, на які потрібно звернути деяку увагу в Clojure, саме тому я вважаю, що більшість нетривіальних
в'язнів

3
Я думаю, що причина, що в ядрі є не просто функція exp, полягає в тому, що числова вежа clojure сильно зламана з міркувань ефективності. Отже, існують всілякі різні речі, які ви могли б мати на увазі під впливом експоненції. Що має бути (exp 2 (exp 2 200))? Помилка чи велике ціле число, для якого потрібно обчислити вік? Якщо ви просто хочете звичайного досвіду з плаваючою точкою, тоді вбудований java. Якщо ви хочете мову, де цифри роблять все можливе, щоб діяти як дійсні, і повісити вартість, використовуйте схему замість clojure.
Джон Лоуренс Аспден

Відповіді:


141

класична рекурсія (дивіться це, вона дме стек)

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

хвоста рекурсія

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

функціональний

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

підлий (також удари стека, але не так легко)

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

бібліотека

(require 'clojure.contrib.math)

11
Розважальна відповідь!
Метт Фенвік

см повністю ітераційний варіант підлих рішень нижче stackoverflow.com/a/22977674/231589
Карл Rosaen

5
Clojure.contrib.math тепер застарілий, див. Math.Numeric-Tower
alvaro g

Друга пропозиція (рекурсивна хвоста) має цілий перелив для n <0.
Pointo Senshi

1
Фантастична відповідь. Ось як це зробити з макросом: (defmacro exp [xn] `(* ~ @ (take n (повторити x))))
Daniel Szmulewicz

78

Clojure має функцію живлення, яка добре працює: я б рекомендував використовувати це, а не переходити через Java interop, оскільки він обробляє всі типи чисел з довільною точністю Clojure правильно. Він знаходиться в просторі імен clojure.math.numeric-tower .

Це закликає швидше exptдо експоненції , powerабо powщо, можливо, пояснює, чому це важко знайти ... все одно ось невеликий приклад (зауважте, що useпрацює, але краще використовувати 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

Нагадування про встановлення пакета

Спочатку потрібно встановити пакет Java, org.clojure.math.numeric-towerщоб зробити clojure.math.numeric-towerдоступний простір імен Clojure !

У командному рядку:

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

Потім відредагуйте project.cljта додайте [org.clojure/math.numeric-tower "0.0.4"]до вектора залежностей.

Запустіть лейн-REPL (не клоджур REPL)

$ lein repl

Зараз:

(require '[clojure.math.numeric-tower :as math])
(math/expt 4 2)
;=> 16

або

(require '[clojure.math.numeric-tower :as math :refer [expt]])
(expt 4 2)
;=> 16

1
Напевно, невідомо, оскільки воно, схоже, не є частиною стандартного Clojure. 1.3.0 викидає помилки щодо неможливості знайти math.clj, коли я намагаюся зробити це таким чином.
Брайан Ноблеуч

9
Я думаю, це зараз у "clojure.math.numeric-tower" станом на 1.3 (оскільки clojure.contrib розпався на окремі бібліотеки)
mikera

Додано "нагадування про встановлення пакета", щоб зменшити розчарування користувачів.
Девід Тонхофер

60

Ви можете використовувати Java Math.powабо BigInteger.powметоди:

(Math/pow base exponent)

(.pow (bigint base) exponent)

+1, хоча я знаю, що ви можете взаємодіяти з java libs; однак, 1) Math.pow працює з парними, мені потрібні Integers, ви можете навести приклад? 2) Чи справді вам потрібно використовувати інтероп для чого-небудь. простий як повноваження?
Пітер

Clojure побудований навколо бібліотек Java, він не намагається виправити те, що не порушено, і Math / pow працює чудово. Чому потрібно піклуватися про парні чи цілі числа? Ви також можете скористатися цим richhickey.github.com/clojure-contrib/…
DaVinci

1
@Peter: 1) Якщо ваші повноваження не настільки великі, що їх більше не можна точно представити парними, насправді немає проблеми з просто передачею результату на int. 2) Я не бачу, як писати Math/powскладніше, math-powабо як би це не було, якби було б еквівалент clojure. Якщо вже існує простий метод java, який виконує те, що ви хочете, немає ніяких причин відтворювати функціональність у clojure. Java interop не є по суті шкідливим.
sepp2k

3
@Da Vinci: дивне зауваження, мова це власна, і має багато функцій, які є на Яві (на зразок stringreverse)
Пітер

4
Я думаю, що вам краще використовувати Clojure's clojure.contrib.math / expt, якщо ви хочете точних повноважень з великого числа. Можливо, те саме робиться під капотом, але набагато приємніше, ніж переходити через Java
interop

13

Коли спочатку було задано це питання, функцією офіційної бібліотеки для цього була функція clojure.contrib.math / expt . З цього часу він перемістився до clojure.math.numeric-tower


+1 для цієї відповіді, оскільки вона обробляє всю точну арифметику Clojure (тобто BigDecimal / BigInteger).
mikera

"Примітка - вкладки" contrib "перемістилися до окремих репостів під Clojure org" ; ця відповідь лише за посиланням зараз вводить в оману.
Бред Кох

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

6
також ви можете використовувати для цього буквальне позначення:(.pow 2M 100)
noisesmith

1
Їх типи різні. user => (type 2M) java.math.BigDecimal user => (type (BigInteger. "2")) java.math.BigInteger
KIM Taegyoon

найкраще рішення imo, showr, використовуючи існуючі бібліотеки, включаючи обробку bigint. +1
Данило Грущук

Для мене (Math/pow Math/E x)робить трюк (замінюючи Math/Eбазою на ваш вибір).
Заз

6

Якщо вам дійсно потрібна функція, а не метод, ви можете просто перетворити її:

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

І в цій функції ви можете передавати його intчи подібне. Функції часто є більш корисними, ніж методи, тому що ви можете передати їх як параметри іншим функціям - у цьому випадку mapмені спадає на думку.

Якщо вам справді потрібно уникати Java interop, ви можете написати власну функцію живлення. Наприклад, це проста функція:

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

Це обчислює потужність для цілого показника (тобто немає коренів).

Крім того, якщо ви маєте справу з великою кількістю, ви можете використовувати BigIntegerзамість цього int.

І якщо ви маєте справу з дуже великою кількістю, ви можете висловити їх у вигляді списків цифр і написати свої власні арифметичні функції, щоб передавати їх по мірі обчислення результату і виводити результат в інший потік.


4

Я думаю, що це також спрацює:

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

але, схоже, максимум на 2 ^ 63 ... деф не здатний до 2 ^ 200
TJ Trapp,

4

SICP надихнув повну ітераційну швидку версію "підступної" реалізації вище.

(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

Впровадження "підлогого" методу з рекурсією хвоста та підтримкою негативного показника:

(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

Спробуйте

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

для хвостово-рекурсивного рішення O (log n), якщо ви хочете його реалізувати самостійно (підтримує лише додатні цілі числа). Очевидно, що кращим рішенням є використання функцій бібліотеки, на які вказували інші.


0

Як щодо clojure.contrib.genric.math-функцій

У бібліотеці clojure.contrib.generic.math функцій є функція пороху. Це просто макрос до Math.pow і є скоріше "кложурським" способом виклику функції математики Java.

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


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.