Как я могу выполнить возведение в степень в Clojure? Пока мне нужно только целочисленное возведение в степень, но вопрос касается и дробей.
Как я могу выполнить возведение в степень в Clojure? Пока мне нужно только целочисленное возведение в степень, но вопрос касается и дробей.
Ответы:
классическая рекурсия (смотри, она взрывает стек)
(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, поскольку она правильно обрабатывает все числовые типы произвольной точности 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 clojure.math.numeric-tower
доступным!
В командной строке:
$ lein new my-example-project
$ cd lein new my-example-project
Затем отредактируйте project.clj
и добавьте [org.clojure/math.numeric-tower "0.0.4"]
в вектор зависимостей.
Запустите lein 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 по своей сути не вредно.
Когда этот вопрос был первоначально задан, 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, вы можете написать свою собственную функцию мощности. Например, это простая функция:
(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)))))))
Простой однострочник с использованием reduce:
(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-functions
В библиотеке clojure.contrib.generic.math-functions есть функция pow. Это всего лишь макрос для Math.pow и более «банальный» способ вызова математической функции Java.