Làm thế nào để tính lũy thừa trong clojure?


106

Làm cách nào tôi có thể tính lũy thừa trong clojure? Bây giờ tôi chỉ cần lũy thừa số nguyên, nhưng câu hỏi cũng dành cho phân số.


18
Là một người không biết clojure, nhưng có khuynh hướng thích nó (là người yêu thích ngôn ngữ ngọng, lập trình chức năng và có rất nhiều thư viện tiện dụng), tôi thất vọng vì câu hỏi đơn giản này có quá nhiều câu trả lời - hoặc nó đã phải được hỏi ở tất cả. Tôi đã nghĩ rằng lũy ​​thừa sẽ chỉ là một trong những hàm cơ bản được cung cấp mà không cần phải làm bất cứ điều gì đặc biệt. Tôi rất vui vì nó đã được hỏi, mặc dù.
Mars

1
vâng có lẽ một số phiên bản của nó nên nằm trong lõi ... nhưng tôi nghĩ rằng nhiều câu trả lời vẫn là một dấu hiệu tốt. "nhiều đường dẫn để triển khai" dường như là lý do khiến nhiều thứ không được cung cấp - người dùng nên biết chi tiết về chức năng mà họ đang sử dụng vì lợi ích của hiệu quả. ví dụ (như được chỉ ra trong câu trả lời đã chọn) một số cách có thể có khả năng làm nổ ngăn xếp, những cách khác ít có khả năng làm như vậy. có thể một số là lười biếng, một số háo hức ... tất cả chi tiết mà cần phải được trả một số sự chú ý trong Clojure, đó là lý do tại sao tôi cảm thấy hầu hết các libs không tầm thường không được cung cấp do triết lý
jm0

3
Tôi nghĩ lý do không chỉ có hàm exp trong lõi là vì tháp số của clojure bị hỏng nặng vì lý do hiệu quả. Vì vậy, có tất cả các loại khác nhau mà bạn có thể có nghĩa là bằng lũy ​​thừa. (Exp 2 (exp 2 200)) nên là gì? Một lỗi hoặc một số nguyên lớn cần một tuổi để tính toán? Nếu bạn chỉ muốn điểm kinh nghiệm dấu phẩy động thông thường, thì java đã được tích hợp sẵn. Nếu bạn muốn một ngôn ngữ mà ở đó các con số cố gắng hết sức để hoạt động giống như số thực và treo chi phí, hãy sử dụng lược đồ thay vì clojure.
John Lawrence Aspden

Câu trả lời:


141

đệ quy cổ điển (xem này, nó thổi chồng lên)

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

đệ quy đuôi

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

chức năng

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

lén lút (cũng có thể thổi stack, nhưng không dễ dàng như vậy)

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

thư viện

(require 'clojure.contrib.math)

11
Câu trả lời giải trí!
Matt Fenwick

xem phiên bản lặp lại đầy đủ của giải pháp lén lút bên dưới stackoverflow.com/a/22977674/231589
Karl Rosaen

5
Clojure.contrib.math hiện không được dùng nữa, hãy xem Math.Numeric-Tower
alvaro g Ngày

Đề nghị thứ hai (đuôi đệ quy) có tràn số nguyên với n <0.
Pointo Senshi

1
Câu trả lời tuyệt vời. Dưới đây là làm thế nào để làm với nó với một vĩ mô: (defmacro exp [xn] `(* ~ @ (take n (repeat x))))
Daniel Szmulewicz

78

Clojure có một hàm nguồn hoạt động tốt: Tôi khuyên bạn nên sử dụng hàm này thay vì sử dụng hàm tương tác Java vì nó xử lý chính xác tất cả các loại số có độ chính xác tùy ý của Clojure. Nó nằm trong không gian tên clojure.math.numeric-tower .

Nó được gọi là exptcho lũy thừa chứ không phải là powerhay powmà có lẽ giải thích tại sao đó là một chút khó khăn để tìm ... anyway đây là một ví dụ nhỏ (lưu ý rằng usetác phẩm nhưng sử dụng tốt hơn 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

Nhắc nhở về cài đặt gói

Trước tiên, bạn phải cài đặt gói Java org.clojure.math.numeric-towerđể làm cho không gian tên Clojure clojure.math.numeric-towercó thể truy cập được!

Trên dòng lệnh:

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

Sau đó, chỉnh sửa project.cljvà thêm [org.clojure/math.numeric-tower "0.0.4"]vào vector phụ thuộc.

Bắt đầu REPL lein (không phải REPL áo choàng)

$ lein repl

Hiện nay:

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

hoặc là

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

1
Có lẽ là không xác định vì nó không có vẻ là một phần của Clojure tiêu chuẩn. 1.3.0 đưa ra lỗi về việc không thể xác định vị trí math.clj khi tôi cố gắng thực hiện theo cách này.
Brian Knoblauch

9
Tôi nghĩ đó là bây giờ trong "clojure.math.numeric-tháp" như 1,3 (vì clojure.contrib đã chia thành các thư viện cá nhân)
mikera

Đã thêm "nhắc nhở về cài đặt gói" để giảm bớt sự thất vọng của người dùng.
David Tonhofer

60

Bạn có thể sử dụng java Math.powhoặc BigInteger.powcác phương pháp:

(Math/pow base exponent)

(.pow (bigint base) exponent)

+1, mặc dù tôi biết bạn có thể tương tác với java libs; tuy nhiên, 1) Math.pow hoạt động với nhân đôi, tôi cần Số nguyên, bạn có thể cho ví dụ không? 2) Bạn có thực sự phải sử dụng interop cho sth không. đơn giản như quyền hạn?
Peter

Clojure được xây dựng bao quanh các thư viện java, nó không cố gắng sửa chữa những gì không bị hỏng và Math / pow hoạt động tốt. Tại sao bạn cần quan tâm đến số nhân đôi hoặc số nguyên? Bạn cũng có thể sử dụng richhickey.github.com/clojure-contrib/…
DaVinci này

1
@Peter: 1) Trừ khi quyền hạn của bạn lớn đến mức không thể biểu diễn chính xác bằng các nhân đôi được nữa, nếu không thực sự không có vấn đề gì khi chỉ chuyển kết quả thành int. 2) Tôi không thấy cách viết Math/powphức tạp hơn math-powhoặc bất cứ cái tên nào nếu có một chiếc áo choàng tương đương. Nếu đã có một phương thức java đơn giản thực hiện những gì bạn muốn, thì không có lý do gì để tạo lại chức năng trong clojure. Java interop vốn dĩ không có hại.
sepp2k

3
@Da vinci: nhận xét kỳ lạ, đó là ngôn ngữ của riêng nó và có rất nhiều chức năng có trong Java (như stringreverse)
Peter

4
Tôi nghĩ bạn tốt hơn nên sử dụng clojure.contrib.math / expt của Clojure nếu bạn muốn có sức mạnh biginteger chính xác. Có lẽ cũng làm như vậy dưới mui xe nhưng nhiều đẹp hơn vì đi qua Java interop .....
mikera

13

Khi câu hỏi này ban đầu được hỏi, clojure.contrib.math / expt là chức năng thư viện chính thức để thực hiện điều này. Kể từ đó, nó đã chuyển sang clojure.math.numeric-tower


+1 cho câu trả lời này vì nó xử lý chính xác tất cả các số học chính xác của Clojure (tức là BigDecimal / BigInteger).
mikera


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

6
bạn cũng có thể sử dụng các ký hiệu theo nghĩa đen cho việc này:(.pow 2M 100)
noisesmith

1
Các loại của chúng khác nhau. user => (loại 2M) dùng java.math.BigDecimal => (loại (BigInteger "2").) java.math.BigInteger
KIM Taegyoon

imo giải pháp tốt nhất, showr, sử dụng các thư viện hiện có và bao gồm cả việc xử lý bigint. +1
Daniel Gruszczyk

Đối với tôi (Math/pow Math/E x)thực hiện thủ thuật (thay thế Math/Ebằng cơ sở của sự lựa chọn của bạn).
Zaz

6

Nếu bạn thực sự cần một hàm chứ không phải một phương thức, bạn có thể chỉ cần gói nó:

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

Và trong chức năng này, bạn có thể truyền nó đến inthoặc tương tự. Các hàm thường hữu ích hơn các phương thức vì bạn có thể truyền chúng dưới dạng tham số cho các hàm khác - trong trường hợp mapnày tôi nghĩ đến.

Nếu bạn thực sự cần tránh tương tác Java, bạn có thể viết hàm nguồn của riêng mình. Ví dụ, đây là một hàm đơn giản:

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

Điều đó tính toán lũy thừa cho số mũ nguyên (tức là không có căn).

Ngoài ra, nếu bạn đang xử lý số lượng lớn , bạn có thể muốn sử dụng BigIntegerthay thế int.

Và nếu bạn đang xử lý các số rất lớn , bạn có thể muốn biểu thị chúng dưới dạng danh sách các chữ số và viết các hàm số học của riêng bạn để truyền qua chúng khi chúng tính toán kết quả và xuất kết quả ra một số luồng khác.


4

Tôi nghĩ điều này cũng sẽ hoạt động:

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

nhưng dường như ra tối đa 2 ^ 63 ... def không có khả năng 2 ^ 200
TJ Trapp

4

SICP đã lấy cảm hứng từ phiên bản nhanh chóng lặp đi lặp lại đầy đủ của việc triển khai 'lén lút' ở trên.

(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

Thực hiện phương pháp "lén lút" với đệ quy đuôi và hỗ trợ số mũ âm:

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

2

Một lớp lót đơn giản sử dụng giảm:

(defn pow [a b] (reduce * 1 (repeat b a)))

1

Thử

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

đối với giải pháp O (log n) đệ quy đuôi, nếu bạn muốn tự thực hiện (chỉ hỗ trợ số nguyên dương). Rõ ràng, giải pháp tốt hơn là sử dụng các hàm thư viện mà những người khác đã chỉ ra.


0

Còn về clojure.contrib.genric.math-functions

Có một hàm pow trong thư viện clojure.contrib.generic.math-functions. Nó chỉ là một macro đối với Math.pow và giống như một cách gọi hàm Java toán học theo kiểu "sơ sài".

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


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.