combinatorics.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 package require Tcl 8.0
00018
00019 namespace ::math {
00020
00021
00022
00023
00024
00025 namespace export ln_Gamma;
00026 namespace export factorial;
00027 namespace export choose;
00028
00029
00030
00031
00032
00033 namespace export Beta;
00034
00035 }
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058 ret ::math::InitializeFactorial () {
00059
00060 variable factorialList
00061
00062 set factorialList [list 1]
00063 set f 1
00064 for { set i 1 } { $i < 171 } { incr i } {
00065 if { $i > 12. } {
00066 set f [expr { $f * double($i)}]
00067 } else {
00068 set f [expr { $f * $i }]
00069 }
00070 lappend factorialList $f
00071 }
00072
00073 }
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096 ret ::math::InitializePascal () {
00097
00098 variable pascal
00099
00100 set pascal [list 1]
00101 for { set n 1 } { $n < 34 } { incr n } {
00102 lappend pascal 1
00103 set l2 [list 1]
00104 for { set k 1 } { $k < $n } { incr k } {
00105 set km1 [expr { $k - 1 }]
00106 set c [expr { [lindex $l $km1] + [lindex $l $k] }]
00107 lappend pascal $c
00108 lappend l2 $c
00109 }
00110 lappend pascal 1
00111 lappend l2 1
00112 set l $l2
00113 }
00114
00115 }
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149 ret ::math::ln_Gamma ( type x ) {
00150
00151 # Handle the common case of a real argument that's within the
00152 # permissible range.
00153
00154 if { [string is double -strict $x]
00155 && ( $x > 0 )
00156 && ( $x <= 2.5563481638716906e+305 )
00157 } {
00158 set x [expr { $x - 1.0 }]
00159 set tmp [expr { $x + 5.5 }]
00160 set tmp [ expr { ( $x + 0.5 ) * log( $tmp ) - $tmp }]
00161 set ser 1.0
00162 foreach cof {
00163 76.18009173 -86.50532033 24.01409822
00164 -1.231739516 .00120858003 -5.36382e-6
00165 } {
00166 set x [expr { $x + 1.0 }]
00167 set ser [expr { $ser + $cof / $x }]
00168 }
00169 return [expr { $tmp + log( 2.50662827465 * $ser ) }]
00170 }
00171
00172 # Handle the error cases.
00173
00174 if { ![string is double -strict $x] } {
00175 return -code error [expectDouble $x]
00176 }
00177
00178 if { $x <= 0.0 } {
00179 set proc [lindex [info level 0] 0]
00180 return -code error \
00181 -errorcode [list ARITH DOMAIN \
00182 "argument to $proc must be positive"] \
00183 "argument to $proc must be positive"
00184 }
00185
00186 return -code error \
00187 -errorcode [list ARITH OVERFLOW \
00188 "floating-point value too large to represent"] \
00189 "floating-point value too large to represent"
00190
00191 }
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224 ret ::math::factorial ( type x ) {
00225
00226 variable factorialList
00227
00228 # Common case: factorial of a small integer
00229
00230 if { [string is integer -strict $x]
00231 && $x >= 0
00232 && $x < [llength $factorialList] } {
00233 return [lindex $factorialList $x]
00234 }
00235
00236 # Error case: not a number
00237
00238 if { ![string is double -strict $x] } {
00239 return -code error [expectDouble $x]
00240 }
00241
00242 # Error case: gamma in the left half plane
00243
00244 if { $x <= -1.0 } {
00245 set proc [lindex [info level 0] 0]
00246 set message "argument to $proc must be greater than -1.0"
00247 return -code error -errorcode [list ARITH DOMAIN $message] $message
00248 }
00249
00250 # Error case - gamma fails
00251
00252 if { [catch { expr {exp( [ln_Gamma [expr { $x + 1 }]] )} } result] } {
00253 return -code error -errorcode $::errorCode $result
00254 }
00255
00256 # Success - computed factorial n as Gamma(n+1)
00257
00258 return $result
00259
00260 }
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288 ret ::math::choose ( type n , type k ) {
00289
00290 variable pascal
00291
00292 # Use a precomputed table for small integer args
00293
00294 if { [string is integer -strict $n]
00295 && $n >= 0 && $n < 34
00296 && [string is integer -strict $k]
00297 && $k >= 0 && $k <= $n } {
00298
00299 set i [expr { ( ( $n * ($n + 1) ) / 2 ) + $k }]
00300
00301 return [lindex $pascal $i]
00302
00303 }
00304
00305 # Test bogus arguments
00306
00307 if { ![string is double -strict $n] } {
00308 return -code error [expectDouble $n]
00309 }
00310 if { ![string is double -strict $k] } {
00311 return -code error [expectDouble $k]
00312 }
00313
00314 # Forbid negative n
00315
00316 if { $n < 0. } {
00317 set proc [lindex [info level 0] 0]
00318 set msg "first argument to $proc must be non-negative"
00319 return -code error -errorcode [list ARITH DOMAIN $msg] $msg
00320 }
00321
00322 # Handle k out of range
00323
00324 if { [string is integer -strict $k] && [string is integer -strict $n]
00325 && ( $k < 0 || $k > $n ) } {
00326 return 0
00327 }
00328
00329 if { $k < 0. } {
00330 set proc [lindex [info level 0] 0]
00331 set msg "second argument to $proc must be non-negative,\
00332 or both must be integers"
00333 return -code error -errorcode [list ARITH DOMAIN $msg] $msg
00334 }
00335
00336 # Compute the logarithm of the desired binomial coefficient.
00337
00338 if { [catch { expr { [ln_Gamma [expr { $n + 1 }]]
00339 - [ln_Gamma [expr { $k + 1 }]]
00340 - [ln_Gamma [expr { $n - $k + 1 }]] } } r] } {
00341 return -code error -errorcode $::errorCode $r
00342 }
00343
00344 # Compute the binomial coefficient itself
00345
00346 if { [catch { expr { exp( $r ) } } r] } {
00347 return -code error -errorcode $::errorCode $r
00348 }
00349
00350 # Round to integer if both args are integers and the result fits
00351
00352 if { $r <= 2147483647.5
00353 && [string is integer -strict $n]
00354 && [string is integer -strict $k] } {
00355 return [expr { round( $r ) }]
00356 }
00357
00358 return $r
00359
00360 }
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392 ret ::math::Beta ( type z , type w ) {
00393
00394 # Check form of both args so that domain check can be made
00395
00396 if { ![string is double -strict $z] } {
00397 return -code error [expectDouble $z]
00398 }
00399 if { ![string is double -strict $w] } {
00400 return -code error [expectDouble $w]
00401 }
00402
00403 # Check sign of both args
00404
00405 if { $z <= 0.0 } {
00406 set proc [lindex [info level 0] 0]
00407 set msg "first argument to $proc must be positive"
00408 return -code error -errorcode [list ARITH DOMAIN $msg] $msg
00409 }
00410 if { $w <= 0.0 } {
00411 set proc [lindex [info level 0] 0]
00412 set msg "second argument to $proc must be positive"
00413 return -code error -errorcode [list ARITH DOMAIN $msg] $msg
00414 }
00415
00416 # Compute beta using gamma function, keeping stack trace clean.
00417
00418 if { [catch { expr { exp( [ln_Gamma $z] + [ln_Gamma $w]
00419 - [ln_Gamma [ expr { $z + $w }]] ) } } beta] } {
00420
00421 return -code error -errorcode $::errorCode $beta
00422
00423 }
00424
00425 return $beta
00426
00427 }
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438 namespace ::math {
00439 InitializeFactorial
00440 InitializePascal
00441 }
00442