FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   PreferencesPreferences   Log in to check your private messagesLog in to check your private messages   Log inLog in 
Forum index » Science and Technology » Math » Symbolic
factorial of largish numbers
Post new topic   Reply to topic Page 2 of 2 [27 Posts] View previous topic :: View next topic
Goto page:  Previous  1, 2
Author Message
Klueless
science forum beginner


Joined: 30 May 2005
Posts: 19

PostPosted: Thu Apr 06, 2006 4:22 am    Post subject: Re: factorial of largish numbers Reply with quote

"Richard J. Fateman" <fateman@eecs.berkeley.edu> wrote in message news:e10udn$2jgi$1@agate.berkeley.edu...
Quote:
http://www.cs.berkeley.edu/~fateman/papers/factorial.pdf

There is a lot here you should look at.
<http://www.luschny.de/math/factorial/FastFactorialFunctions.htm>
<http://www.luschny.de/math/factorial/conclusions.html>
Back to top
Richard Fateman
science forum Guru Wannabe


Joined: 03 May 2005
Posts: 181

PostPosted: Thu Apr 06, 2006 4:59 am    Post subject: Re: factorial of largish numbers Reply with quote

I looked at the algorithm in gmp, and timed calling it directly from Lisp.
It is
somewhat faster than running a good factorial algorithm in Lisp (There is no
reason for it to be much faster... it is still doing the same big
multiplies, and
doesn't really have a better algorithm).

As I recall it makes some number of buckets and tries to accumulate
products in all of them of about equal size. (Like the version in Maxima.)
This is not too bad, but presumably not as good as Peter Luschny's best,
which requires interleaving a prime number sieve with the calculation,
similar
to what Tim Daly suggests.

I tried running the version (in Lisp)of Lucshny's posted earlier, but
modified to run
in Lisp + GMP. It is good, but not as good as some others, probably
because it requires creating lots of GMPZ numbers to return from the
"prod" routine, instead of storing those numbers destructively on top
of each other.

RJF


"Robert Israel" <israel@math.ubc.ca> wrote in message
news:e122tl$ite$1@nntp.itservices.ubc.ca...
Quote:
In article <e10udn$2jgi$1@agate.berkeley.edu>,
Richard J. Fateman <fateman@eecs.berkeley.edu> wrote:
I invite comments on a paper posted at
http://www.cs.berkeley.edu/~fateman/papers/factorial.pdf

on computing factorial of, say, 20,000 or more.
Programs in lisp and some comparison with Maple and
Mathematica. are included.

I've used (Allegro 7.0) Lisp + GMP for best times, but maybe
you have other ideas.
Thanks
RJF

I believe that what current versions of Maple use is the GMP factorial
function mpz_fac_ui: see <http://www.swox.com/gmp/gmp-man-4.2.pdf>.
It's quite fast: 40000! took only 0.093 seconds on my machine (Pentium
4, 3 GHz running Maple 10.03 Classic under Windows XP). Section
16.7.2 of that document tells about the algorithm used.

Robert Israel israel@math.ubc.ca
Department of Mathematics http://www.math.ubc.ca/~israel
University of British Columbia Vancouver, BC, Canada

Back to top
Peter Luschny
science forum beginner


Joined: 13 May 2005
Posts: 36

PostPosted: Thu Apr 06, 2006 7:55 am    Post subject: Re: factorial of largish numbers Reply with quote

Rob Warnock wrote:

Quote:
This will not run in Common Lisp as as written, because of the
WHILE, and even when that is corrected will give incorrect answers
[e.g., (SPRFACT 5) => 3] because the default READTABLE-CASE in
Common Lisp is :UPCASE. If you put this line at the top of your
program:

(setf (readtable-case *readtable*) :invert)

and then define WHILE as follows [yeah, yeah, I know, using DO
would be safer]:

(defmacro while (bool &body body)
`(loop while ,bool do ,@body))

and only *THEN* type [or paste] the above definitions for SPRFACT/PROD,
then it will work and give correct answers.

Thank you! Yes, I ran into trouble with ‘while’, as you expected.

LispWorks said: Error: ‘Undefined function WHILE called with
arguments (T 0 0 2 1 -1 -1 NIL)’.

On the other hand Allegro CL said: Error: ‘Attempt to make a
function definition for the name while as a macro. This name
is in the EXCL package and redefining it is a violation for
portable programs.’

But with your hints above things now work.

Quote:
But on CMUCL it's more than twice as slow [0.21f0 s versus 0.1f0 s]
than the previous K/FACT version mentioned in this thread. [And, yes,
I did compile it.]

Therefore I compared things with Java. For some largish numbers.

SPRFACT K/Fact
N = 32,000 0.2 0.3
N = 256,000 3.9 6.4
N = 1,024,000 20.1 34.0
N = 2,048,000 44.8 76.0

All times in seconds.

For more benchmark results, also showing a relative
ranking including the more powerful prime algorithms:
http://www.luschny.de/math/factorial/FactorialBenchmark.html

Quote:
I suspect that if you rewrite it without using
special variables it might improve [e.g., perhaps by making PROD be
an FLET inside the LET binding within SPRFACT].

Thank you. Regrettably I am not proficient enough with Lisp to try
to improve on these fine points.

But yes, K/FACT is an enjoyable tiny recursion I haven't seen before.
I will include it in my collection of factorial algorithms.

But as far as I can see, it has not the asymptotic power of the
split recursion algorithm. For a better understanding of the latter
I add the trace of a small example (perhaps to prompt a more
Lisp-like implementation of the idea?) :

Compute 30!

init: p=1, r=1, then loop:

p_1 = 3
p = p * p_1 = 3
r = r * p = 3

p_2 = 5 * 7
p = p * p_2 = 105
r = r * p = 315

p_3 = 9 * 11 * 13 * 15
p = p * p_3 = 2027025
r = r * p = 638512875

p_4 = 17 * 19 * 21 * 23 * 25 * 27 * 29
p = p * p_4 = 6190283353629375
r = r * p = 3952575621190533915703125

return r * 2^26 = 265252859812191058636308480000000

Do the computation of the partial products p_n by binary
splitting (i.e. like a binary tree) to ensure a balanced load.

Regards Peter
Back to top
Frank Buss
science forum beginner


Joined: 06 Apr 2006
Posts: 3

PostPosted: Thu Apr 06, 2006 8:31 am    Post subject: Re: factorial of largish numbers Reply with quote

Rob Warnock wrote:

Quote:
perhaps by making PROD be
an FLET inside the LET binding within SPRFACT].

you need LABELS, because PROD is a recursive function.

(defun sprfact (n)
(cond ((< n 0) (error "bad arg ~s to sprfact" n))
((< n 2) 1)
(t
(let ((p 1)
(r 1)
(nn 1)
(log2n (floor (log n 2)))
(h 0)
(shift 0)
(high 1)
(len 0))
(labels ((prod (n)
(let ((m (ash n -1)))
(cond ((= m 0) (incf nn 2))
((= n 2) (* (incf nn 2)
(incf nn 2)))
(t (* (prod (- n m))
(prod m)))))))
(loop while (/= h n) do
(incf shift h)
(setf h (ash n (- log2n)))
(decf log2n)
(setf len high)
(setf high (if (oddp h) h (1- h)))
(setf len (ash (- high len) -1))
(cond ((> len 0)
(setf p (* p (prod len)))
(setf r (* r p)))))
(ash r shift))))))

But it still doesn't look very Lisp-like. I didn't understand the
algorithm, so I can't write a better implementation :-)

--
Frank Buss, fb@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
Back to top
Peter Luschny
science forum beginner


Joined: 13 May 2005
Posts: 36

PostPosted: Thu Apr 06, 2006 10:28 am    Post subject: Re: factorial of largish numbers Reply with quote

Frank Buss wrote:

Quote:
you need LABELS, because PROD is a recursive function.
(defun sprfact (n)

Thank you, Frank. As far as I can judge this runs fine!

Quote:
But it still does not look very Lisp-like.

Well, I have been told Lisp is hard, isn't it? ;-)

Quote:
I didn't understand the
algorithm, so I can't write a better implementation Smile

Look at the trace I gave in my other posting.

I am now learning Lisp since 32 hours. However, I do not
think this qualifies me to suggest an implementation
to this group ;-)

Regards Peter
Back to top
Mark Lawton
science forum beginner


Joined: 22 Mar 2006
Posts: 11

PostPosted: Thu Apr 06, 2006 12:07 pm    Post subject: Re: factorial of largish numbers Reply with quote

Hi Richard,

Not really a serious comment - just interesting to note that the
following Maxima function computes 20,000! correctly to 9 digits:

fact(x) := (y: sum(float(log(n)),n,1,x),
y: float(y/log(10)),
print(10^(y-fix(y)), "e", fix(y)),"")$

If I set floating point precision to 100, and rewrite the function as a
bigfloat, it computes it correctly to 92 digits:

fact(x) := (y: sum(bfloat(log(n)),n,1,x),
y: bfloat(y/log(10)),
print(10^(y-fix(y)), "e", fix(y)),"")$

So if you don't need your factorial's full precision, you can use this
method. I remembered this trick of obtaining factorials by summing the
logs from the early 1980s, when the only tool I had was my TI-58C
programmable calculator.

Richard J. Fateman wrote:
Quote:
I invite comments on a paper posted at
http://www.cs.berkeley.edu/~fateman/papers/factorial.pdf

on computing factorial of, say, 20,000 or more.
Programs in lisp and some comparison with Maple and
Mathematica. are included.

I've used (Allegro 7.0) Lisp + GMP for best times, but maybe
you have other ideas.
Thanks
RJF
Back to top
Mark Lawton
science forum beginner


Joined: 22 Mar 2006
Posts: 11

PostPosted: Thu Apr 06, 2006 12:31 pm    Post subject: Re: factorial of largish numbers Reply with quote

I missed the obvious point that there is no need to use logs when using
a CAS - sorry.

fact(x) := (y: 1,
for i: 2 thru x do
y: bfloat(y * i),
y)$

Using the above Maxima function with floating point precision set to
100, the result of 20,000! is produced in 0.5 seconds (circa 2 ghz PC)
and all 100 digits produced are correct.

Mark Lawton wrote:
Quote:
Hi Richard,

Not really a serious comment - just interesting to note that the
following Maxima function computes 20,000! correctly to 9 digits:

fact(x) := (y: sum(float(log(n)),n,1,x),
y: float(y/log(10)),
print(10^(y-fix(y)), "e", fix(y)),"")$

If I set floating point precision to 100, and rewrite the function as a
bigfloat, it computes it correctly to 92 digits:

fact(x) := (y: sum(bfloat(log(n)),n,1,x),
y: bfloat(y/log(10)),
print(10^(y-fix(y)), "e", fix(y)),"")$

So if you don't need your factorial's full precision, you can use this
method. I remembered this trick of obtaining factorials by summing the
logs from the early 1980s, when the only tool I had was my TI-58C
programmable calculator.
Back to top
Richard Fateman
science forum Guru Wannabe


Joined: 03 May 2005
Posts: 181

PostPosted: Thu Apr 06, 2006 5:53 pm    Post subject: Re: factorial of largish numbers Reply with quote

Thanks for all the comments!

The sparse recursive factorial algorithm as Peter L wrote in Java
does a lot of allocation of numbers (as bignums) when they
are really just small numbers. A more efficient way of doing
could be to allocate two bignums for p and r and overwrite them. The
"API" offered by GMP allows you to do this overwriting, and the Lisp
version I provide allows access to these destructive versions.
(The Python implementation, according to at least one note I
found by Googling, did not provide such access.) Anyway, here
is a Lisp version for sparse recursive, with a modification of
the "prod" routine to take another argument, ans. If the old prod(n)
computed a value C(n), the new gprod(n,ans) produces a result
like ans:=C(n)*ans, destroying the old value of ans.
The ordering and size of multiplications is perhaps not as good
in this re-working. I believe it is just an unrolling of the recurrence
f(n,m):=f(n,2*m)*f(n-m,2*m).

This sprfact is not quite as fast as the "k" program, presumably because
it doesn't do as good a job of keeping numbers equally small, and
accumulating
their product "gently". It uses almost no storage except inside the GMP
allocator.

This is the program (it won't run unless you have my gmp library). If
you are familiar with GMP, the programs mpz_.... are named the same as
the GMP entries. I've also changed the naming and the while, and labels to
be
fully ANSI common lisp. I've also prefixed the fixnum operators with
cl:: just in case this might make it compile better. I've overloaded +, *,
etc
with GMP generic functions.


(defun gsprfact(n);;works. in :gmp package
(declare (fixnum n) (optimize (speed 3)(safety 0)) )
(let ((p (into 1)) (r (into 1)) ;initialize by converting 1 into GMPZ
numbers.
(NN 1) (log2n (floor (log n 2)))
(h 0) (shift 0) (high 1) (len 0))
(declare (fixnum log2n h shift high len NN))
(labels ((gprod(n ans)
(declare (fixnum n NN))
(let ((m (ash n -1)))
(declare (fixnum m))
(cond ((cl::= m 0)
(mpz_mul_ui ans ans
(cl::incf NN 2)))
((cl::= n 2)
(mpz_mul_ui ans ans
(cl::* (cl::incf NN 2)
(cl::incf NN 2))))
(t (gprod (cl::- n m) (gprod m ans))))
ans)))
(cond ((< n 0)(error "bad arg ~s to factorial" n))
((< n 2) (gmp::into 1))
(t
(loop while (cl::/= h n) do
(cl::incf shift h)
(setq h (ash n (cl::- log2n)))
(cl::decf log2n)
(setq len high)
(setq high (if (oddp h) h (cl::1- h)))
(setq len (ash (cl::- high len) -1))
(cond ((cl::> len 0)
(gprod len (gmpz-z p));change p
(mpz_mul (gmpz-z r)(gmpz-z r)(gmpz-z p)) )))
(mpz_mul_2exp (gmpz-z r) (gmpz-z r) shift);; arithmetic shift
r)))))

Here's another version, ANSI CL, but less hacking.

(defun sprfact(n)
(declare (fixnum n))
(let ((p 1) (r 1) (NN 1) (log2n (floor (log n 2)))
(h 0) (shift 0) (high 1) (len 0))
(declare (fixnum log2n h shift high len NN))
(labels ((prod(n)
(declare (fixnum n))
(let ((m (ash n -1)))
(cond ((= m 0) (incf NN 2))
((= n 2) (* (incf NN 2)(incf NN 2)))
(t (* (prod (- n m)) (prod m)))))))

(cond ((< n 0)(error "bad arg ~s to factorial" n))
((< n 2) 1)
(t
(loop while (/= h n) do
(incf shift h)
(setf h (ash n (- log2n)))
(decf log2n)
(setf len high)
(setf high (if (oddp h) h (1- h)))
(setf len (ash (- high len) -1))
(cond ((> len 0)
(setf p (* p (prod len)))
(setf r (* r p)))))
(ash r shift))))))
Back to top
Geoffrey Summerhayes
science forum beginner


Joined: 06 Apr 2006
Posts: 1

PostPosted: Thu Apr 06, 2006 6:17 pm    Post subject: Re: factorial of largish numbers Reply with quote

<daly@axiom-developer.org> wrote in message
news:1144275433.752025.9500@u72g2000cwu.googlegroups.com...
Quote:
I suggest a different algorithm using binary arithmetic.
Call it the "trailing zeros" algorithm.

1 = 1
2 = 10 = 1 * 2^1 ==> 2! = 2
3 = 11 = 3 * 2^0 ==> 3! = 3*1*2^1 = 110 = 6
4 = 100 = 1 * 2^2 ==> 4! = 3 * 2^3 = 11000 = 24
5 = 101 = 5 * 2^2 ==> 5! = 5 * 3 * 2^3 = 1111000 = 120
6 = 110 = 3 * 2^1 ==> 6! = 3 * 5 * 3 * 2^4 = 10 1101 0000 = 720
7 = 111 = 7 * 2^0 ==> 7! = 7 * 3 * 5 * 3 * 2^4 = 1 0011 1011 0000 =
5040
8 = 1000 = 1 * 2^3 ==> 8! = 7 * 3 * 5 * 3 * 2^7 = 1 0011 1011 0000 0000
= 40320

notice that we're using shifting to rid the world of the large trailing
zeros
and that certain computations can be see speedup because we're only
keeping significant factor bits. 7! and 8! obviously are the same
computation.

If I understand this right, you're thinking along the lines of:

(defstruct big-fact-num
mantissa
power-of-2)

(defun convert(n)
;; Obviously need something more efficent here
(do ((x n (floor x 2))
(power 0 (incf power)))
((oddp x) (values x power))))

(defun fact(n)
;; Again, simple algorithm
(if (< n 2)
(make-big-fact-num :mantissa 1 :power-of-2 0)
(let ((fact-so-far (fact (1- n))))
(multiple-value-bind (mant-n pow-n)
(convert n)
(incf (big-fact-num-power-of-2 fact-so-far) pow-n)
(setf (big-fact-num-mantissa fact-so-far)
(* (big-fact-num-mantissa fact-so-far) mant-n)))
fact-so-far)))

CL-USER > (fact Cool
#S(BIG-FACT-NUM :MANTISSA 315 :POWER-OF-2 7)

CL-USER > (* 315 (expt 2 7))
40320

???
----
Geoff
Back to top
Richard Fateman
science forum Guru Wannabe


Joined: 03 May 2005
Posts: 181

PostPosted: Thu Apr 06, 2006 8:42 pm    Post subject: Re: factorial of largish numbers Reply with quote

The algorithms given by Peter Luschny cover a lot of ground.

I think Tim's observation is basically that if k(n,1) == n!, then

k(n,m) := if even(n) then 2^n*k(n/2,m)*k(n-m,m) // rule for (2*n)!
else n*k(n-1,m) // where n-1 will be even.

Note that multiplication by 2^n for integer n can be written as an
arithmetic shift in languages that permit it. In lisp, (ash x n) .

There are similar rules for other factorials of numbers
with other divisors, e.g. if n is divisible by 3.

Multiplying x by 3 can be done by (+ x (ash x 1)) though mult by
powers of 3 are not as fast as binary shifts.

There is a potential for speedup by observing that the answer can always be
factored,
but then if you want to multiply it through to get a single bignum, you
have to figure out the right sequence of multiplies to save time.
Interleaving a prime number sieve with the computation can win, but
only by about a factor of 2, according to Luschny.

For GMP-based systems you should try to get equal-sized inputs to multiply.
For systems which do not use 'advanced' bignum multiply, different
heuristics apply: for them it may be faster to have inputs that are
smallnum X bignum.


There is perhaps a simpler way of expressing some of the
idea in that Geoff's version, eg.

;; (h2 n) is factorial of n, but faster.
(defun hf2(n);; n is positive integer
(if (< n 2) 1
(if (evenp n)
(let ((q (ash n -1)))
(ash (* (hf2 q)(f (- n 1) 2)) q))
(* n (hf2 (1- n))))))

(defun f (n m) (if (<= n 1) 1 (* n (f (- n m) m)))) ;; could be hacked, too.
Back to top
Richard Fateman
science forum Guru Wannabe


Joined: 03 May 2005
Posts: 181

PostPosted: Sat Apr 08, 2006 3:16 pm    Post subject: Re: factorial of largish numbers / more stylish Lisp Reply with quote

Peter, Frank (and others who seem to have been caught by this
problem)...

Here is a more lisp-like version of the split-recursive factorial

(defun fact (n)
(let ((shift 0))
(labels
((kg1 (n m)
(cond ((and (evenp n)(> m 1))
(incf shift (ash n -1))
(kg1 (ash n -1) (ash m -1)))
((<= n m) n)
(t (* (kg1 n (ash m 1))
(kg1 (- n m)(ash m 1)))))))
(ash (kg1 n 1) shift) )))

It runs at the same speed, pretty much, as the unrolled program posted
earlier. I recommend that
you set the optimization flags to (speed 3)(safety 0) and declare that
n,m,shift are fixnums.

To make it run competitively in Allegro CL, I changed it to use the GMP
library,
but then had to change it more so it used a small collection of preallocated
GMP numbers
instead of madly allocating small GMP numbers just to toss them out after
one or
two arithmetic operations. A lisp system using GMP "natively" might be able
to
use the above program. Discussion at
http://www.cs.berkeley.edu/~fateman/papers/factorial.pdf
has been updated.
Thanx
RJF




"Peter Luschny" <spamgrube@luschny.de> wrote in message
news:e12qh7$noh$1@online.de...
Quote:
Frank Buss wrote:

But it still does not look very Lisp-like.

Well, I have been told Lisp is hard, isn't it? Wink
Back to top
Peter Luschny
science forum beginner


Joined: 13 May 2005
Posts: 36

PostPosted: Sat Apr 08, 2006 7:20 pm    Post subject: Re: factorial of largish numbers / more stylish Lisp Reply with quote

Richard Fateman wrote:

Quote:
Peter, Frank (and others who seem to have been caught by this
problem)...

Thanks Richard.

I contribute the secret formula for the binary splitting factorial.

I also wrote a new implementation in a C-like language, more Lisp-
oriented (at least I hope so), no more 'while's necessary ;-)

http://www.luschny.de/math/factorial/binarysplitfact.html

Regards Peter
Back to top
Google

Back to top
Display posts from previous:   
Post new topic   Reply to topic Page 2 of 2 [27 Posts] Goto page:  Previous  1, 2
View previous topic :: View next topic
The time now is Wed Jan 07, 2009 11:01 pm | All times are GMT
Forum index » Science and Technology » Math » Symbolic
Jump to:  

Similar Topics
Topic Author Forum Replies Last Post
No new posts D-numbers: a generalization of Sophie... wkehowski@cox.net Math 8 Thu Jul 20, 2006 12:48 am
No new posts Operator Overloading (Sets and Numbers) DGoncz@aol.com Math 1 Sun Jul 16, 2006 8:26 pm
No new posts Prime numbers Colin Math 11 Wed Jul 12, 2006 8:14 pm
No new posts Clifford Numbers in Mathcad DGoncz@aol.com Math 4 Wed Jul 12, 2006 11:34 am
No new posts How many real numbers are there? Mike Deeth Math 18 Fri Jul 07, 2006 8:23 pm

Internet Advertising | Mortgage Loans | MPAA | Guitar Lessons | W810i
Copyright © 2004-2005 DeniX Solutions SRL
Other DeniX Solutions sites: Electronics forum |  Medicine forum |  Unix/Linux blog |  Unix/Linux documentation |  Unix/Linux forums


Powered by phpBB © 2001, 2005 phpBB Group
[ Time: 0.4596s ][ Queries: 16 (0.3161s) ][ GZIP on - Debug on ]