--*********************************************************
-- C(n,r),
P(n,r), and nFactorial(n) version 2.0
-- Jim Andrews,
August 2004
-- http://vispo.com/lingo/Cnr.htm
--Thanks to
Robert Tweed, Valentin Schmidt,
--Andrew Morton,
and others on the DirGames-L
--list who
helped with this.
-- This is
a movie script for Director 8+.
-- API:
--C(n,r) returns
the number of combinations
--of n things
taken r at a time.
--P(n,r) returns
the number of permutations
--of n things
taken r at a time.
--nFactorial(n)
returns n!=n(n-1)(n-2)...1
--Ctester and
Ctester2 are for running C(n,r) in
--batch to
test it out and see how it performs in
--terms of
execution time.
--Ptester is,
similarly, for running P(n,r) in batch to
--see how it
performs.
--*********************************************************
--PUBLIC HANDLERS
--*********************************************************
on C(n,r)
--PUBLIC
--FUNCTION**********************************************
--Returns
the number of combinations of n things
--taken
r at a time. For instance, if there are
--four
letters A, B, C, D and we select two of them,
--the
possible combinations are AB, AC, AD, BC, BD,
--CD,
ie, there are 6 combinations. Order does not
--matter.
--FORMULA************************************************
--C(n,r)=n!
/ ((n-r)!r!) = n(n-1)(n-2)...(n-r+1)/r!
--C(n,r)
calculates n/r * (n-1)/(r-1) * ... * (n-r+1)/1
--Note
that 0!=1 and when n>0, C(-n,r)=C(n+r-1,r)*(-1)^r
--PARAMETERS*********************************************
--Both
n and r must be type integer. If they aren't, 0 is
--returned.
n can be any integer. r>=0 or 0 is returned.
--abs(n)>=r
or 0 is returned.
--RETURN
VALUE*******************************************
--C(n,r)
returns a valid float or INF if the value is too high.
--INF
is an odd float number that stands for 'infinite'.
--It
isn't infinite but just too large for Lingo to handle.
--If
you want to test to see if the result is INF, the way
--to
do it is 'if b=1e309 then', where b is the return value
--from
C(n,r). 1e309 is one of the smallest numbers that
--is
too big for Lingo to handle.
--C(n,r)
returns a valid float for all values of n<1030. When
--n
exceeds 1029, then whether C(n,r) returns a float or
--INF
depends on whether C(n,r) exceeds the maximal Lingo
--float
value which is somewhere around power(10,309)=
--1e309.
if integerP(n) and integerP(r) then
if n> 0 then
if r > 0 then
if n>=r then
a=1.0
repeat
with i= 0 to r-1
a=a*
((n-i)/float(r-i))
end
repeat
if a< the maxinteger then
return a
else
if a
= 1e309 then
--Then
a is too big a number for Lingo to handle.
--If
you type 'put 1e309' in the Message Window,
--you
see it is in fact INF, ie, a very odd float.
return a
else
return ceiling(a)
end
if
end
if
else
--Else
n<r
alert("C(n,r)
called with n<r. 0 is returned.")
return 0
end
if
else
--Else
r<=0
if r=0 then
--Then
n>0 and r=0
return 1
else
--Else
n>0 and r<0
alert("C(n,r)
called with negative r parameter. 0 is returned.")
return 0
end
if
end
if
else
--Else
n<=0
if n= 0 then
if r=0 then
--Then
n=0 and r=0
return 1
else
--Else
n=0 and r <> 0
return 0
end
if
else
--Else
n<0
--The
books define it this way.
return power(-1,r)*C((-1)*n+r-1,r)
end
if
end
if
else
--Else
n or r or both are not integers
alert("C(n,r) called with non-integer parameter(s).
0 is returned.")
return 0
end
if
end
on P(n,r)
--PUBLIC
--FUNCTION**********************************************
--Returns
the number of permutations of n things
--taken
r at a time. For instance, if there are
--four
letters A, B, C, D and we select two of them,
--the
possible permutations are AB, BA, AC, CA, AD, DA,
--BC,
CB, BD, DB, CD, DC, ie, there are 12 permutations.
--Order
does matter.
--FORMULA************************************************
--P(n,r)=n!
/ (n-r)! = n(n-1)(n-2)...(n-r+1)
--PARAMETERS*********************************************
--Both
n and r must be type integer. If they aren't, 0 is
--returned.
n can be any integer. r>=0 or 0 is returned.
--abs(n)>=r
or 0 is returned.
--RETURN
VALUE*******************************************
--P(n,r)
returns a float or INF if the value is too high.
--Note
that P(n,r) is maximal when r=n, in which case
--P(n,r)=n!
So, like the n! function below, P(n,r) is
--good
for n<=170. Whether P(n,r) returns a float or INF
--for
values of n>170 depends on the value of r.
if integerP(n) and integerP(r) then
if n> 0 then
if r > 0 then
if n>=r then
a=1.0
repeat
with i= 0 to r-1
a=a*
(n-i)
end
repeat
return a
else
--Else
abs(n)<r
alert("P(n,r) called with n<r. 0 is returned.")
return 0
end
if
else
--Else
r<=0
if r=0 then
--Then
n>0 and r=0
return 1
else
--Else
n>0 and r<0
alert("P(n,r)
called with negative r parameter. 0 is returned.")
return 0
end
if
end if
else
--Else
n<=0
if n= 0 then
if r=0 then
--Then
n=0 and r=0
return 1
else
--Else
n=0 and r <> 0
return 0
end
if
else
--Else
n<0
return power(-1,r)*P((-1)*n+r-1,r)
end if
end if
else
--Else
n or r or both are not integers
alert("P(n,r) called with non-integer parameter(s). 0 is returned.")
return 0
end if
end
on nFactorial(n)
--PUBLIC
--FUNCTION**********************************************
--Returns
n! = n(n-1)...1
--Note
that 0!=1.
--PARAMETER*********************************************
--n
is an integer >=0. If not, 0 is returned.
--RETURN
VALUE*******************************************
--Returns
a float. nFactorial(n) is good for n<=170.
--Returns
INF for values of n > 170.
if integerP(n) then
if n>0 then
a=1.0
repeat
with i=2 to n
a=a*i
end repeat
return a
else
--Else
n<=0
if n=0 then
--0!
is defined to be 1.
return 1
else
--n<0,
so we return 0
alert("nFactorial called with negative value
of n. 0 is returned.")
return 0
end if
end if
else
--Else
n is not an integer
alert("nFactorial called with non-integer parameter. 0 is returned.")
return 0
end if
end nFactorial
on Ctester(n1, n2)
--PUBLIC
--This
was written to test C(n,r).
--This
runs C(n,r) n2 - n1 times.
--For
instance, if n1=4 and n2=10, tester will compute
--C(4,2),
C(5,2), C(6,3), C(7/3), C(8,4), C(9,4), and C(10,5)
--and
display the time each took to compute.
--In
each case, r is basically half of n.
thetext=""
repeat with i=n1 to n2
n=i
r=abs(i/2)
startTime=the milliseconds
theResult=C(n,r)
theEndTime=the milliseconds - startTime
theText=theText & "C(" & string(n) & "," & string(r) & ")=" & \
string(theResult) & " time: " & string(theEndTime) & " ms" & RETURN
end repeat
put theText
end
on Ctester2(n1, n2, r)
--PUBLIC
--This
was written to test C(n,r).
--This
runs C(n,r) n2 - n1 times.
--For
instance, if n1=4 and n2=10, and r=3, Ctester2 will compute
--C(4,3),
C(5,3), C(6,3), C(7,3), C(8,3), C(9,3), and C(10,3)
--and
display the time each took to compute.
thetext=""
repeat with i=n1 to n2
n=i
startTime=the milliseconds
theResult=C(n,r)
theEndTime=the milliseconds - startTime
theText=theText & "C(" & string(n) & "," & string(r) & ")=" & \
string(theResult) & " time: " & string(theEndTime) & " ms" & RETURN
end repeat
put theText
end
on Ptester(n1, n2)
--PUBLIC
--This
was written to test P(n,r).
--This
runs P(n,r) n2 - n1 times.
--For
instance, if n1=4 and n2=10, tester will compute
--P(4,2),
P(5,2), P(6,3), P(7/3), P(8,4), P(9,4), and P(10,5)
--and
display the time each took to compute.
thetext=""
repeat with i=n1 to n2
n=i
r=abs(i/2)
startTime=the milliseconds
theResult=P(n,r)
theEndTime=the milliseconds - startTime
theText=theText & "P(" & string(n) & "," & string(r) & ")=" & \
string(theResult) & " time: " & string(theEndTime) & " ms" & RETURN
end repeat
put theText
end
--*********************************************************
--PRIVATE HANDLERS
--*********************************************************
on ceiling(x)
--Takes
a positive float argument
--and
returns the ceiling value.
--For
instance, if a float is b.c,
--where
c is the decimal part, then
--if
c is 0, ceiling(b.c) = b.c, but
--if
c is not 0, then ceiling(b.c)=
--b
+ 1 as a float value.
y=rnd(x)
if y < x then
return y+1
else
return y
end if
end
on rnd (x)
--This
takes a float argument
--and
truncates any decimal
--as
the return value.
--So
if a float is b.c where
--c
is the decimal portion,
--then
rnd(a.b) returns a.0
fp = the floatprecision
the floatprecision = 0
r = value(string(x))
the floatprecision = fp
return r
end
V I S P O