Esiste una funzione per l'esponenziazione di interi in OCaml? ** è solo per i galleggianti. Anche se sembra essere per lo più accurato, non c'è una possibilità di errori di precisione, qualcosa come 2. ** 3. = 8. restituendo false a volte? Esiste una funzione di libreria per l'esponenziazione di interi? Potrei scrivere il mio, ma le preoccupazioni sull'efficienza arrivano a questo, e sarei sorpreso se non ci fosse già una funzione del genere.Esponenziazione numero intero in OCaml
risposta
Per quanto riguarda la parte in virgola mobile della tua domanda: OCaml chiama la funzione pow()
del sistema sottostante. L'esponenziazione a virgola mobile è una funzione difficile da implementare, ma deve essere solo fedele (ovvero, esatta a uno Unit in the Last Place) per rendere 2. ** 3. = 8.
valutare su true
, perché 8.0
è l'unico float
all'interno di un ULP del risultato matematicamente corretto 8.
Tutte le librerie matematiche devono (*) essere fedele, quindi non dovresti preoccuparti di questo particolare esempio. Ma not all of them actually are, quindi hai ragione di preoccuparti.
una ragione migliore per preoccuparsi sarebbe, se si utilizza 63-bit interi o più ampi, che gli argomenti o il risultato della esponenziazione non possono essere rappresentati esattamente come OCaml galleggia (in realtà IEEE 754 numeri a precisione doppia che non può rappresentare 9_007_199_254_740_993
o 2 + 1). In questo caso, l'esponenziazione in virgola mobile è un cattivo sostituto per l'esponenziazione di interi, non a causa di un punto debole in un'implementazione particolare, ma perché non è progettato per rappresentare esattamente interi così grandi.
(*) Un altro riferimento divertente leggere su questo argomento è “A Logarithm Too Clever by Half” di William Kahan.
Non nella libreria standard. Ma puoi facilmente scriverne uno da solo (usando exponentiation by squaring per essere veloce), o riutilizzare una libreria estesa che fornisce questo. In Batteries, è Int.pow.
Qui di seguito è una proposta di attuazione:
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)
Se v'è un rischio di troppo pieno, perché si sta manipolando numeri molto grandi, probabilmente si dovrebbe utilizzare una libreria grande intero tale da Zarith, che fornisce tutti i tipi delle funzioni di esponenziazione.
(potrebbe essere necessario il "elevamento a potenza modulare", calcolando (a^n) mod p
; questo può essere fatto in modo tale da evitare overflow applicando il mod nei calcoli intermedi, ad esempio nella funzione pow
sopra.)
Grande risposta. Sfortunatamente, ho potuto scegliere solo una risposta migliore: /. Inoltre, non sono convinto che questo sia sempre il modo più veloce per implementare l'esponenziazione di interi. In effetti, penso che ci sia una domanda di Eulero di progetto (che devo ancora risolvere) in questo senso. Penso davvero che l'exponentiation intero dovrebbe essere aggiunto alla libreria standard. Anche se non è più efficiente (cosa che non sono sicuro sia vero), è una cosa abbastanza comune da aver bisogno e dover convertire e deconvertire dai float è fastidioso. Naturalmente l'importazione di una libreria non è difficile, ma non c'è motivo per cui questo non dovrebbe essere standard. – user2258552
Bene, se avete idee migliori su come implementare l'implementazione di interi nel caso generale, sentitevi liberi di suggerire un'implementazione. – gasche
@ user2258552 Non sono d'accordo con la vostra premessa che l'esponenziazione di interi è così comune. In pratica, lavori quasi sempre con piccoli esponenti fissi, o * hai bisogno * di un'aritmetica arbitraria di precisione come suggerito da gasche. TL; DR: Non credere di aver bisogno dell'esponenziazione di interi su valori di precisione fissi e renditi conto che hai bisogno di una libreria aritmetica di precisione arbitraria. –
Ecco un'altra applicazione che utilizza exponentiation elevando al quadrato (come quello fornito da @gasche), ma questo è ricorsiva in coda
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
Nota quella coda -la ricorsività non ha importanza per una funzione logaritmica nel suo input. Come hai mai potuto saltare lo stack? Ovviamente, se la ricorsività della coda offre un punto di vista diverso che rivela qualcosa di interessante sul codice o lo rende più facile da leggere, può comunque essere interessante. – gasche
@gasche hai ragione. Questo codice non ha senso per numeri interi a 63 o 31 bit. Un algoritmo del genere avrebbe senso per numeri arbitrari di precisione. – Halst
"rivela qualcosa di interessante": ha suggerito il tuo commento, @gasche. :-) – Mars
L'esponenziazione in virgola mobile è veloce quanto il numero intero in esponenziazione dato che gli argomenti sono gli stessi? Inoltre, tanto per essere chiari, è la tua affermazione che l'esponenziazione in virgola mobile dovrebbe essere fedele per tutti gli interi a, b per i quali -2^30 ≤ a^b <2^30, o solo per il mio particolare esempio di 2 3 e 8? – user2258552
@ user2258552 Per quanto riguarda la velocità: l'esponenziazione in virgola mobile è probabilmente più lenta di una intera intera ben scritta. Riguardo al significato di "dovrebbe": una fedele funzione elementare è accurata per un ULP attraverso il suo dominio di definizione. Tutti i libms dovrebbero essere fedeli perché è un ragionevole compromesso tra costo computazionale e accuratezza che renderebbe felici tutti. La precisione a 0,5 ULP è un po 'troppo prevedibile per tutte le libms a causa del dilemma del produttore di tabelle, ma l'accuratezza di 1 ULP è ottenibile a costi ragionevoli. (ma, ancora una volta, pow() è una delle funzioni elementari più difficili) –
Per quanto riguarda la velocità: alla luce di ciò, quindi ha davvero molto poco senso non includere una funzione di elevazione del numero intero nella libreria standard ... – user2258552