EKB This is an implementation of the incomplete Beta function Ix(a, b), defined as:
/ x 1 | a-1 b-1 Ix(a,b) = ------- | dt t (1 - t) B(a, b) | / 0
where B(a,b) is the Beta function. The incomplete Beta function is the cumulative probability function for the Beta distribution. (This code has been tested as part of the tests run on the Beta distribution.)
Here's the code:
package require math namespace import ::math::ln_Gamma namespace import ::math::Beta # # Implement the incomplete beta function Ix(a, b) # proc incompleteBeta {a b x {tol 1.0e-9}} { if {$x < 0.0 || $x > 1.0} { error "Value out of range in incomplete Beta function: x = $x, not in \[0, 1\]" } if {$a <= 0.0} { error "Value out of range in incomplete Beta function: a = $a, must be > 0" } if {$b <= 0.0} { error "Value out of range in incomplete Beta function: b = $b, must be > 0" } if {$x < $tol} { return 0.0 } if {$x > 1.0 - $tol} { return 1.0 } # This is for convenience: incBeta_series reduces b to < 1, so better # if b is smaller if {$a > $b} { return [incBeta_series $a $b $x $tol] } else { set z [incBeta_series $b $a [expr {1.0 - $x}] $tol] return [expr {1.0 - $z}] } } ##################################################### # # Series expansion for Ix(a, b) # # Abramowitz & Stegun formula 26.5.4 # # This series has terms # # [B(a+1,n)/B(a+b,n)] * x^n # # This is guaranteed to converge only if # b < 1. Use the recurrence formula 26.5.15 # from A&S to bring b below 1. Recurrence is: # # Ix(a,b) = (G(a+b)/(G(a+1)G(b))) * x^a * (1-x)^(b-1) + # Ix(a+1, b-1) # # Also, use B(a,b) = G(a)G(b)/G(a+b) to # rewrite coeff as # # Cn = [G(a+1)/G(a+b)] * G(a+b+n)/G(a+n+1) # ##################################################### proc incBeta_series {a b x tol} { # a+b is invariant under recurrence formula set aplusb [expr {$a + $b}] set lnGapb [ln_Gamma $aplusb] # Calculate for convenience -- these don't change set lnx [expr {log($x)}] set ln1mx [expr {log(1.0 - $x)}] set retval 0.0 while {$b > 1} { # Convenient to increment a here, because expression a+1 appears in Gamma set a [expr {$a + 1}] # Pack everything in one big exp, since individual terms can over/underflow set retval [expr {$retval + \ exp($lnGapb - [ln_Gamma $a] - [ln_Gamma $b] + \ ($a - 1) * $lnx + ($b - 1) * $ln1mx)}] set b [expr {$b - 1}] } set pref_num [expr {pow($x, $a) * pow(1.0 - $x, $b)}] set pref_denom [expr {$a * [Beta $a $b]}] # Catch case where large a/b and small x lead to very small numbers if {$pref_num == 0.0} { return 0.0 } set pref [expr {$pref_num/$pref_denom}] set term 1.0 set z 1.0 set sum 1.0 set n 0 set adjtol [expr {$tol * $pref}] set t1 [expr {[ln_Gamma [expr {$a + 1}]] - $lnGapb}] while 1 { set z [expr {$z * $x}] incr n set abn [expr {$a + $n + $b}] set an1 [expr {$a + $n + 1}] set factor [expr {exp($t1 + [ln_Gamma $abn] - [ln_Gamma $an1])}] set nexterm [expr {$z * $factor}] if {abs($nexterm - $term) < $adjtol} { break } set term $nexterm set sum [expr {$sum + $term}] } # Add previous cumulative sum to the series expansion expr {$retval + $pref * $sum} }