⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 discrete.c

📁 该文件为c++的数学函数库!是一个非常有用的编程工具.它含有各种数学函数,为科学计算、工程应用等程序编写提供方便!
💻 C
字号:
/* randist/discrete.c *  * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough *  * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. *  * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU * General Public License for more details. *  * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *//*   Random Discrete Events      Given K discrete events with different probabilities P[k]   produce a value k consistent with its probability.   This program is free software; you can redistribute it and/or   modify it under the terms of the GNU General Public License as   published by the Free Software Foundation; either version 2 of the   License, or (at your option) any later version.   This program is distributed in the hope that it will be useful, but   WITHOUT ANY WARRANTY; without even the implied warranty of   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   General Public License for more details.  You should have received   a copy of the GNU General Public License along with this program;   if not, write to the Free Foundation, Inc., 59 Temple Place, Suite   330, Boston, MA 02111-1307 USA*//* * Based on: Alastair J Walker, An efficient method for generating * discrete random variables with general distributions, ACM Trans * Math Soft 3, 253-256 (1977).  See also: D. E. Knuth, The Art of * Computer Programming, Volume 2 (Seminumerical algorithms), 3rd * edition, Addison-Wesley (1997), p120. * Walker's algorithm does some preprocessing, and provides two * arrays: floating point F[k] and integer A[k].  A value k is chosen * from 0..K-1 with equal likelihood, and then a uniform random number * u is compared to F[k].  If it is less than F[k], then k is * returned.  Otherwise, A[k] is returned.    * Walker's original paper describes an O(K^2) algorithm for setting * up the F and A arrays.  I found this disturbing since I wanted to * use very large values of K.  I'm sure I'm not the first to realize * this, but in fact the preprocessing can be done in O(K) steps. * A figure of merit for the preprocessing is the average value for * the F[k]'s (that is, SUM_k F[k]/K); this corresponds to the * probability that k is returned, instead of A[k], thereby saving a * redirection.  Walker's O(K^2) preprocessing will generally improve * that figure of merit, compared to my cheaper O(K) method; from some * experiments with a perl script, I get values of around 0.6 for my * method and just under 0.75 for Walker's.  Knuth has pointed out * that finding _the_ optimum lookup tables, which maximize the * average F[k], is a combinatorially difficult problem.  But any * valid preprocessing will still provide O(1) time for the call to * gsl_ran_discrete().  I find that if I artificially set F[k]=1 -- * ie, better than optimum! -- I get a speedup of maybe 20%, so that's * the maximum I could expect from the most expensive preprocessing. * Folding in the difference of 0.6 vs 0.75, I'd estimate that the * speedup would be less than 10%. * I've not implemented it here, but one compromise is to sort the * probabilities once, and then work from the two ends inward.  This * requires O(K log K), still lots cheaper than O(K^2), and from my * experiments with the perl script, the figure of merit is within * about 0.01 for K up to 1000, and no sign of diverging (in fact, * they seemed to be converging, but it's hard to say with just a * handful of runs). * The O(K) algorithm goes through all the p_k's and decides if they * are "smalls" or "bigs" according to whether they are less than or * greater than the mean value 1/K.  The indices to the smalls and the * bigs are put in separate stacks, and then we work through the * stacks together.  For each small, we pair it up with the next big * in the stack (Walker always wanted to pair up the smallest small * with the biggest big).  The small "borrows" from the big just * enough to bring the small up to mean.  This reduces the size of the * big, so the (smaller) big is compared again to the mean, and if it * is smaller, it gets "popped" from the big stack and "pushed" to the * small stack.  Otherwise, it stays put.  Since every time we pop a * small, we are able to deal with it right then and there, and we * never have to pop more than K smalls, then the algorithm is O(K). * This implementation sets up two separate stacks, and allocates K * elements between them.  Since neither stack ever grows, we do an * extra O(K) pass through the data to determine how many smalls and * bigs there are to begin with and allocate appropriately.  In all * there are 2*K*sizeof(double) transient bytes of memory that are * used than returned, and K*(sizeof(int)+sizeof(double)) bytes used * in the lookup table.    * Walker spoke of using two random numbers (an integer 0..K-1, and a * floating point u in [0,1]), but Knuth points out that one can just * use the integer and fractional parts of K*u where u is in [0,1]. * In fact, Knuth further notes that taking F'[k]=(k+F[k])/K, one can * directly compare u to F'[k] without having to explicitly set * u=K*u-int(K*u). * Usage: * Starting with an array of probabilities P, initialize and do * preprocessing with a call to: *    gsl_rng *r; *    gsl_ran_discrete_t *f; *    f = gsl_ran_discrete_preproc(K,P);    * Then, whenever a random index 0..K-1 is desired, use *    k = gsl_ran_discrete(r,f); * Note that several different randevent struct's can be * simultaneously active. * Aside: A very clever alternative approach is described in * Abramowitz and Stegun, p 950, citing: Marsaglia, Random variables * and computers, Proc Third Prague Conference in Probability Theory, * 1962.  A more accesible reference is: G. Marsaglia, Generating * discrete random numbers in a computer, Comm ACM 6, 37-38 (1963). * If anybody is interested, I (jt) have also coded up this version as * part of another software package.  However, I've done some * comparisons, and the Walker method is both faster and more stingy * with memory.  So, in the end I decided not to include it with the * GSL package.    * Written 26 Jan 1999, James Theiler, jt@lanl.gov * Adapted to GSL, 30 Jan 1999, jt */#include <config.h>#include <stdio.h>              /* used for NULL, also fprintf(stderr,...) */#include <stdlib.h>             /* used for malloc's */#include <math.h>#include <gsl/gsl_rng.h>#include <gsl/gsl_randist.h>#define DEBUG 0#define KNUTH_CONVENTION 1      /* Saves a few steps of arithmetic                                 * in the call to gsl_ran_discrete()                                 *//*** Begin Stack (this code is used just in this file) ***//* Stack code converted to use unsigned indices (i.e. s->i == 0 now   means an empty stack, instead of -1), for consistency and to give a   bigger allowable range. BJG */typedef struct {    size_t N;                      /* max number of elts on stack */    size_t *v;                     /* array of values on the stack */    size_t i;                      /* index of top of stack */} gsl_stack_t;static gsl_stack_t *new_stack(size_t N) {    gsl_stack_t *s;    s = (gsl_stack_t *)malloc(sizeof(gsl_stack_t));    s->N = N;    s->i = 0;                  /* indicates stack is empty */    s->v = (size_t *)malloc(sizeof(size_t)*N);    return s;}static voidpush_stack(gsl_stack_t *s, size_t v){    if ((s->i) >= (s->N)) {        fprintf(stderr,"Cannot push stack!\n");        abort();                /* FIXME: fatal!! */    }    (s->v)[s->i] = v;    s->i += 1;}static size_t pop_stack(gsl_stack_t *s){    if ((s->i) == 0) {        fprintf(stderr,"Cannot pop stack!\n");        abort();               /* FIXME: fatal!! */    }    s->i -= 1;    return ((s->v)[s->i]);}static inline size_t size_stack(const gsl_stack_t *s){    return s->i;}static void free_stack(gsl_stack_t *s){    free((char *)(s->v));    free((char *)s);}/*** End Stack ***//*** Begin Walker's Algorithm ***/gsl_ran_discrete_t *gsl_ran_discrete_preproc(size_t Kevents, const double *ProbArray){    size_t k,b,s;    gsl_ran_discrete_t *g;    size_t nBigs, nSmalls;    gsl_stack_t *Bigs;    gsl_stack_t *Smalls;    double *E;    double pTotal = 0.0, mean, d;        if (Kevents < 1) {      /* Could probably treat Kevents=1 as a special case */      GSL_ERROR_VAL ("number of events must be a positive integer",                         GSL_EINVAL, 0);    }    /* Make sure elements of ProbArray[] are positive.     * Won't enforce that sum is unity; instead will just normalize     */    for (k=0; k<Kevents; ++k) {        if (ProbArray[k] < 0) {          GSL_ERROR_VAL ("probabilities must be non-negative",                            GSL_EINVAL, 0) ;        }        pTotal += ProbArray[k];    }    /* Begin setting up the main "object" (just a struct, no steroids) */    g = (gsl_ran_discrete_t *)malloc(sizeof(gsl_ran_discrete_t));    g->K = Kevents;    g->F = (double *)malloc(sizeof(double)*Kevents);    g->A = (size_t *)malloc(sizeof(size_t)*Kevents);    E = (double *)malloc(sizeof(double)*Kevents);    if (E==NULL) {      GSL_ERROR_VAL ("Cannot allocate memory for randevent", ENOMEM, 0);    }    for (k=0; k<Kevents; ++k) {        E[k] = ProbArray[k]/pTotal;    }    /* Now create the Bigs and the Smalls */    mean = 1.0/Kevents;    nSmalls=nBigs=0;    for (k=0; k<Kevents; ++k) {        if (E[k] < mean) ++nSmalls;        else             ++nBigs;    }    Bigs   = new_stack(nBigs);    Smalls = new_stack(nSmalls);    for (k=0; k<Kevents; ++k) {        if (E[k] < mean) {            push_stack(Smalls,k);        }        else {            push_stack(Bigs,k);        }    }    /* Now work through the smalls */    while (size_stack(Smalls) > 0) {        s = pop_stack(Smalls);        if (size_stack(Bigs) == 0) {            (g->A)[s]=s;            (g->F)[s]=1.0;            continue;        }        b = pop_stack(Bigs);        (g->A)[s]=b;        (g->F)[s]=Kevents*E[s];#if DEBUG        fprintf(stderr,"s=%2d, A=%2d, F=%.4f\n",s,(g->A)[s],(g->F)[s]);#endif                d = mean - E[s];        E[s] += d;              /* now E[s] == mean */        E[b] -= d;        if (E[b] < mean) {            push_stack(Smalls,b); /* no longer big, join ranks of the small */        }        else if (E[b] > mean) {            push_stack(Bigs,b); /* still big, put it back where you found it */        }        else {            /* E[b]==mean implies it is finished too */            (g->A)[b]=b;            (g->F)[b]=1.0;        }    }    while (size_stack(Bigs) > 0) {        b = pop_stack(Bigs);        (g->A)[b]=b;        (g->F)[b]=1.0;    }    /* Stacks have been emptied, and A and F have been filled */    if ( size_stack(Smalls) != 0) {      GSL_ERROR_VAL ("Smalls stack has not been emptied",                     GSL_ESANITY, 0 );    }    #if 0    /* if 1, then artificially set all F[k]'s to unity.  This will     * give wrong answers, but you'll get them faster.  But, not     * that much faster (I get maybe 20%); that's an upper bound     * on what the optimal preprocessing would give.     */    for (k=0; k<Kevents; ++k) {        (g->F)[k] = 1.0;    }#endif#if KNUTH_CONVENTION    /* For convenience, set F'[k]=(k+F[k])/K */    /* This saves some arithmetic in gsl_ran_discrete(); I find that     * it doesn't actually make much difference.     */    for (k=0; k<Kevents; ++k) {        (g->F)[k] += k;        (g->F)[k] /= Kevents;    }#endif        free_stack(Bigs);    free_stack(Smalls);    free((char *)E);    return g;}size_tgsl_ran_discrete(const gsl_rng *r, const gsl_ran_discrete_t *g){    size_t c=0;    double u,f;    u = gsl_rng_uniform(r);#if KNUTH_CONVENTION    c = (u*(g->K));#else    u *= g->K;    c = u;    u -= c;#endif    f = (g->F)[c];    /* fprintf(stderr,"c,f,u: %d %.4f %f\n",c,f,u); */    if (f == 1.0) return c;    if (u < f) {        return c;    }    else {        return (g->A)[c];    }}void gsl_ran_discrete_free(gsl_ran_discrete_t *g){    free((char *)(g->A));    free((char *)(g->F));    free((char *)g);}doublegsl_ran_discrete_pdf(size_t k, const gsl_ran_discrete_t *g){    size_t i,K;    double f,p=0;    K= g->K;    if (k>K) return 0;    for (i=0; i<K; ++i) {        f = (g->F)[i];#if KNUTH_CONVENTION        f = K*f-i;#endif                if (i==k) {            p += f;        } else if (k == (g->A)[i]) {            p += 1.0 - f;        }    }    return p/K;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -