2016 Update
This commit is contained in:
parent
948b86eafa
commit
dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
53
Task/Subtractive-generator/Java/subtractive-generator.java
Normal file
53
Task/Subtractive-generator/Java/subtractive-generator.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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.#
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue