2010-09-24 4 views
4

Je cherche un moyen de mettre en place une fonction logistique rapide. La définition classique de la fonction logistique est la suivante:Fonction logistique rapide

y(x) = 1/(1 + (1/e^x)) où^est l'exponentiation.

ou également: y(x) = (e^x)/(e^x + 1)

Cependant, ma fonction logistique particulière a une base E à la place de l'e, de sorte que:

y(x) = E^x/(E^x + 1) 

E et x dans mon cas sont des nombres entiers de 32 bits, à virgule fixe dans base 2 de type 16.16. E est aussi proche que possible de la constante réelle e.

Quel est l'algorithme le plus rapide pour une telle fonction? Je préférerais pas de tables de recherche. Quelque chose comme des manipulations de bits devrait être parfait.


MISE À JOUR:

Intuitivement, je me sens qu'il existe une méthode très simple et rapide, basé sur la formule d'Euler:

e^(i*x) = cos(x) + i*sin(x)

où i est l'unité imaginaire.

+0

Est-ce que la portabilité question? Il existe des moyens rapides d'évaluer de telles fonctions en piratant les flotteurs IEEE, mais ils ne sont pas beaux et ne sont pas portables. – Doug

+0

@Pas que cela ait de l'importance, mais IEEE est correct, car la plupart des processeurs à l'échelle du serveur le supportent – psihodelia

+0

Après les modifications, votre question est complètement différente de celle d'origine. J'ai supprimé ma réponse car elle est devenue non pertinente après vos modifications. – qrdl

Répondre

2

puissances ne sont pas facilement convertir en décalages de bits, car

E^x = 2^log2(E^x) = 2^(x*log2(E)) 

et cela vous donnera un nombre fractionnaire de bits à changer. Vous pouvez calculer x * log2 (E) puis décomposer la puissance en une somme de décalages de bits séparés, mais il est peu probable que cela soit aussi rapide qu'une table de recherche suivie de quelques itérations de la méthode de Newton. Là encore, cela dépend du coût de vos opérations arithmétiques à virgule fixe.

+0

Je pense qu'il y a une solution basée sur une série de Taylor, car e^x peut être représenté comme une série: sum ((x^n)/n!) Si n va de 0 à + Inf. – psihodelia

+0

La série Taylor ne converge pas très rapidement pour certaines valeurs; vous êtes mieux avec des changements de bits. Si vous pouvez garantir que vous serez dans la gamme de | x | >> 1, alors vous pouvez calculer e^- | x | assez efficacement de cette façon. –

8

Gardez les débordements et débordements à l'esprit. Le code exp(x)/(1 + exp(x)) débordera pour les grandes valeurs positives de x même si le résultat devrait être d'environ 1. Vous pouvez éviter cela en utilisant 1/(1 + exp(-x)). Cela va à 0 pour valeurs négatives de x, mais cela peut être OK selon votre contexte puisque le résultat exact est proche de zéro dans ce cas. Si votre code est appelé pour de grandes valeurs positives ou négatives de x, vous pouvez gagner un peu de temps en retournant 0 ou 1 pour les valeurs qui vont être représentées comme 0 ou 1 et évitez d'appeler exp. Donc, votre code pourrait être quelque chose comme

if (x > positive cutoff) return 1; 
if (x < negative cutoff) return 0; 
return 1/(1 + exp(-x)) 

Que ce soit effectivement plus efficace dépend de votre environnement et à quelle fréquence vous obtenez des arguments passés les valeurs de coupure.

+1

Je n'aime pas si. Un bon algorithme devrait donner un résultat correct, soit 0 soit 1 pour les valeurs extrêmes. – psihodelia

+0

Les valeurs extrêmes dépendent de la précision requise. – Wok

+5

Le code '1/(1 + exp (-x))' fonctionnera. Aucune instruction "if". Il gère très bien les arguments extrêmes. Mais les instructions 'if' pourraient économiser un peu de temps d'exécution. –

3

Je vais parler de flotteurs, pas d'ints, parce que c'est là où la technologie semble être. La fonction standard de ce type de calcul consiste à utiliser une logique de casse spéciale pour représenter la fonction dans une certaine plage [a, b], en l'approximant par une fonction rationnelle - un polynôme divisé par un autre, puis corrigez tout ce que vous avez à faire pour réduire la portée. La source de exp (x) à http://www.netlib.org/fdlibm/e_exp.c semble suivre ce modèle.

Ceci vous donne une approximation de exp (x) de la forme a (x)/b (x). Vous voulez réellement 1/(1 + exp (-x)). Vous devriez être capable de réorganiser l'implémentation de a (x)/b (x) afin de l'amener à faire b (-x)/(a ​​(-x) + b (-x)), de sorte que vous ayez juste une instruction de division, à l'intérieur de l'exp (x) réarrangée, au lieu d'une instruction de division à l'intérieur et une à l'extérieur. Cela vous permettra d'économiser quelque chose, en fonction du coût de la division sur votre machine - cela peut être perceptible si votre boucle interne est réellement composée de 90% d'appels à la fonction logistique. Le schéma de réduction de gamme et d'approximation rationnelle est si fermement ancré que je doute que vous fassiez beaucoup mieux sans sacrifier beaucoup de précision, bien que si vous recourez à des nombres entiers, vous pouvez être prêt à le faire.

J'ose dire que vous pourriez transférer cela dans le monde des points fixes si vous avez travaillé assez dur. J'ai peur d'avoir tendance à revenir à l'interpolation linéaire entre les valeurs d'une table, c'est-à-dire en supposant que je ne pourrais pas trouver un moyen de sortir la fonction logistique de la boucle interne.

+0

Utiliser cette fonction rationnelle me semble être une très bonne idée, mais j'imagine que trouver les paramètres polynomiaux comme ceci serait sous-optimal --- pas nécessairement la meilleure précision pour la taille polynomiale. Quelqu'un at-il une référence pour les paramètres de fonction rationnelle calculée spécifiquement pour approximer la fonction logistique, et non l'exponentielle? – dividebyzero