## creating an arbitrary random distribution

there is several ways to achieve a certain given random distribution.
here two methods are shown, whereby the last one is the only reliable one
in terms of efficiency.

interesting links related to random distribution:

### [1] Monte-Carlo-Method:

The area of the function is used to specify whether a random x-y-pair falls below or above (Monte-Carlo-Method)
The distribution acts as a limit function and find values that meet this constraint.
The disadvantage is that the efficiency of this method might vary inexpectedly and even fail.

```(
g = { arg irpow=0.6, size=100;
var x, y, a, min, max;
a = Array.fill(size, { arg i; abs(i)**((1 / irpow) - 1) }).normalizeSum;
min = a.minItem;
max = a.maxItem;
a = a.collect { |el| el.linlin(min, max, 0, 1) };
while {
y = 1.0.rand;
x = a.blendAt(size.asFloat.rand);
//x.postln;
x < y
};
x

};
)

g.value;
```

### [2] Using an integrated function table

(why this works: see here)

```
(

f = {arg irpow=0.9, size=100;
var a, b, sum=0;
a = Array.fill(size, { arg i; abs(i)**((1 / irpow) - 1) });
size = a.size;
a = a.collect { |el| sum = sum + el }; // incrementally integrate
a = a.normalize(0, size-1); // divide by sum (maximum value) and scale by max index
b = Array.fill(size, { arg i; a.indexInBetween(i) });  // flip array
b = b / size // rescale to 0..1
}
)

z = f.value(0.9, 200);
z.plot;

// now this produces values of the desired distribution:
z.blendAt((z.size - 1).asFloat.rand);

```

### Using a histogram, one can test if the results are appropriate:

```
(
var randomNumbers, histogram, maxValue = 500.0, numVals = 100000, f, z, dfunc, size=200;

//dfunc = { arg i; var pow=0.056; i = i / size; abs(i)**((1 / pow) - 1) };

dfunc = { arg i; i = i / size; (sin(i * 80) * 0.3 + sin(i * 23)).max(0) };

f = { arg func, size = 100;
var a, b, sum=0;
a = Array.fill( size, func );
a = a.collect { |el| sum = sum + el }; // incrementally integrate
a = a.normalize(0, size-1); // divide by sum (maximum value) and scale by max index
b = Array.fill(size, { arg i; a.indexInBetween(i) });  // flip array
b = b / size // rescale to 0..1
};
z = f.value( dfunc, size );

// plot a sample of the underlying distribution function
Array.fill( size, { arg i; dfunc.value( i )}).plot("distribution function");

// now this produces values of the desired distribution:
randomNumbers = Array.fill(numVals, { z.blendAt((z.size.asFloat - 1).rand); }) * maxValue;
z.plot("mapping table");
histogram = Signal.newClear(maxValue);

randomNumbers.do({ arg each; var count, histoIndex;
histoIndex = each.asInteger;
count = histogram.at(histoIndex);
histogram.put(histoIndex, count + 1)
});

histogram.plot("histogram for distribution function application 0 - " ++ maxValue);
)

```

For the function:
`dfunc = { arg i; i = i / size; (sin(i  * 80) * 0.3 + sin(i  * 23)).max(0) };`

it results in the following relation between distribution function, scaling function and histogram of the results:

### Using an envelope as a distribution function:

```
(
var randomNumbers, histogram, maxValue = 500.0, numVals = 100000, f, z, dfunc, env, size=1500;

env = Env([0, 1, 0, 0.3, 1], [1, 1, 1, 1].normalizeSum, [-0.4,8, 'step', 'lin']).plot;

dfunc = { arg i; env[i / size] };

f = { arg func, size=100;
var a, b, sum=0;
a = Array.fill(size, func);
a = a.collect { |el| sum = sum + el }; // incrementally integrate
a = a.normalize(0, size-1); // divide by sum (maximum value) and scale by max index
b = Array.fill(size, { arg i; a.indexInBetween(i) });  // flip array
b = b / size // rescale to 0..1
};

z = f.value(dfunc, 2000);

// now this produces values of the desired distribution:

randomNumbers = Array.fill(numVals, { z.blendAt((z.size - 1).asFloat.rand); }) * maxValue;
z.plot("mapping table");
histogram = Signal.newClear(maxValue);

randomNumbers.do({ arg each; var count, histoIndex;
histoIndex = each.asInteger;
count = histogram.at(histoIndex);
histogram.put(histoIndex, count + 1)
});

histogram.plot("histogram for distribution function application 0 - " ++ maxValue);
)

```

### asRandomTable and tableRand are methods part of supercollider which implement the above algorithm.

```
// keep z and c as a global random table
// using the "l" key (when th plot view is in focus) you can unlock the plot view and draw into the array.
// have a look at the mapping table: it changes only a very little, but this change has
// a distinct effect on the distribution

(
var v, env, size=180; // keep w, z, d global

env = Env([0, 1, 0, 0, 1, 0], [0.3, 1, 1, 1, 0.2].normalizeSum, [-0.4,8, 'step', 'lin']);
c = Array.fill(size, { arg i; env[i / size] });

z = c.asRandomTable;

// views:

v = z.plot("mapping table");
w = c.plot("distribution function");

// now this produces values of the desired distribution:

f = { z = c.asRandomTable; d.value = z;  };

// dig for multislider view.
w.view.children[0].children[1].mouseUpAction = { f.value; };

)

```

```// create a synth definition for a short grain:
(
SynthDef(\pgrain,
{ arg out = 0, freq=800, sustain=0.001, amp=0.5, pan = 0;
var window;
window = Env.sine(sustain, amp);
Out.ar(out,
Pan2.ar(
SinOsc.ar(freq),
pan
) * EnvGen.ar(window, doneAction:2)
)
}
).store;
)
```

```// play a task definition.

Tdef(\x).play;

```

```// this definition can be changed on the fly :
(
Tdef(\x, {
loop {
s.sendBundle(0.2,
["/s_new", \pgrain, -1, 1, 1, \freq, z.tableRand.linexp(0,1,200, 18000)]
);
0.03.wait;
}

});
)

```

### Server side random distribution

```

// tuning randomness on the server with the above created randomTable
// using waveshaping, the same method can be done on an audio signal:

// allocate a buffer and write the random table into them
s.sendBundle(nil, ["/b_alloc", 144, 1, z.size, ["/b_setn", 144, 0, z.size] ++ z]);

// play a placeholder proxy
Ndef(\x).play;

// use the random values for sine frequency distribution
(
Ndef(\x, {
var inRand, outRand;
inRand = TRand.kr(0, 1.0, Impulse.kr(MouseY.kr(0.2, 400, 1)));
outRand = Shaper.kr(144, inRand);
SinOsc.ar(
LinExp.kr(outRand, 0, 1, 300, 7000)
) * 0.1

});
)

// or for colored noise:

(
Ndef(\x, {
var inRand, outRand;
inRand = WhiteNoise.ar;
outRand = Shaper.ar(144, inRand);
})
)

// to continue editing the distribution with the GUI left from above, use this function:
(
f = {
z = c.asRandomTable;
d.value = z;
s.sendBundle(nil, ["/b_setn", 144, 1, z.size] ++ z);

};
)

// end, clear variables
d = z = w = nil;
Ndef(\x).clear;
Tdef(\x).clear;
```