2013-06-05 40 views
8

Có hàm số mũ lũy thừa số nguyên trong OCaml không? ** chỉ dành cho phao. Mặc dù nó có vẻ là chủ yếu là chính xác, không phải là có một khả năng lỗi chính xác, một cái gì đó giống như 2. ** 3. = 8. trở về sai đôi khi? Có chức năng thư viện cho lũy thừa số nguyên không? Tôi có thể viết của riêng tôi, nhưng mối quan tâm hiệu quả đi vào đó, và tôi cũng sẽ ngạc nhiên nếu không có một chức năng như vậy.Số mũ lũy thừa trong OCaml

Trả lời

10

Về phần dấu chấm động của câu hỏi của bạn: OCaml gọi hàm pow() của hệ thống nằm bên dưới. Điểm lũy thừa dấu chấm là một hàm khó thực hiện, nhưng nó chỉ cần trung thành (có nghĩa là chính xác đến một Unit in the Last Place) để làm cho 2. ** 3. = 8. đánh giá là true, vì 8.0 là chỉtrong một ULP của kết quả toán học chính xác 8.

Tất cả thư viện toán học nên (*) trung thành, vì vậy bạn không cần phải lo lắng về ví dụ cụ thể này. Nhưng not all of them actually are, vì vậy bạn có quyền lo lắng.


Một lý do tốt hơn để lo lắng sẽ là, nếu bạn đang sử dụng 63-bit số nguyên hoặc rộng hơn, rằng các đối số hoặc kết quả của các lũy thừa không thể được đại diện chính xác như OCaml nổi (trên thực tế IEEE 754 số tăng gấp đôi độ chính xác không thể đại diện cho 9_007_199_254_740_993 hoặc 2 + 1). Trong trường hợp này, lũy thừa dấu phẩy động là một thay thế xấu cho lũy thừa số nguyên, không phải vì một điểm yếu trong việc triển khai cụ thể, mà bởi vì nó không được thiết kế để biểu diễn chính xác các số nguyên lớn.


(*) Một tham khảo thú vị khác để đọc về chủ đề này là “A Logarithm Too Clever by Half” của William Kahan.

+0

Là lũy thừa điểm động nhanh như lũy thừa số nguyên cho rằng các đối số giống nhau không? Ngoài ra, chỉ cần được rõ ràng, là tuyên bố của bạn rằng lũy ​​thừa điểm nổi phải trung thành đúng cho tất cả các số nguyên a, b trong đó -2^30 ≤ a^b <2^30, hoặc chỉ cho ví dụ cụ thể của tôi về 2 3 và số 8? – user2258552

+0

@ user2258552 Về tốc độ: lũy thừa điểm động có thể chậm hơn một số nguyên được viết tốt nhất. Về ý nghĩa của “nên”: một chức năng cơ bản trung thành là chính xác cho một ULP trên miền định nghĩa của nó. Tất cả các libms nên trung thành bởi vì nó là một sự thỏa hiệp hợp lý giữa chi phí tính toán và độ chính xác mà sẽ làm cho khá nhiều người hạnh phúc. Độ chính xác 0,5 ULP là một chút quá nhiều để mong đợi của tất cả các libms vì tình trạng khó xử của nhà sản xuất bảng, nhưng độ chính xác đến 1 ULP có thể đạt được với chi phí hợp lý. (nhưng, một lần nữa, pow() là một trong những chức năng cơ bản khó nhất) –

+0

Về tốc độ: trong ánh sáng đó, sau đó nó thực sự làm cho rất ít ý nghĩa để không bao gồm một hàm số mũ lũy thừa trong thư viện chuẩn ... – user2258552

19

Không có trong thư viện chuẩn. Nhưng bạn có thể dễ dàng tự viết một mình (sử dụng exponentiation by squaring để nhanh) hoặc sử dụng lại thư viện mở rộng cung cấp. Trong Batteries, nó là Int.pow.

Dưới đây là một việc thực hiện đề xuất:

let rec pow a = function 
    | 0 -> 1 
    | 1 -> a 
    | n -> 
    let b = pow a (n/2) in 
    b * b * (if n mod 2 = 0 then 1 else a) 

Nếu có một nguy cơ tràn vì bạn đang thao tác với số lượng rất lớn, có lẽ bạn nên sử dụng một thư viện lớn nguyên như Zarith, cung cấp tất cả các loại của hàm mũ.

(Bạn có thể cần những "lũy thừa modulo", tính toán (a^n) mod p, điều này có thể được thực hiện trong một cách mà tránh tràn bằng cách áp dụng các mod trong tính toán trung gian, ví dụ trong hàm pow ở trên.)

+3

Great câu trả lời. Thật không may, tôi chỉ có thể chọn một câu trả lời hay nhất: /. Ngoài ra, tôi không tin rằng đây luôn là cách nhanh nhất để thực hiện lũy thừa số nguyên. Trong thực tế, tôi nghĩ rằng có một câu hỏi Project Euler (mà tôi vẫn chưa giải quyết) dọc theo những dòng này. Tôi thực sự nghĩ rằng số mũ lũy thừa phải được thêm vào thư viện chuẩn. Ngay cả khi nó không phải là bất kỳ hiệu quả hơn (mà tôi không chắc chắn là sự thật), đó là một điều khá phổ biến cần và phải chuyển đổi và deconvert từ phao là gây phiền nhiễu. Tất nhiên việc nhập một thư viện không phải là khó, nhưng không có lý do nào không phải là tiêu chuẩn. – user2258552

+2

Vâng, nếu bạn có ý tưởng tốt hơn về cách triển khai thực hiện số nguyên trong trường hợp chung, vui lòng đề xuất triển khai. – gasche

+0

@ user2258552 Tôi không đồng ý với tiền đề của bạn rằng lũy ​​thừa số nguyên là quá phổ biến. Trong thực tế, bạn hầu như luôn luôn làm việc với các số mũ cố định nhỏ, hoặc bạn * cần * một số học chính xác tùy ý theo gợi ý của gasche. TL; DR: Ngừng tin rằng bạn cần số mũ lũy thừa trên ints chính xác cố định và nhận ra bạn cần một thư viện số học chính xác tùy ý. –

3

Đây là một thực hiện trong đó sử dụng thuật toán bình phương và nhân (như một cung cấp bởi @gasche), nhưng điều này là đuôi-đệ quy

let is_even n = 
    n mod 2 = 0 

(* https://en.wikipedia.org/wiki/Exponentiation_by_squaring *) 
let pow base exponent = 
    if exponent < 0 then invalid_arg "exponent can not be negative" else 
    let rec aux accumulator base = function 
    | 0 -> accumulator 
    | 1 -> base * accumulator 
    | e when is_even e -> aux accumulator (base * base) (e/2) 
    | e -> aux (base * accumulator) (base * base) ((e - 1)/2) in 
    aux 1 base exponent 
+1

Lưu ý rằng đuôi -recursivity không quan trọng đối với hàm logarit trong đầu vào của nó. Làm thế nào bạn có thể thổi đống? Tất nhiên, nếu đuôi đệ quy cho một quan điểm khác nhau cho thấy một cái gì đó thú vị về mã, hoặc làm cho nó dễ dàng hơn để đọc, sau đó nó vẫn có thể được thú vị. – gasche

+0

@gasche bạn nói đúng. Mã này không có ý nghĩa đối với các số nguyên 63 hoặc 31 bit. Một thuật toán như thế sẽ có ý nghĩa đối với các số có độ chính xác tùy ý. – Halst

+0

"tiết lộ điều gì đó thú vị": Nó nhắc nhở bình luận của bạn, @gasche. :-) – Mars

Các vấn đề liên quan