📄 tools.c
字号:
double InverseCDFBeta(double prob, double p, double q, double lnbeta)
{
/* This calculates the inverseCDF of the beta distribution
Cran, G. W., K. J. Martin and G. E. Thomas (1977).
Remark AS R19 and Algorithm AS 109, Applied Statistics, 26(1), 111-114.
Remark AS R83 (v.39, 309-310) and correction (v.40(1) p.236).
My own implementation of the algorithm did not bracket the variable well.
This version is Adpated from the pbeta and qbeta routines from
"R : A Computer Language for Statistical Data Analysis", and it fails for
extreme values of p and q as well, although it seems better than my
previous version.
Ziheng Yang, May 2001
*/
double fpu=3e-308, acu_min=1e-300, lower=fpu, upper=1-2.22e-16;
/* acu_min>= fpu: Minimal value for accuracy 'acu' which will depend on (a,p); */
int swap_tail, i_pb, i_inn;
double a, adj, g, h, pp, prev=0, qq, r, s, t, tx=0, w, y, yprev;
double acu;
volatile double xinbta;
if(prob<0 || prob>1 || p<0 || q<0) error2("out of range in InverseCDFBeta");
/* define accuracy and initialize */
xinbta = prob;
/* test for admissibility of parameters */
if(p<0 || q<0 || prob<0 || prob>1) error2("beta par err");
if (prob == 0 || prob == 1)
return prob;
if(lnbeta==0) lnbeta=LnGamma(p)+LnGamma(q)-LnGamma(p+q);
/* change tail if necessary; afterwards 0 < a <= 1/2 */
if (prob <= 0.5) {
a = prob; pp = p; qq = q; swap_tail = 0;
}
else { /* change tail, swap p <-> q :*/
a = 1. - prob; pp = q; qq = p; swap_tail = 1;
}
/* calculate the initial approximation */
r = sqrt(-log(a * a));
y = r - (2.30753+0.27061*r)/(1.+ (0.99229+0.04481*r) * r);
if (pp > 1. && qq > 1.) {
r = (y * y - 3.) / 6.;
s = 1. / (pp*2. - 1.);
t = 1. / (qq*2. - 1.);
h = 2. / (s + t);
w = y * sqrt(h + r) / h - (t - s) * (r + 5./6. - 2./(3.*h));
xinbta = pp / (pp + qq * exp(w + w));
}
else {
r = qq*2.;
t = 1. / (9. * qq);
t = r * pow(1. - t + y * sqrt(t), 3.);
if (t <= 0.)
xinbta = 1. - exp((log((1. - a) * qq) + lnbeta) / qq);
else {
t = (4.*pp + r - 2.) / t;
if (t <= 1.)
xinbta = exp((log(a * pp) + lnbeta) / pp);
else
xinbta = 1. - 2./(t+1.);
}
}
/* solve for x by a modified newton-raphson method, using CDFBeta */
r = 1. - pp;
t = 1. - qq;
yprev = 0.;
adj = 1.;
/* Changes made by Ziheng to fix a bug in qbeta()
qbeta(0.25, 0.143891, 0.05) = 3e-308 wrong (correct value is 0.457227)
*/
if(xinbta<0) xinbta=(a+.5)/2;
else if (xinbta < lower) xinbta = lower;
else if (xinbta > upper) xinbta = upper;
/* Desired accuracy should depend on (a,p)
* This is from Remark .. on AS 109, adapted.
* However, it's not clear if this is "optimal" for IEEE double prec.
* acu = fmax2(acu_min, pow(10., -25. - 5./(pp * pp) - 1./(a * a)));
* NEW: 'acu' accuracy NOT for squared adjustment, but simple;
* ---- i.e., "new acu" = sqrt(old acu)
*/
acu = pow(10., -13. - 2.5/(pp * pp) - 0.5/(a * a));
acu = max2(acu, acu_min);
for (i_pb=0; i_pb < 1000; i_pb++) {
y = CDFBeta(xinbta, pp, qq, lnbeta);
/* y = pbeta2(xinbta, pp, qq, lnbeta) -- to SAVE CPU; */
y = (y - a) *
exp(lnbeta + r * log(xinbta) + t * log(1. - xinbta));
if (y * yprev <= 0)
prev = max2(fabs(adj),fpu);
g = 1;
for (i_inn=0; i_inn < 1000; i_inn++) {
adj = g * y;
if (fabs(adj) < prev) {
tx = xinbta - adj; /* trial new x */
if (tx >= 0. && tx <= 1.) {
if (prev <= acu || fabs(y) <= acu) goto L_converged;
if (tx != 0. && tx != 1.) break;
}
}
g /= 3.;
}
/* this prevents trouble with excess FPU precision on some machines. */
xtrunc = tx;
if (xtrunc == xinbta)
goto L_converged;
xinbta = tx;
yprev = y;
}
/*
printf("\nwarning: InverseCDFBeta(%.2f, %.5f, %.5f) = %.6e\n",
prob,p,q, (swap_tail ? 1. - xinbta : xinbta));
*/
L_converged:
return (swap_tail ? 1. - xinbta : xinbta);
}
static double prob_InverseCDF, *par_InverseCDF;
static double (*cdf_InverseCDF)(double x,double par[]);
double diff_InverseCDF(double x);
double diff_InverseCDF(double x)
{
/* This is the difference between the given p and the CDF(x), the
objective function to be minimized.
*/
double px=(*cdf_InverseCDF)(x,par_InverseCDF);
return(square(prob_InverseCDF-px));
}
double InverseCDF(double(*cdf)(double x,double par[]),
double p,double x,double par[],double xb[2])
{
/* Use x for initial value if in range
*/
double sdiff,step=min2(0.05,(xb[1]-xb[0])/100);
prob_InverseCDF=p; par_InverseCDF=par; cdf_InverseCDF=cdf;
if(x<=xb[0]||x>=xb[1]) x=.5;
LineSearch(diff_InverseCDF, &sdiff, &x, xb, step);
return(x);
}
int ScatterPlot (int n, int nseries, int yLorR[], double x[], double y[],
int nrow, int ncol, int ForE)
{
/* This plots a scatter diagram. There are nseries of data (y)
for the same x. nrow and ncol specifies the #s of rows and cols
in the text output.
Use ForE=1 for floating format
yLorR[nseries] specifies which y axis (L or R) to use, if nseries>1.
*/
char *chart,ch, *fmt[2]={"%*.*e ", "%*.*f "}, symbol[]="*~^@",overlap='&';
int i,j,is,iy,ny=1, ncolr=ncol+3, irow=0, icol=0, w=10, wd=2;
double large=1e32, xmin, xmax, xgap, ymin[2], ymax[2], ygap[2];
for (i=1,xmin=xmax=x[0]; i<n; i++)
{ if(xmin>x[i]) xmin=x[i]; if(xmax<x[i]) xmax=x[i]; }
for (i=0; i<2; i++) { ymin[i]=large; ymax[i]=-large; }
for (j=0; j<(nseries>1)*nseries; j++)
if (yLorR[j]==1) ny=2;
else if (yLorR[j]!=0) printf ("err: y axis %d", yLorR[j]);
for (j=0; j<nseries; j++) {
for (i=0,iy=(nseries==1?0:yLorR[j]); i<n; i++) {
if (ymin[iy]>y[j*n+i]) ymin[iy]=y[j*n+i];
if (ymax[iy]<y[j*n+i]) ymax[iy]=y[j*n+i];
}
}
if (xmin==xmax) { puts("no variation in x?"); }
xgap=(xmax-xmin)/ncol;
for (iy=0; iy<ny; iy++) ygap[iy]=(ymax[iy]-ymin[iy])/nrow;
printf ("\n%10s", "legend: ");
for (is=0; is<nseries; is++) printf ("%2c", symbol[is]);
printf ("\n%10s", "y axies: ");
if (ny==2) for(is=0; is<nseries; is++) printf ("%2d", yLorR[is]);
printf ("\nx : (%10.2e, %10.2e)", xmin, xmax);
printf ("\ny[1]: (%10.2e, %10.2e)\n", ymin[0], ymax[0]);
if (ny==2) printf ("y[2]: (%10.2e, %10.2e) \n", ymin[1], ymax[1]);
chart=(char*)malloc((nrow+1)*ncolr*sizeof(char));
for (i=0; i<nrow+1; i++) {
for (j=1; j<ncol; j++) chart[i*ncolr+j]=' ';
if (i%5==0) chart[i*ncolr+0]=chart[i*ncolr+j++]='+';
else chart[i*ncolr+0]=chart[i*ncolr+j++]='|';
chart[i*ncolr+j]='\0';
if (i==0||i==nrow)
FOR(j,ncol+1) chart[i*ncolr+j]=(char)(j%10==0?'+':'-');
}
for (is=0; is<nseries; is++) {
for (i=0,iy=(nseries==1?0:yLorR[is]); i<n; i++) {
for(j=0; j<ncol+1; j++) if(x[i]<=xmin+(j+0.5)*xgap) { icol=j; break; }
for(j=0; j<nrow+1; j++)
if(y[is*n+i]<=ymin[iy]+(j+0.5)*ygap[iy]) { irow=nrow-j; break;}
/*
chart[irow*ncolr+icol]=symbol[is];
*/
if ((ch=chart[irow*ncolr+icol])==' ' || ch=='-' || ch=='+')
chart[irow*ncolr+icol]=symbol[is];
else
chart[irow*ncolr+icol]=overlap;
}
}
printf ("\n");
for (i=0; i<nrow+1; i++) {
if (i%5==0) printf (fmt[ForE], w-1, wd, ymin[0]+(nrow-i)*ygap[0]);
else printf ("%*s", w, "");
printf ("%s", chart+i*ncolr);
if (ny==2 && i%5==0) printf(fmt[ForE], w-1, wd, ymin[1]+(nrow-i)*ygap[1]);
printf ("\n");
}
printf ("%*s", w-6, "");
for (j=0; j<ncol+1; j++) if(j%10==0) printf(fmt[ForE], 10-1,wd,xmin+j*xgap);
printf ("\n%*s\n", ncol/2+1+w, "x");
free(chart);
return(0);
}
long factorial(int n)
{
long f, i;
if (n>10) error2("n>10 in factorial");
for (i=2,f=1; i<=(long)n; i++) f*=i;
return (f);
}
double Binomial(double n, double k, double *scale)
{
/* calculates n choose k, but currently only works for int k>0
n can be any real number.
If(*scale!=0) the result should be c+exp(*scale).
*/
double c,i,large=1e99;
*scale=0;
if(k==0) return(1);
if(k<0 || (int)k!=k)
error2("k is not a whole number in Binomial.");
if(n>0 && (int)n==n) k=min2(k,n-k);
for (i=1,c=1; i<=k; i++) {
c*=(n-k+i)/i;
if(c>large)
{ *scale+=log(c); c=1; }
}
return(c);
}
/****************************
Vectors and matrices
*****************************/
double Det3x3 (double x[3*3])
{
return
x[0*3+0]*x[1*3+1]*x[2*3+2]
+ x[0*3+1]*x[1*3+2]*x[2*3+0]
+ x[0*3+2]*x[1*3+0]*x[2*3+1]
- x[0*3+0]*x[1*3+2]*x[2*3+1]
- x[0*3+1]*x[1*3+0]*x[2*3+2]
- x[0*3+2]*x[1*3+1]*x[2*3+0] ;
}
int matby (double a[], double b[], double c[], int n,int m,int k)
/* a[n*m], b[m*k], c[n*k] ...... c = a*b
*/
{
int i,j,i1;
double t;
FOR (i,n) FOR(j,k) {
for (i1=0,t=0; i1<m; i1++) t+=a[i*m+i1]*b[i1*k+j];
c[i*k+j] = t;
}
return (0);
}
int matIout (FILE *fout, int x[], int n, int m)
{
int i,j;
for (i=0,FPN(fout); i<n; i++,FPN(fout))
FOR(j,m) fprintf(fout,"%6d", x[i*m+j]);
return (0);
}
int matout (FILE *fout, double x[], int n, int m)
{
int i,j;
for (i=0,FPN(fout); i<n; i++,FPN(fout))
FOR(j,m) fprintf(fout," %11.6f", x[i*m+j]);
return (0);
}
int matout2 (FILE * fout, double x[], int n, int m, int wid, int deci)
{
int i,j;
for (i=0,FPN(fout); i<n; i++,FPN(fout))
FOR(j,m) fprintf(fout," %*.*f", wid-1, deci, x[i*m+j]);
return (0);
}
int mattransp1 (double x[], int n)
/* transpose a matrix x[n*n], stored by rows.
*/
{
int i,j;
double t;
FOR (i,n) for (j=0; j<i; j++)
if (i!=j) { t=x[i*n+j]; x[i*n+j]=x[j*n+i]; x[j*n+i]=t; }
return (0);
}
int mattransp2 (double x[], double y[], int n, int m)
{
/* transpose a matrix x[n][m] --> y[m][n]
*/
int i,j;
FOR (i,n) FOR (j,m) y[j*n+i]=x[i*m+j];
return (0);
}
int matinv (double x[], int n, int m, double space[])
{
/* x[n*m] ... m>=n
*/
register int i,j,k;
int *irow=(int*) space;
double ee=1.0e-30, t,t1,xmax;
double det=1.0;
FOR (i,n) {
xmax = 0.;
for (j=i; j<n; j++)
if (xmax < fabs(x[j*m+i])) { xmax = fabs(x[j*m+i]); irow[i]=j; }
det *= xmax;
if (xmax < ee) {
printf("\nDet = %.4e close to zero at %3d!\t\n", xmax,i+1);
return(-1);
}
if (irow[i] != i) {
FOR (j,m) {
t = x[i*m+j];
x[i*m+j] = x[irow[i]*m+j];
x[irow[i]*m+j] = t;
}
}
t = 1./x[i*m+i];
FOR (j,n) {
if (j == i) continue;
t1 = t*x[j*m+i];
FOR(k,m) x[j*m+k] -= t1*x[i*m+k];
x[j*m+i] = -t1;
}
FOR(j,m) x[i*m+j] *= t;
x[i*m+i] = t;
} /* i */
for (i=n-1; i>=0; i--) {
if (irow[i] == i) continue;
FOR(j,n) {
t = x[j*m+i];
x[j*m+i] = x[j*m + irow[i]];
x[j*m + irow[i]] = t;
}
}
return (0);
}
int getpi_sqrt (double pi[], double pi_sqrt[], int n, int *npi0)
{
int j;
for(j=0,*npi0=0; j<n; j++)
if(pi[j]) pi_sqrt[(*npi0)++]=sqrt(pi[j]);
*npi0 = n - *npi0;
return(0);
}
int eigenQREV (double Q[], double pi[], double pi_sqrt[], int n, int npi0,
double Root[], double U[], double V[])
{
/*
This finds the eigen solution of the rate matrix Q for a time-reversible
Markov process, using the algorithm for a real symmetric matrix.
Rate matrix Q = S * diag{pi} = U * diag{Root} * V,
where S is symmetrical, all elements of pi are positive, and U*V = I.
pi_sqrt[n-npi0] has to be calculated before calling this routine.
[U 0] [Q_0 0] [U^-1 0] [Root 0]
[0 I] [0 0] [0 I] = [0 0]
Ziheng Yang, 25 December 2001 (ref is CME/eigenQ.pdf)
*/
int i,j, inew,jnew, nnew=n-npi0, status;
/* store in U the symmetrical matrix S = sqrt(D) * Q * sqrt(-D) */
if(pi_sqrt==NULL) error2("pi_sqrt should be calculated before");
if(npi0==0) {
for(i=0; i<n; i++)
for(j=0,U[i*n+i] = Q[i*n+i]; j<i; j++)
U[i*n+j] = U[j*n+i] = (Q[i*n+j] * pi_sqrt[i]/pi_sqrt[j]);
status=eigenRealSym(U, n, Root, V);
for(i=0;i<n;i++) for(j=0;j<n;j++) V[i*n+j] = U[j*n+i] * pi_sqrt[j];
for(i=0;i<n;i++) for(j=0;j<n;j++) U[i*n+j] /= pi_sqrt[i];
}
else {
for(i=0,inew=0; i<n; i++) {
if(pi[i]) {
for(j=0,jnew=0; j<i; j++)
if(pi[j]) {
U[inew*nnew+jnew] = U[jnew*nnew+inew]
= Q[i*n+j] * pi_sqrt[inew]/pi_sqrt[jnew];
jnew++;
}
U[inew*nnew+inew] = Q[i*n+i];
inew++;
}
}
status=eigenReal
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -