2013-06-05 1 views
8

Existe-t-il une fonction d'exponentiation d'entier dans OCaml? ** est seulement pour les flotteurs. Bien qu'il semble être la plupart du temps précis, n'y at-il pas une possibilité d'erreurs de précision, quelque chose comme 2. ** 3. = 8. retour faux parfois? Existe-t-il une fonction de bibliothèque pour l'exponentiation d'entier? Je pourrais écrire le mien, mais les soucis d'efficacité entrent dans cela, et aussi je serais surpris s'il n'y a pas déjà une telle fonction.Exponentiation d'entier dans OCaml

Répondre

10

En ce qui concerne la partie à virgule flottante de votre question: OCaml appelle la fonction pow() du système sous-jacent. L'exponentiation à virgule flottante est une fonction difficile à implémenter, mais elle doit seulement être fidèle (c'est-à-dire exacte à Unit in the Last Place) pour que 2. ** 3. = 8. évalue à true, car 8.0 est le seul à l'intérieur d'une ULP du résultat 8 mathématiquement correct.

Toutes les bibliothèques mathématiques doivent (*) être fidèles, vous ne devriez donc pas avoir à vous soucier de cet exemple particulier. Mais not all of them actually are, donc vous avez raison de vous inquiéter.


Une meilleure raison de s'inquiéter serait, si vous utilisez des entiers ou plus larges de 63 bits, que les arguments ou le résultat de l'exponentiation ne peuvent pas être représentés exactement comme flotte OCaml (en fait IEEE 754 numéros à double précision cela ne peut pas représenter 9_007_199_254_740_993 ou 2 + 1). Dans ce cas, l'exponentiation à virgule flottante est un mauvais substitut à l'exponentiation d'un entier, non pas à cause d'une faiblesse dans une implémentation particulière, mais parce qu'elle n'est pas conçue pour représenter exactement des entiers aussi grands.


(*) Une autre référence amusant à lire à ce sujet est « A Logarithm Too Clever by Half » par William Kahan.

+0

L'exponentiation en virgule flottante est-elle aussi rapide que l'exponentiation d'entier, étant donné que les arguments sont les mêmes? Aussi, juste pour être clair, est votre déclaration que l'exponentiation en virgule flottante devrait être fidèle vrai pour tous les entiers a, b pour lesquels -2^30 ≤ a^b <2^30, ou juste pour mon exemple particulier de 2 3 et 8? – user2258552

+0

@ user2258552 En ce qui concerne la vitesse: l'exponentiation en virgule flottante est probablement plus lente qu'une expression entière bien écrite. En ce qui concerne la signification de "devrait": une fonction élémentaire fidèle est précise pour un ULP à travers son domaine de définition. Toutes les bibliothèques devraient être fidèles parce que c'est un compromis raisonnable entre le coût de calcul et la précision qui rendrait tout le monde heureux. La précision à 0,5 ULP est un peu trop à attendre de toutes les bibliothèques à cause du dilemme de la table, mais la précision à 1 ULP est réalisable à un coût raisonnable. (mais, encore une fois, pow() est l'une des fonctions élémentaires les plus difficiles) –

+0

En ce qui concerne la vitesse: à la lumière de cela, il est très peu logique de ne pas inclure une fonction d'exponentiation entière dans la bibliothèque standard ... – user2258552

19

Pas dans la bibliothèque standard. Mais vous pouvez facilement en écrire un vous-même (en utilisant exponentiation by squaring pour être rapide), ou réutiliser une bibliothèque étendue qui fournit ceci. En Batteries, il s'agit de Int.pow.

Voici une mise en œuvre proposée:

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) 

S'il y a un risque de débordement parce que vous manipulez de très grands nombres, vous devriez probablement utiliser une bibliothèque grand entier tel que Zarith, qui fournit toutes sortes des fonctions d'exponentiation.

(Vous pouvez avoir besoin du « exponentiation modulaire », calcul (a^n) mod p, ce qui peut être fait d'une manière qui évite les débordements en appliquant le mod dans les calculs intermédiaires, par exemple dans la fonction pow ci-dessus.)

+3

Grande réponse. Malheureusement, je ne pouvais choisir qu'une seule meilleure réponse: /. En outre, je ne suis pas convaincu que ce soit toujours le moyen le plus rapide d'implémenter l'exponentiation d'entiers. En fait, je pense qu'il y a une question de Project Euler (que je n'ai pas encore résolu) dans ce sens. Je pense vraiment que l'exponentiation d'entier devrait être ajoutée à la bibliothèque standard. Même si ce n'est pas plus efficace (ce dont je ne suis pas sûr est vrai), c'est une chose assez courante et la conversion et la déconvolution des flotteurs sont ennuyantes. Bien sûr, l'importation d'une bibliothèque n'est pas difficile, mais il n'y a aucune raison que cela ne soit pas standard. – user2258552

+2

Eh bien, si vous avez de meilleures idées sur la façon de mettre en œuvre une implémentation d'entiers dans le cas général, n'hésitez pas à suggérer une implémentation. – gasche

+0

@ user2258552 Je ne suis pas d'accord avec votre hypothèse que l'exponentiation d'entiers est si commune. En pratique, vous travaillez presque toujours avec de petits exposants fixes, ou vous avez besoin d'une arithmétique de précision arbitraire comme suggéré par gasche. TL; DR: Arrêtez de croire que vous avez besoin d'une exponentiation d'entier sur des entiers de précision fixe et réalisez que vous avez besoin d'une bibliothèque arithmétique de précision arbitraire. –

3

Voici une autre application qui utilise exponentiation par élévation au carré (comme celui fourni par @gasche), mais celui-ci est récursive

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

-recursivité n'a pas d'importance pour une fonction logarithmique dans son entrée. Comment pourriez-vous jamais souffler la pile? Bien sûr, si la récursivité de la queue donne un point de vue différent qui révèle quelque chose d'intéressant sur le code, ou le rend plus facile à lire, alors cela peut toujours être intéressant. – gasche

+0

@gasche vous avez raison. Ce code n'a aucun sens pour les entiers 63 ou 31 bits. Un tel algorithme aurait du sens pour les nombres de précision arbitraire. – Halst

+0

"révèle quelque chose d'intéressant": Il a provoqué votre commentaire, @gasche. :-) – Mars