Як я можу зробити експоненцію в кложурі? Наразі мені потрібна лише ціла експоненція, але питання стосується і дробів.
Як я можу зробити експоненцію в кложурі? Наразі мені потрібна лише ціла експоненція, але питання стосується і дробів.
Відповіді:
класична рекурсія (дивіться це, вона дме стек)
(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)
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
Ви можете використовувати Java Math.pow
або BigInteger.pow
методи:
(Math/pow base exponent)
(.pow (bigint base) exponent)
Math/pow
складніше, math-pow
або як би це не було, якби було б еквівалент clojure. Якщо вже існує простий метод java, який виконує те, що ви хочете, немає ніяких причин відтворювати функціональність у clojure. Java interop не є по суті шкідливим.
Коли спочатку було задано це питання, функцією офіційної бібліотеки для цього була функція clojure.contrib.math / expt . З цього часу він перемістився до clojure.math.numeric-tower
user=> (.pow (BigInteger. "2") 10)
1024
user=> (.pow (BigInteger. "2") 100)
1267650600228229401496703205376
(.pow 2M 100)
(Math/pow Math/E x)
робить трюк (замінюючи Math/E
базою на ваш вибір).
Якщо вам дійсно потрібна функція, а не метод, ви можете просто перетворити її:
(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
.
І якщо ви маєте справу з дуже великою кількістю, ви можете висловити їх у вигляді списків цифр і написати свої власні арифметичні функції, щоб передавати їх по мірі обчислення результату і виводити результат в інший потік.
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)))
Використовуйте clojure.math.numeric-tower
раніше 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))
Впровадження "підлогого" методу з рекурсією хвоста та підтримкою негативного показника:
(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)))))))
Простий однолінійний з використанням скорочення:
(defn pow [a b] (reduce * 1 (repeat b a)))
Спробуйте
(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), якщо ви хочете його реалізувати самостійно (підтримує лише додатні цілі числа). Очевидно, що кращим рішенням є використання функцій бібліотеки, на які вказували інші.
Як щодо clojure.contrib.genric.math-функцій
У бібліотеці clojure.contrib.generic.math функцій є функція пороху. Це просто макрос до Math.pow і є скоріше "кложурським" способом виклику функції математики Java.