If At First You Don't Succeed


Last time[1] we took a first look at Bernoulli processes which are formed from a sequence of independent experiments, known as Bernoulli trials, each of which is governed by the Bernoulli distribution with a probability \(p\) of success. Since the outcome of one trial has no effect upon the next, such processes are memoryless meaning that the number of trials that we need to perform before getting a success is independent of how many we have already performed whilst waiting for one.
We have already seen[2] that if waiting times for memoryless events with fixed average arrival rates are continuous then they must be exponentially distributed and in this post we shall be looking at the discrete analogue.

A Series Of Unfortunate Events

Since the trials are independent, the probability of having \(k\) failures in a row is trivially
\[ (1-p)^k \]
If we represent the number of failures before a success with a random variable \(K\) then its probability mass function, or PMF, must be
\[ f_p(k) = \Pr(K = k) = (1-p)^k \times p \]
for non-negative integer \(k\) and zero otherwise. To calculate its cumulative distribution function, or CDF, we simply add up values of the PMF with
\[ \begin{align*} F_p(k) &= \Pr(K \leqslant k) = \Pr\left(K \leqslant \lfloor k \rfloor\right)\\ &= \sum_{j=0}^{\lfloor k \rfloor} f_p(j) = \sum_{j=0}^{\lfloor k\rfloor} (1-p)^j \times p \end{align*} \]
where the odd shaped brackets represent the floor of the value between them, being the largest integer that is less than or equal to it, and \(\sum\) is the summation sign. Now this is simply a geometric series and so we have
\[ F_p(k) = \begin{cases} \dfrac{p \times \left(1-(1-p)^{\lfloor k \rfloor +1}\right)}{1 - (1-p)} = 1 - (1-p)^{\lfloor k \rfloor + 1} & k \geqslant 0\\ 0 & \text{otherwise} \end{cases}\\ \]
Derivation 1: The Inverse CDF
Firstly, define
\[ \begin{align*} c &= 1 - (1-p)^{k+1}\\ 1-c &= (1-p)^{k+1} \end{align*} \]
Taking logarithms yields
\[ \begin{align*} \ln (1-c) &= (k+1) \times \ln (1-p)\\ k &= \frac{\ln (1-c)}{\ln (1-p)} - 1 \end{align*} \]
The result follows from the fact that if \(c\) is greater than \(F_p\left(\lfloor k \rfloor\right)\) then \(k\) will be greater than \(\lfloor k \rfloor\) and therefore within the accumulated mass at \(\lceil k \rceil\).
because of which the distribution of \(K\) is called the geometric distribution. The inverse of the CDF is given by
\[ F_p^{-1}(c) = \Bigg\lceil \frac{\ln (1-c)}{\ln (1-p)} - 1 \Bigg\rceil \]
where these odd shaped brackets represent the ceiling of the value between them, being the smallest integer that is greater than or equal to it, as shown by derivation 1.

To make observations of \(K\) we can simply evaluate the inverse at the result of a standard uniformly distributed random variable
\[ \begin{align*} U &\sim U(0, 1)\\ K &= F_p^{-1}(U) \end{align*} \]
Finally, we have the characteristic function, or CF, which is defined by
\[ \hat{f_p}(t) = E\left[e^{itK}\right] \]
where \(E\) represents the expected value of the term within the square brackets following it and \(i\) is the imaginary unit, and equals
Derivation 2: The Limit
Defining \(p = \frac{\lambda}{n}\) and \(X = \frac{K+1}{n}\) we have
\[ \begin{align*} \Pr(X \leqslant x) &= \Pr(K \leqslant n \times x - 1)\\ &= 1 - \left(1-\frac{\lambda}{n}\right)^{\lfloor n \times x - 1\rfloor + 1} \end{align*} \]
Taking the limit as \(n\) tends to infinity yields
\[ \begin{align*} \lim_{n \rightarrow \infty}\Pr(X \leqslant x) &= \lim_{n \rightarrow \infty} \left(1 - \left(1-\frac{\lambda}{n}\right)^{n \times x}\right)\\ &= 1 - \left(\lim_{n \rightarrow \infty} \left(1-\frac{\lambda}{n}\right)^n\right)^x\\ &= 1 - \left(e^{-\lambda}\right)^x = 1 - e^{-\lambda \times x} \end{align*} \]
which is the exponential CDF.
\[ \begin{align*} \hat{f_p}(t) &= \sum_{k=0}^\infty (1-p)^k \times p \times e^{itk}\\ &= \sum_{k=0}^\infty p \times \left((1-p)\times e^{it}\right)^k \end{align*} \]
This is another geometric series and so we have
\[ \begin{align*} \hat{f_p}(t) &= p \times \frac{1-\left((1-p) \times e^{it}\right)^\infty}{1-(1-p) \times e^{it}}\\ &= \frac{p}{1-(1-p) \times e^{it}} \end{align*} \]
provided that \(1-p\) is less than one.

Note that the geometric and exponential distributions are the only memoryless waiting time distributions and that the latter is the limit of the former as the probability of success tends to zero and the number of trials tends to infinity, as shown by derivation 2.
We have previously seen that the minimum of \(n\) independent observations of an exponential random variable is also exponentially distributed[3], which is another property that it shares with the geometric distribution, as proven in derivation 3.

Derivation 3: The Minimum
Firstly note that
\[ \Pr(K > k) = 1 - \Pr(K \leqslant k) = (1-p)^{\lfloor k \rfloor + 1} \]
and secondly note that if the minimum of the \(K_i\) is greater than \(k\) then they must all be greater than \(k\) so that
\[ \begin{align*} \Pr\left(\min_{1 \leqslant i \leqslant n} K_i > k\right) &= \Pr\left(K_1 > k \wedge K_2 > k \wedge \dots \wedge K_n >k\right)\\ &= \Pr\left(K_1 > k\right) \times \Pr\left(K_2 > k\right) \times \dots \times \Pr\left(K_n >k\right)\\ &= (1-p)^{\lfloor k \rfloor + 1} \times (1-p)^{\lfloor k \rfloor + 1} \times \dots \times (1-p)^{\lfloor k \rfloor + 1}\\ &= \left((1-p)^{\lfloor k \rfloor + 1}\right)^n = \left((1-p)^{n}\right)^{\,\lfloor k \rfloor + 1} \end{align*} \]
where \(\wedge\) means and, and consequently
\[ \Pr\left(\min_{1 \leqslant i \leqslant n} K_i \leqslant k\right) = 1 - \left(1-p^\prime\right)^{\,\lfloor k \rfloor + 1} = F_{p^\prime}(k) \]
where \(p^\prime = 1-(1-p)^n\).

The ak Geometric Distribution Functions

The implementation of the geometric distribution follows our usual conventions, as demonstrated by ak.geometricPMF in listing 1.

Listing 1: ak.geometricPMF
function pmf(p, k) {
 if(k>=0 && k===ak.floor(k)) return Math.pow(1-p, k)*p;
 return isNaN(k) ? ak.NaN : 0;

ak.geometricPMF = function() {
 var state = {p: 0.5};
 var arg0  = arguments[0];
 var f, ll;

 constructors[ak.nativeType(arg0)](state, arg0, arguments);

 f = function(k){return pmf(state.p, k);};
 f.p = function(){return state.p;};
 return Object.freeze(f);

var constructors = {};

Here the pmf helper function performs the actual calculation and the state object is initialised by dispatch to a constructors object. Listing 2 gives those for default and specified success probability initialisation.

Listing 2: The Constructors
constructors[ak.UNDEFINED_T] = function() {

constructors[ak.NUMBER_T] = function(state, p, args) {
 var arg1 = args[1];
 constructors[ak.NUMBER_T][ak.nativeType(arg1)](state, arg1, args);

 state.p = Number(p);
 if(!(state.p>0 && state.p<1)) {
  throw new Error('invalid p in ak.geometric distribution');

constructors[ak.NUMBER_T][ak.UNDEFINED_T] = function(state) {

Note that, as we did for the Bernoulli distribution, we're constraining the probability of success to lie strictly between zero and one.
Program 1 plots the PMF for a range of success probabilities.

Program 1: The Geometric PMF

The CDF is even simpler, as shown by listing 3.

Listing 3: ak.geometricCDF
ak.geometricCDF = function() {
 var state = {p: 0.5};
 var arg0  = arguments[0];
 var f;

 constructors[ak.nativeType(arg0)](state, arg0, arguments);

 f = function(k){return k<0 ? 0 : 1-Math.pow(1-state.p, ak.floor(k)+1);};
 f.p = function(){return state.p;};
 return Object.freeze(f);

Program 2 demonstrates its correctness by comparing the PMF at \(k\) to the difference between the CDF at \(k\) and at \(k-1\).

Program 2: The PMF Versus The CDF

In order to cope with rounding errors, when calculating the inverse of the CDF we round down instead of up and then increment the result until the value of the CDF is no less than its argument, as done by the invCDF helper function in listing 4.

Listing 4: ak.geometricInvCDF
function invCDF(p, ln1p, c) {
 var k;
 if(isNaN(c)) return ak.NaN;
 if(c>=1)     return ak.INFINITY;
 if(c<=0)     return 0;

 k = ak.floor(Math.log(1-c)/ln1p - 1);
 while(1-Math.pow(1-p, k+1)<c) ++k;
 return k;

ak.geometricInvCDF = function() {
 var state = {p: 0.5};
 var arg0  = arguments[0];
 var ln1p, f;

 constructors[ak.nativeType(arg0)](state, arg0, arguments);

 ln1p = Math.log(1-state.p);

 f = function(c){return invCDF(state.p, ln1p, c);};
 f.p = function(){return state.p;};
 return Object.freeze(f);

Note that ak.geometricInvCDF is pre-calculating the logarithm of \(1-p\) to avoid recalculating it every time the helper function is called.
Program 3 compares the inverse of the CDF's result to its argument.

Program 3: The Inverse CDF

To calculate the CF we shall exploit the facts that
\[ e^{i\theta} = \cos \theta + i \times \sin \theta \]
\[ \frac{1}{x - i \times y} = \frac{x + i \times y}{(x + i \times y) \times (x - i \times y)} = \frac{x + i \times y}{x^2 + y^2} \]
so that we can directly initialise an ak.complex object with the real and imaginary parts of its results. The cf helper function does so for ak.geometricCF in listing 5

Listing 5: ak.geometricCF
function cf(p, t) {
 var ct = Math.cos(t);
 var st = Math.sin(t);
 var re = 1-(1-p)*ct;
 var im = (1-p)*st;
 var d  = re*re+im*im;
 return ak.complex(p*re/d, p*im/d);

ak.geometricCF = function() {
 var state = {p: 0.5};
 var arg0  = arguments[0];
 var f;

 constructors[ak.nativeType(arg0)](state, arg0, arguments);

 f = function(t){return cf(state.p, t);};
 f.p = function(){return state.p;};
 return Object.freeze(f);

and program 4 plots the real and imaginary parts of its results in black and red respectively.

Program 4: The Geometric CF

Finally we have the random number generator for which we need constructors to allow the specification of standard uniform random number generators other than the default Math.random, as provided in listing 6.

Listing 6: ak.geometricRnd
ak.geometricRnd = function() {
 var state = {p: 0.5, rnd: Math.random};
 var arg0  = arguments[0];
 var ln1p, f;

 constructors[ak.nativeType(arg0)](state, arg0, arguments);

 ln1p = Math.log(1-state.p);

 f = function(){return invCDF(state.p, ln1p, state.rnd());};

 f.p = function(){return state.p;};
 f.rnd = function(){return state.rnd;};
 return Object.freeze(f);

constructors[ak.FUNCTION_T] = function(state, rnd) {
 state.rnd = rnd;

constructors[ak.NUMBER_T][ak.FUNCTION_T] = function(state, rnd) {
 state.rnd = rnd;

Here we're simply calling the helper function invCDF with the result of the state.rnd generator.
Program 5 illustrates the use of ak.geometricRnd by comparing a histogram of its results to a graph of the geometric PMF.

Program 5: ak.geometricRnd

You will find the implementations of these functions in GeometricDistribution.js and we shall take a look at another property of Bernoulli processes next time.


[1] One Thing Or Another, www.thusspakeak.com, 2020.

[2] How Long Must I Wait?, www.thusspakeak.com, 2015.

[3] The Longest Wait, www.thusspakeak.com, 2015.

Leave a comment