2016 Update

This commit is contained in:
Tina Müller 2016-12-05 22:15:40 +01:00
parent 948b86eafa
commit dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions

View file

@ -1,34 +1,35 @@
A ''subtractive generator'' calculates a sequence of [[random number generator|random numbers]], where each number is congruent to the subtraction of two previous numbers from the sequence. <br>
The formula is
* <math>r_n = r_{(n - i)} - r_{(n - j)} \pmod m</math>
* <big><math>r_n = r_{(n - i)} - r_{(n - j)} \pmod m</math></big>
for some fixed values of <math>i</math>, <math>j</math> and <math>m</math>, all positive integers. Supposing that <math>i > j</math>, then the state of this generator is the list of the previous numbers from <math>r_{n - i}</math> to <math>r_{n - 1}</math>. Many states generate uniform random integers from <math>0</math> to <math>m - 1</math>, but some states are bad. A state, filled with zeros, generates only zeros. If <math>m</math> is even, then a state, filled with even numbers, generates only even numbers. More generally, if <math>f</math> is a factor of <math>m</math>, then a state, filled with multiples of <math>f</math>, generates only multiples of <math>f</math>.
for some fixed values of <big><math>i</math></big>, <big><math>j</math></big> and <big><math>m</math></big>, all positive integers. Supposing that <big><math>i > j</math></big>, then the state of this generator is the list of the previous numbers from <big><math>r_{n - i}</math></big> to <big><math>r_{n - 1}</math></big>. Many states generate uniform random integers from <big><math>0</math></big> to <big><math>m - 1</math></big>, but some states are bad. A state, filled with zeros, generates only zeros. If <big><math>m</math></big> is even, then a state, filled with even numbers, generates only even numbers. More generally, if <big><math>f</math></big> is a factor of <big><math>m</math></big>, then a state, filled with multiples of <big><math>f</math></big>, generates only multiples of <big><math>f</math></big>.
All subtractive generators have some weaknesses. The formula correlates <math>r_n</math>, <math>r_{(n - i)}</math> and <math>r_{(n - j)}</math>; these three numbers are not independent, as true random numbers would be. Anyone who observes <math>i</math> consecutive numbers can predict the next numbers, so the generator is not cryptographically secure. The authors of ''Freeciv'' ([http://svn.gna.org/viewcvs/freeciv/trunk/utility/rand.c?view=markup utility/rand.c]) and ''xpat2'' (src/testit2.c) knew another problem: the low bits are less random than the high bits.
All subtractive generators have some weaknesses. The formula correlates <big><math>r_n</math></big>, <big><math>r_{(n - i)}</math></big> and <big><math>r_{(n - j)}</math></big>; these three numbers are not independent, as true random numbers would be. Anyone who observes <big><math>i</math></big> consecutive numbers can predict the next numbers, so the generator is not cryptographically secure. The authors of ''Freeciv'' ([http://svn.gna.org/viewcvs/freeciv/trunk/utility/rand.c?view=markup utility/rand.c]) and ''xpat2'' (src/testit2.c) knew another problem: the low bits are less random than the high bits.
The subtractive generator has a better reputation than the [[linear congruential generator]], perhaps because it holds more state. A subtractive generator might never multiply numbers: this helps where multiplication is slow. A subtractive generator might also avoid division: the value of <math>r_{(n - i)} - r_{(n - j)}</math> is always between <math>-m</math> and <math>m</math>, so a program only needs to add <math>m</math> to negative numbers.
The subtractive generator has a better reputation than the [[linear congruential generator]], perhaps because it holds more state. A subtractive generator might never multiply numbers: this helps where multiplication is slow. A subtractive generator might also avoid division: the value of <big><math>r_{(n - i)} - r_{(n - j)}</math></big> is always between <big><math>-m</math></big> and <big><math>m</math></big>, so a program only needs to add <big><math>m</math></big> to negative numbers.
The choice of <math>i</math> and <math>j</math> affects the period of the generator. A popular choice is <math>i = 55</math> and <math>j = 24</math>, so the formula is
The choice of <big><math>i</math></big> and <big><math>j</math></big> affects the period of the generator. A popular choice is <big><math>i = 55</math></big> and <big><math>j = 24</math></big>, so the formula is
* <math>r_n = r_{(n - 55)} - r_{(n - 24)} \pmod m</math>
* <big><math>r_n = r_{(n - 55)} - r_{(n - 24)} \pmod m</math></big>
The subtractive generator from ''xpat2'' uses
* <math>r_n = r_{(n - 55)} - r_{(n - 24)} \pmod{10^9}</math>
* <big><math>r_n = r_{(n - 55)} - r_{(n - 24)} \pmod{10^9}</math></big>
The implementation is by J. Bentley and comes from program_tools/universal.c of [ftp://dimacs.rutgers.edu/pub/netflow/ the DIMACS (netflow) archive] at Rutgers University. It credits Knuth, [[wp:The Art of Computer Programming|''TAOCP'']], Volume 2, Section 3.2.2 (Algorithm A).
Bentley uses this clever algorithm to seed the generator.
# Start with a single <math>seed</math> in range <math>0</math> to <math>10^9 - 1</math>.
# Set <math>s_0 = seed</math> and <math>s_1 = 1</math>. The inclusion of <math>s_1 = 1</math> avoids some bad states (like all zeros, or all multiples of 10).
# Compute <math>s_2, s_3, ..., s_{54}</math> using the subtractive formula <math>s_n = s_{(n - 2)} - s_{(n - 1)} \pmod{10^9}</math>.
# Reorder these 55 values so <math>r_0 = s_{34}</math>, <math>r_1 = s_{13}</math>, <math>r_2 = s_{47}</math>, ..., <math>r_n = s_{(34 * (n + 1) \pmod{55})}</math>.
#* This is the same order as <math>s_0 = r_{54}</math>, <math>s_1 = r_{33}</math>, <math>s_2 = r_{12}</math>, ..., <math>s_n = r_{((34 * n) - 1 \pmod{55})}</math>.
# Start with a single <big><math>seed</math></big> in range <big><math>0</math></big> to <big><math>10^9 - 1</math></big>.
# Set <big><math>s_0 = seed</math></big> and <big><math>s_1 = 1</math></big>. The inclusion of <big><math>s_1 = 1</math></big> avoids some bad states (like all zeros, or all multiples of 10).
# Compute <big><math>s_2, s_3, ..., s_{54}</math></big> using the subtractive formula <big><math>s_n = s_{(n - 2)} - s_{(n - 1)} \pmod{10^9}</math></big>.
# Reorder these 55 values so <big><math>r_0 = s_{34}</math></big>, <big><math>r_1 = s_{13}</math></big>, <big><math>r_2 = s_{47}</math></big>, ..., <big><math>r_n = s_{(34 * (n + 1) \pmod{55})}</math></big>.
#* This is the same order as <big><math>s_0 = r_{54}</math></big>, <big><math>s_1 = r_{33}</math></big>, <big><math>s_2 = r_{12}</math></big>, ..., <big><math>s_n = r_{((34 * n) - 1 \pmod{55})}</math></big>.
#* This rearrangement exploits how 34 and 55 are relatively prime.
# Compute the next 165 values <math>r_{55}</math> to <math>r_{219}</math>. Store the last 55 values.
# Compute the next 165 values <big><math>r_{55}</math></big> to <big><math>r_{219}</math></big>. Store the last 55 values.
This generator yields the sequence <math>r_{220}</math>, <math>r_{221}</math>, <math>r_{222}</math> and so on. For example, if the seed is 292929, then the sequence begins with <math>r_{220} = 467478574</math>, <math>r_{221} = 512932792</math>, <math>r_{222} = 539453717</math>. By starting at <math>r_{220}</math>, this generator avoids a bias from the first numbers of the sequence. This generator must store the last 55 numbers of the sequence, so to compute the next <math>r_n</math>. Any array or list would work; a [[ring buffer]] is ideal but not necessary.
This generator yields the sequence <big><math>r_{220}</math></big>, <big><math>r_{221}</math></big>, <big><math>r_{222}</math></big> and so on. For example, if the seed is 292929, then the sequence begins with <big><math>r_{220} = 467478574</math></big>, <big><math>r_{221} = 512932792</math></big>, <big><math>r_{222} = 539453717</math></big>. By starting at <big><math>r_{220}</math></big>, this generator avoids a bias from the first numbers of the sequence. This generator must store the last 55 numbers of the sequence, so to compute the next <big><math>r_n</math></big>. Any array or list would work; a [[ring buffer]] is ideal but not necessary.
Implement a subtractive generator that replicates the sequences from ''xpat2''.
<br><br>

View file

@ -0,0 +1,20 @@
defmodule Subtractive do
def new(seed) when seed in 0..999_999_999 do
s = Enum.reduce(1..53, [1, seed], fn _,[a,b|_]=acc -> [b-a | acc] end)
|> Enum.reverse
|> List.to_tuple
state = for i <- 1..55, do: elem(s, rem(34*i, 55))
{:ok, _pid} = Agent.start_link(fn -> state end, name: :Subtractive)
Enum.each(1..220, fn _ -> rand end) # Discard first 220 elements of sequence.
end
def rand do
state = Agent.get(:Subtractive, &(&1))
n = rem(Enum.at(state, -55) - Enum.at(state, -24) + 1_000_000_000, 1_000_000_000)
:ok = Agent.update(:Subtractive, fn _ -> tl(state) ++ [n] end)
hd(state)
end
end
Subtractive.new(292929)
for _ <- 1..10, do: IO.puts Subtractive.rand

View file

@ -0,0 +1,53 @@
import java.util.function.IntSupplier;
import static java.util.stream.IntStream.generate;
public class SubtractiveGenerator implements IntSupplier {
static final int MOD = 1_000_000_000;
private int[] state = new int[55];
private int si, sj;
public SubtractiveGenerator(int p1) {
subrandSeed(p1);
}
void subrandSeed(int p1) {
int p2 = 1;
state[0] = p1 % MOD;
for (int i = 1, j = 21; i < 55; i++, j += 21) {
if (j >= 55)
j -= 55;
state[j] = p2;
if ((p2 = p1 - p2) < 0)
p2 += MOD;
p1 = state[j];
}
si = 0;
sj = 24;
for (int i = 0; i < 165; i++)
getAsInt();
}
@Override
public int getAsInt() {
if (si == sj)
subrandSeed(0);
if (si-- == 0)
si = 54;
if (sj-- == 0)
sj = 54;
int x = state[si] - state[sj];
if (x < 0)
x += MOD;
return state[si] = x;
}
public static void main(String[] args) {
generate(new SubtractiveGenerator(292_929)).limit(10)
.forEach(System.out::println);
}
}

View file

@ -0,0 +1,46 @@
function Get-SubtractiveRandom ( [int]$Seed )
{
function Mod ( [int]$X, [int]$M = 1000000000 ) { ( $X % $M + $M ) % $M }
If ( $Seed )
{
$R = New-Object int[] 55
$N1 = 55 - 1
$N2 = ( $N1 + 34 ) % 55
$R[$N1] = $Seed
$R[$N2] = 1
ForEach ( $x in 2..(55-1) )
{
$N0, $N1, $N2 = $N1, $N2, ( ( $N2 + 34 ) % 55 )
$R[$N2] = Mod ( $R[$N0] - $R[$N1] )
}
$i = -55 - 1
$j = -24 - 1
ForEach ( $x in 55..219 )
{
$i = ++$i % 55
$j = ++$j % 55
$R[$i] = Mod ( $R[$i] - $R[$j] )
}
$Script:RandomRing = $R
$Script:RandomIndex = $i
}
$i = $Script:RandomIndex = ++$Script:RandomIndex % 55
$j = ( $i + 55 - 24 ) % 55
return ( $Script:RandomRing[$i] = Mod ( $Script:RandomRing[$i] - $Script:RandomRing[$j] ) )
}
Get-SubtractiveRandom 292929
Get-SubtractiveRandom
Get-SubtractiveRandom
Get-SubtractiveRandom
Get-SubtractiveRandom

View file

@ -1,23 +1,24 @@
/*REXX pgm uses a subtractive generator, creates a sequence of random numbers.*/
numeric digits 20; s.0=292929; s.1=1; billion=10**9
cI=55; cJ=24; cP=34; billion=1e9 /* [↑] same*/
do i=2 to cI-1
s.i=mod(s(i-2) - s(i-1), billion)
end /*i*/
do j=0 to cI-1
r.j=s(mod(cP*(j+1), cI))
end /*j*/
m=219
do k=cI to m; x=k//cI
r.x=mod(r(mod(k-cI, cI)) - r(mod(k-cJ, cI)), billion)
end /*m*/
/*REXX program uses a subtractive generator, and creates a sequence of random numbers. */
s.0=292929; s.1=1; billion=10**9 /* ◄────────┐ */
numeric digits 20; billion=1e9 /*same as─►─┘ */
cI=55; do i=2 to cI-1
s.i=mod(s(i-2) - s(i-1), billion)
end /*i*/
Cp=34
do j=0 to cI-1
r.j=s(mod(cP*(j+1), cI))
end /*j*/
m=219; Cj=24
do k=cI to m; _=k//cI
r._=mod(r(mod(k-cI, cI)) - r(mod(k-cJ, cI)), billion)
end /*m*/
t=235
do n=m+1 to t; y=n//cI
r.y=mod(r(mod(n-cI, cI)) - r(mod(n-cJ, cI)), billion)
say right(r.y, 40)
end /*n*/
exit /*stick a fork in it, we're all done. */
/*────────────────────────────────────────────────────────────────────────────*/
mod: procedure; parse arg a,b; return ((a // b) + b) // b
r: parse arg _; return r._
s: parse arg _; return s._
do n=m+1 to t; _=n//cI
r._=mod(r(mod(n-cI, cI)) - r(mod(n-cJ, cI)), billion)
say right(r._, 40)
end /*n*/
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
mod: procedure; parse arg a,b; return ((a // b) + b) // b
r: parse arg #; return r.#
s: parse arg #; return s.#