Well, I wouldn't have used such colorful language, but I share Prof. Fateman's skepticism on the utility of lgamma for computing factorials.
I tried the expression as given, (see attachment, if you want to try yourself or point out errors) and it begins to diverge from the correct value at n=17 (which is a bit more than 48 bits for the integer). Interestingly, if I switch to gamma(), instead of exp(lgamma()), it doesn't diverge until n=23, which corresponds to a 75 bit number. (Presumably there are a lot of zeros at the bottom of a factorial.) So, that *would* work if you were in C and restricted to 64 bit long longs. But, as Fateman points out, with that restriction you might as well keep a short table. Of course, in Python we expect thousands of bits to work transparently, so I would definitely avoid converting from lgamma to an int. BTW, I don't understand the "far superior" implementation of fact later in the posting. When I try it, I just get regular linear recursion. Presumably there is a typo in my translation or in the original posting. On Saturday, May 3, 2014 4:35:41 PM UTC-7, Richard Fateman wrote: > > > > On Tuesday, April 1, 2014 2:58:34 PM UTC-7, Ondřej Čertík wrote: >> >> On Mon, Mar 31, 2014 at 9:14 PM, Matthew Rocklin <mroc...@gmail.com> >> wrote: >> > http://www.evanmiller.org/mathematical-hacker.html >> > >> > I reference that blog post pretty often. I fully intend to reference >> it >> > again in my talk (if it is accepted). >> > > That's unfortunate. I was totally unacquainted with Evan Miller. I read > this particular > blog entry. I did not follow any of the links, but I think that the essay > is a piece of crap. > It is uninformed, shallow, and, I daresay, just wrong. I hope you > re-think referring to it. > > > >> > >> > The interesting thing about the Factorial / Gamma / loggamma example is >> that >> > to find the solution you need to find someone who knows both that n! = >> > Gamma(n+ 1) and who knows that a loggamma routine is commonly found in >> lower >> > level languages. Those bits of information are usually held by >> different >> > experts. Ondrej said "Of course, that's obvious" when I first reposted >> the >> > article on G+. >> > > Well, let me quote from the article.. > " > *if it is ever necessary to compute a factorial, a programmer should be > taught to take advantage of the system's log-gamma function, as the > following C code does: * > > * > long int fac(unsigned long int n) { > return lround(exp(lgamma(n+1))); > } > * > > * Again, no recursion is required as long as one...* > > Here are a few things that are wrong. Rounding an approximate floating > point > number to a integer may not get you the exact integer factorial you want. > The program will certainly not work for arbitrary unsigned long int n > because > the exp() will overflow. Say certainly by n=175. > If you want a floating-point approximation to any factorial that can be > represented as an IEEE double, you could do it by checking if N<175 and > then index into an array of length 175. [I'd check that 175 more carefully > if I were really doing this. And the number would change a little for > 64-bit ints. > > If you want exact integer factorials on a 32 bit computer, you only need > an array of 12, since > 13! > 2^32. > > So the program is stupid. Why? Because the author is mathematically > and computationally ignorant. (And seemingly quite full of himself.) > > Say you want to solve the much more interesting problem of computing > factorials > of any size integer. Then you might see > http://www.luschny.de/math/factorial/FastFactorialFunctions.htm > which tells how it might be done better. > > Here's a far superior factorial, in lisp, and yes it is recursive. I > suppose you > could write it iteratively, but why bother. A Lisp compiler will do that > for you. > > (defun fact(n)(f n 1)) > > (defun f (n m) (if (<= n 1) 1 (* n (f (- n m) m)))) > > It is used in some computer algebra systems. > > > The snide characterizations that lisp programmers know more or less > mathematics > or that what they know is more or less relevant is just junk. > > Of the 3 people quoted -- Raymond, Graham, Yegge... > I've never heard of Yegge; I think that Graham is smart, and Raymond just > says things > to be, uh, smart-ass. > > > > > > >> >> That's funny, I forgot that I said that and just had the same reaction. >> >> However, we are still missing rational function approximation in SymPy >> or mpmath. >> That's what is used to implement things like log_gamma or erf (error >> function) in >> Fortran or C. So I have just created an issue for it: >> >> https://github.com/sympy/sympy/issues/7359 >> >> and I spent time explaining exactly how it works in it and giving >> examples, >> including for example the implementation of erf() in gfortran. >> >> Aaron, you were asking about examples of numerical cancellation. I >> worked out one in the >> issue as well. >> >> Ondrej >> >> > >> > You're right that this is similar to my last talk. The last one though >> was >> > mostly about an application (numerical linear algebra). I actually >> want to >> > talk a bit more about the philosophy and some of the more abstract >> tools >> > that people might actually use. Your first impression is a valuable >> one >> > though, I should go through my last talk and make sure that I'm not >> > repeating too much that shouldn't be repeated. >> > >> > >> > On Mon, Mar 31, 2014 at 6:03 PM, Aaron Meurer <asme...@gmail.com> >> wrote: >> >> >> >> That's a good point. One of the nicest things about symbolics, when >> you >> >> can get it, is that it can make things drastically more efficient by >> doing >> >> mathematical simplifications. Evaluating integrals symbolically is a >> nice >> >> example of this (especially for SymPy, which has some pretty nice >> algorithms >> >> to compute definite integrals). >> >> >> >> I'm reminded of a popular blog post (I can't find a link right now) >> about >> >> how know math is important for programmers. It has the example of how >> all >> >> these programming languages show how they they compute factorial, and >> how >> >> tail recursion can make it linear or whatever, but the actual best way >> to >> >> compute it is to use loggamma, which gives the answer in constant >> time. >> >> >> >> Aaron Meurer >> >> >> >> >> >> On Mon, Mar 31, 2014 at 7:51 PM, Tim Lahey <tim....@gmail.com> wrote: >> >>> >> >>> >> >>> >> >>> On 31 Mar 2014, at 20:29, Aaron Meurer wrote: >> >>> >> >>>> On Mon, Mar 31, 2014 at 11:32 AM, Matthew Rocklin >> >>>> <mroc...@gmail.com>wrote: >> >>>> >> >>>>> I like that you emphasized the utility for numerics, I think that >> this >> >>>>> is >> >>>>> likely to be a selling point for the SciPy crowd. >> >>>>> >> >>>> >> >>>> Yes, this was very intentional. I may need some help gathering up >> some >> >>>> nice >> >>>> motivating examples if this is accepted. >> >>> >> >>> >> >>> One motivating example for me is the integration of products of >> functions >> >>> over areas and volumes. For finite elements, you'll get products of >> pairs of >> >>> trial functions (usually polynomials). It's even more useful for >> products of >> >>> trig functions. Performing the integration of any of theses is easy >> enough >> >>> with numerical integration, but it's much more efficient to calculate >> the >> >>> integrals symbolically and then perform the evaluation for each >> element. >> >>> >> >>> Cheers, >> >>> >> >>> Tim. >> >>> >> >>> >> >>> -- >> >>> You received this message because you are subscribed to the Google >> Groups >> >>> "sympy" group. >> >>> To unsubscribe from this group and stop receiving emails from it, >> send an >> >>> email to sympy+un...@googlegroups.com. >> >>> To post to this group, send email to sy...@googlegroups.com. >> >>> Visit this group at http://groups.google.com/group/sympy. >> >>> To view this discussion on the web visit >> >>> >> https://groups.google.com/d/msgid/sympy/AE5C61B5-8C69-49AE-BAFF-85E5AC924F8F%40gmail.com. >> >> >> >>> >> >>> For more options, visit https://groups.google.com/d/optout. >> >> >> >> >> >> -- >> >> You received this message because you are subscribed to the Google >> Groups >> >> "sympy" group. >> >> To unsubscribe from this group and stop receiving emails from it, send >> an >> >> email to sympy+un...@googlegroups.com. >> >> To post to this group, send email to sy...@googlegroups.com. >> >> Visit this group at http://groups.google.com/group/sympy. >> >> To view this discussion on the web visit >> >> >> https://groups.google.com/d/msgid/sympy/CAKgW%3D6K8F9weBP-7yjpzrN9hQ5FR54TK_QdCPqbzBdwo20zKdg%40mail.gmail.com. >> >> >> >> >> >> For more options, visit https://groups.google.com/d/optout. >> > >> > >> > -- >> > You received this message because you are subscribed to the Google >> Groups >> > "sympy" group. >> > To unsubscribe from this group and stop receiving emails from it, send >> an >> > email to sympy+un...@googlegroups.com. >> > To post to this group, send email to sy...@googlegroups.com. >> > Visit this group at http://groups.google.com/group/sympy. >> > To view this discussion on the web visit >> > >> https://groups.google.com/d/msgid/sympy/CAJ8oX-HZABscP43PDuoAeWhcpBYW2w39G8qPx0%2BZCbKcYbJdJg%40mail.gmail.com. >> >> >> > >> > For more options, visit https://groups.google.com/d/optout. >> > -- You received this message because you are subscribed to the Google Groups "sympy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sympy+unsubscr...@googlegroups.com. To post to this group, send email to sympy@googlegroups.com. Visit this group at http://groups.google.com/group/sympy. To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/f999c5c5-b99b-46bf-a40b-5d1eb5ff11e6%40googlegroups.com. For more options, visit https://groups.google.com/d/optout.
import math def fact(n): """Factorial algorithm from R. Fateman sympy group post. Gives correct values, but seems to result in ordinary linear behavior.""" return f(n, 1) def f(n, m): """Semantics?""" if n<=1: return 1 return n*f(n-m, m) def miller_fac(n): """Factorial approximation, based on Evan Miller blog post.""" return int(math.exp(math.lgamma(n+1)) + 0.5) def miller2_fac(n): """Slight modification - use gamma instead of lgamma.""" return int(math.gamma(n+1) + 0.5) def test_factorial_implementations(fimpl, f_max): for n in xrange(f_max+1): base = math.factorial(n) # this should be exact f2 = fimpl(n) if base != f2: print "Discrepancy found for", n, base, f2 print "lg of factorial", math.log(base, 2) if __name__ == "__main__": print "Original expression, using exp(lgamma)" test_factorial_implementations(miller_fac, 20) print "Using Gamma" test_factorial_implementations(miller2_fac, 25)