1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
|
/*
Probability RNGs by Lance Putnam (lance@uwalumni.com)
These classes were ported from BASIC from the book
Automated Music Composition by Phil Winsor.
The number receiving the PDF message is by default the mean of the
distribution and is referred to as 'this'.
*/
+SimpleNumber {
/* Eularian Beta Distribution
Smaller prob values produce higher probabilities near respective
boundaries. The mean is 'this'/('this'+'val2'). Be careful when
using prob values > 5 as this can use a lot of CPU.
Special cases:
prob1<1, prob2<1
The probabilities are greatest near 'this' and 'val2'.
prob1=prob2=1
This is a uniform distribution.
prob1>1, prob2>1
This a bounded Gaussian-type distribution. The mean is the
midpoint of the range if prob1=prob2. If prob1 != prob2,
the mean will be skewed to the lower probabilities respective
bound.
prob1=1, prob2=2
This is a linear distribution.
val2: values will be in the range ['this', 'val2']
prob1: determines the probability of values near 'this'
prob2: determines the probability of values near 'val2'
*/
betarand { arg val2=1, prob1=1, prob2=1;
var invProb1, invProb2, temp, sum;
invProb1 = 1/prob1;
invProb2 = 1/prob2;
sum = 2;
while({
sum > 1;
},{
temp = 1.0.rand ** invProb1;
sum = temp + (1.0.rand ** invProb2);
});
^( ((temp/sum) * (val2-this)) + this )
}
/* Cauchy Distribution
A symmetric distribution centered around mean. It is unbounded
above and below the mean similar to the Gaussian dist. but with
a higher occurance of remote values.
spread: determines horizontal dispersion of values along curve
*/
cauchy { arg spread=1.0;
var randNum;
randNum = 0.5;
while({
randNum == 0.5; // to avoid discontinuity at tan(pi/2)
},{
randNum = 1.0.rand;
});
^( (spread * tan(randNum * pi)) + this )
}
/* Gaussian Distribution (aka Normal Distribution)
Generates a bell-shaped curve centered around mean. Good for
simulating life-like behavior and mutations.
dev: determines the spread of values above and below mean
*/
// fast Box-Muller algorithm
gaussian { arg dev=1;
^(((-2*log(1.0.rand)).sqrt * sin(2pi.rand)) * dev) + this
}
/* Knuth's Box-Muller algorithm (not as fast as above)
gaussian2 { arg dev=1;
var result, r, theta, x, y;
//if(valBM.notNil, {
// result = valBM;
// valBM = nil;
// ^((result * dev) + this)
//},{
result = 2;
while({
result >= 1;
},{
x = rrand(-1.0,1);
y = rrand(-1.0,1);
result = (x*x) + (y*y);
});
result = ((-2 * log(result))/result).sqrt;
//valBM = result * x;
y = result * y;
^((y * dev) + this)
//});
}
*/
/* Linear Distribution
This is similar to exprand, but with a looser distribution.
Values closer to 'this' are more likely to occur.
val2: Produces values in range ['this', 'val2'].
*/
linrrand { arg val2=1.0;
var randNum, result;
randNum = 1.0.rand;
result = 1.0.rand;
if( randNum < result, { result = randNum })
^(result*(val2-this) + this)
}
logistic { arg spread=1;
var u;
u = 1.0.rand;
^( (log(u/(1-u)))*spread + this )
}
pareto { arg shape=1;
^( (1.0.rand ** (-1/shape))*this )
}
/* Poisson Distribution
This function models the occurance rate of rare events.
This is a discrete distribution that returns positive integer values.
The distribution of values is unbounded at the upper end. The mean
and variance of the function are 'this'.
*/
poisson {
var count, someNum, temp;
count = 0;
someNum = 1.0.rand;
temp = exp(this.neg);
while({
someNum > temp;
},{
count = count + 1;
someNum = someNum * 1.0.rand;
});
^count
}
/* Weibull Distribution
This is a complex, yet powerful, distribution relying on 3 parameters.
These are known as the location, spread, and shape parameters.
The 'location' and 'spread' params are used interchangeably to set the
mean. The 'shape' param alters the curve of the distribution.
Effects of 'shape'
0 < 'shape' <= 1
-occurance of values near 'location' increases as 'shape'->0.
'shape' = 1
-same as exponential distribution
this: location, or offset, parameter
spread: distribution range scaling factor
shape: curve shaping parameter
*/
weibull { arg spread, shape=1;
var randNum = 1.0;
// this will avoid a div by 0
while({
randNum == 1.0
},{
randNum = 1.0.rand;
});
^( (spread* (log(randNum).neg ** (1.0/shape))) + this )
}
}
+ SequenceableCollection {
// in place gaussian operation on array
gaussian { arg dev=1;
var result, r, theta, pairs, inc;
pairs = this.size.div(2);
inc = 0;
pairs.do({
r = ((-2*log(1.0.rand)).sqrt) * dev;
theta = 2pi.rand;
this[inc] = this[inc] + (r * cos(theta));
inc = inc + 1;
this[inc] = this[inc] + (r * sin(theta));
inc = inc + 1;
});
if(inc < this.size, {
this[inc] = this[inc].gaussian(dev);
});
^this
}
cauchy { arg spread=1; ^this.performBinaryOp('cauchy', spread) }
logistic { arg spread=1; ^this.performBinaryOp('logistic', spread) }
pareto { arg shape=1; ^this.performBinaryOp('pareto', shape) }
poisson { ^this.performUnaryOp('poisson') }
}
|