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


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.