|
|
| Author |
Message |
Klueless science forum beginner
Joined: 30 May 2005
Posts: 19
|
Posted: Thu Apr 06, 2006 4:22 am Post subject:
Re: factorial of largish numbers
|
|
|
"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
|
Posted: Thu Apr 06, 2006 4:59 am Post subject:
Re: factorial of largish numbers
|
|
|
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
|
Posted: Thu Apr 06, 2006 7:55 am Post subject:
Re: factorial of largish numbers
|
|
|
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
|
Posted: Thu Apr 06, 2006 8:31 am Post subject:
Re: factorial of largish numbers
|
|
|
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
|
Posted: Thu Apr 06, 2006 10:28 am Post subject:
Re: factorial of largish numbers
|
|
|
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
|
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
|
Posted: Thu Apr 06, 2006 12:07 pm Post subject:
Re: factorial of largish numbers
|
|
|
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
|
Posted: Thu Apr 06, 2006 12:31 pm Post subject:
Re: factorial of largish numbers
|
|
|
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
|
Posted: Thu Apr 06, 2006 5:53 pm Post subject:
Re: factorial of largish numbers
|
|
|
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
|
Posted: Thu Apr 06, 2006 6:17 pm Post subject:
Re: factorial of largish numbers
|
|
|
<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
#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
|
Posted: Thu Apr 06, 2006 8:42 pm Post subject:
Re: factorial of largish numbers
|
|
|
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
|
Posted: Sat Apr 08, 2006 3:16 pm Post subject:
Re: factorial of largish numbers / more stylish Lisp
|
|
|
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?
|
|
|
| Back to top |
|
 |
Peter Luschny science forum beginner
Joined: 13 May 2005
Posts: 36
|
Posted: Sat Apr 08, 2006 7:20 pm Post subject:
Re: factorial of largish numbers / more stylish Lisp
|
|
|
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 |
|
 |
|
|
The time now is Wed Jan 07, 2009 11:01 pm | All times are GMT
|
|
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
|
|