Bagaimana saya bisa melakukan eksponen di clojure? Untuk saat ini saya hanya membutuhkan eksponen bilangan bulat, tetapi pertanyaannya juga berlaku untuk pecahan.
Bagaimana saya bisa melakukan eksponen di clojure? Untuk saat ini saya hanya membutuhkan eksponen bilangan bulat, tetapi pertanyaannya juga berlaku untuk pecahan.
Jawaban:
rekursi klasik (lihat ini, meniup tumpukan)
(defn exp [x n]
(if (zero? n) 1
(* x (exp x (dec n)))))
rekursi ekor
(defn exp [x n]
(loop [acc 1 n n]
(if (zero? n) acc
(recur (* x acc) (dec n)))))
fungsional
(defn exp [x n]
(reduce * (repeat n x)))
licik (juga menumpuk, tapi tidak begitu mudah)
(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))))))
Perpustakaan
(require 'clojure.contrib.math)
Clojure memiliki fungsi daya yang berfungsi dengan baik: Saya merekomendasikan penggunaan ini daripada melalui interop Java karena ini menangani semua tipe angka presisi arbitrer dengan benar. Itu ada di namespace clojure.math.numeric-tower .
Ini disebut expt
untuk exponentiation daripada power
atau pow
yang mungkin menjelaskan mengapa itu agak sulit untuk menemukan ... anyway inilah contoh kecil (catatan bahwa use
karya tetapi lebih baik menggunakan 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
Anda harus menginstal paket Java terlebih dahulu org.clojure.math.numeric-tower
agar namespace Clojure clojure.math.numeric-tower
dapat diakses!
Di baris perintah:
$ lein new my-example-project
$ cd lein new my-example-project
Kemudian edit project.clj
dan tambahkan [org.clojure/math.numeric-tower "0.0.4"]
ke vektor dependensi.
Mulai REPL lein (bukan REPL clojure)
$ lein repl
Sekarang:
(require '[clojure.math.numeric-tower :as math])
(math/expt 4 2)
;=> 16
atau
(require '[clojure.math.numeric-tower :as math :refer [expt]])
(expt 4 2)
;=> 16
Anda dapat menggunakan java Math.pow
atau BigInteger.pow
metode:
(Math/pow base exponent)
(.pow (bigint base) exponent)
Math/pow
lebih rumit dari math-pow
atau apapun namanya jika ada padanan jubah. Jika sudah ada metode java sederhana yang melakukan apa yang Anda inginkan, tidak ada alasan untuk membuat ulang fungsionalitas di clojure. Interop Java tidak berbahaya secara inheren.
Ketika pertanyaan ini pertama kali diajukan, clojure.contrib.math / expt adalah fungsi perpustakaan resmi untuk melakukan ini. Sejak itu, telah dipindahkan ke menara clojure.math.numeric
user=> (.pow (BigInteger. "2") 10)
1024
user=> (.pow (BigInteger. "2") 100)
1267650600228229401496703205376
(.pow 2M 100)
(Math/pow Math/E x)
melakukan trik (mengganti Math/E
dengan alas pilihan Anda).
Jika Anda benar-benar membutuhkan suatu fungsi dan bukan metode, Anda dapat membungkusnya:
(defn pow [b e] (Math/pow b e))
Dan dalam fungsi ini Anda dapat mentransmisikannya int
atau serupa. Fungsi seringkali lebih berguna daripada metode karena Anda dapat meneruskannya sebagai parameter ke fungsi lain - dalam hal ini map
terlintas di benak saya.
Jika Anda benar-benar perlu menghindari interop Java, Anda dapat menulis fungsi daya Anda sendiri. Misalnya, ini adalah fungsi sederhana:
(defn pow [n p] (let [result (apply * (take (abs p) (cycle [n])))]
(if (neg? p) (/ 1 result) result)))
Itu menghitung pangkat untuk eksponen integer (yaitu tidak ada akar).
Juga, jika Anda berurusan dengan besar angka, Anda mungkin ingin menggunakan BigInteger
bukan int
.
Dan jika Anda berurusan dengan angka yang sangat besar , Anda mungkin ingin mengekspresikannya sebagai daftar digit, dan menulis fungsi aritmatika Anda sendiri untuk mengalirkannya saat mereka menghitung hasil dan mengeluarkan hasilnya ke aliran lain.
SICP menginspirasi versi cepat iteratif penuh dari implementasi 'licik' di atas.
(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)))
Gunakan clojure.math.numeric-tower
, sebelumnya 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))
Penerapan metode "licik" dengan rekursi ekor dan mendukung eksponen negatif:
(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)))))))
Satu baris sederhana menggunakan pengurangan:
(defn pow [a b] (reduce * 1 (repeat b a)))
Mencoba
(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)))))
untuk solusi tail-recursive O (log n), jika Anda ingin menerapkannya sendiri (hanya mendukung bilangan bulat positif). Jelas, solusi yang lebih baik adalah dengan menggunakan fungsi perpustakaan yang telah ditunjukkan oleh orang lain.
Bagaimana dengan clojure.contrib.genric.math-functions
Ada fungsi pow di pustaka clojure.contrib.generic.math-functions. Ini hanyalah makro untuk Math.pow dan lebih merupakan cara "clojureish" untuk memanggil fungsi matematika Java.