Browse Source

* replaced random generator with the Mersenne twister, which is about
3.5 times faster

Jonas Maebe 22 years ago
parent
commit
3f4f17e1f6
1 changed files with 144 additions and 63 deletions
  1. 144 63
      rtl/inc/system.inc

+ 144 - 63
rtl/inc/system.inc

@@ -37,9 +37,6 @@ const
   STACK_MARGIN = 16384;    { Stack size margin for stack checking }
   STACK_MARGIN = 16384;    { Stack size margin for stack checking }
 { Random / Randomize constants }
 { Random / Randomize constants }
   OldRandSeed : Cardinal = 0;
   OldRandSeed : Cardinal = 0;
-  InitialSeed : Boolean = TRUE;
-  Seed2 : Cardinal = 0;
-  Seed3 : Cardinal = 0;
 
 
 { For Error Handling.}
 { For Error Handling.}
   ErrorBase : Pointer = nil;
   ErrorBase : Pointer = nil;
@@ -282,88 +279,168 @@ end;
 
 
 {$i rtti.inc}
 {$i rtti.inc}
 
 
-{****************************************************************************
-                          Random function routines
 
 
-        This implements a very long cycle random number generator by combining
-   three independant generators.  The technique was described in the March
-   1987 issue of Byte.
-   Taken and modified with permission from the PCQ Pascal rtl code.
-****************************************************************************}
 
 
-{$R-}
-{$Q-}
+{----------------------------------------------------------------------
+   Mersenne Twister: A 623-Dimensionally Equidistributed Uniform
+   Pseudo-Random Number Generator.
+
+   What is Mersenne Twister?
+   Mersenne Twister(MT) is a pseudorandom number generator developped by
+   Makoto Matsumoto and Takuji Nishimura (alphabetical order) during
+   1996-1997. MT has the following merits:
+   It is designed with consideration on the flaws of various existing
+   generators.
+   Far longer period and far higher order of equidistribution than any
+   other implemented generators. (It is proved that the period is 2^19937-1,
+   and 623-dimensional equidistribution property is assured.)
+   Fast generation. (Although it depends on the system, it is reported that
+   MT is sometimes faster than the standard ANSI-C library in a system
+   with pipeline and cache memory.)
+   Efficient use of the memory. (The implemented C-code mt19937.c
+   consumes only 624 words of working area.)
+
+   home page
+     http://www.math.keio.ac.jp/~matumoto/emt.html
+   original c source
+     http://www.math.keio.ac.jp/~nisimura/random/int/mt19937int.c
+
+   Coded by Takuji Nishimura, considering the suggestions by
+   Topher Cooper and Marc Rieffel in July-Aug. 1997.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later
+   version.
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+   See the GNU Library General Public License for more details.
+   You should have received a copy of the GNU Library General
+   Public License along with this library; if not, write to the
+   Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307  USA
+
+   Copyright (C) 1997, 1999 Makoto Matsumoto and Takuji Nishimura.
+   When you use this, send an email to: [email protected]
+   with an appropriate reference to your work.
 
 
-Procedure NewSeed;Forward;
+   REFERENCE
+   M. Matsumoto and T. Nishimura,
+   "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform
+   Pseudo-Random Number Generator",
+   ACM Transactions on Modeling and Computer Simulation,
+   Vol. 8, No. 1, January 1998, pp 3--30.
 
 
 
 
-Function Random : Extended;
+  Translated to OP and Delphi interface added by Roman Krejci (6.12.1999)
+
+  http://www.rksolution.cz/delphi/tips.htm
+
+  Revised 21.6.2000: Bug in the function RandInt_MT19937 fixed
+
+  2003/10/26: adapted to use the improved intialisation mentioned at
+  <http://www.math.keio.ac.jp/~matumoto/MT2002/emt19937ar.html> and
+  removed the assembler code
+
+ ----------------------------------------------------------------------}
+
+{$R-} {range checking off}
+{$Q-} {overflow checking off}
+
+{ Period parameter }
+Const
+  MT19937N=624;
+
+Type
+  tMT19937StateArray = array [0..MT19937N-1] of longint; // the array for the state vector
+
+{ Period parameters }
+const
+  MT19937M=397;
+  MT19937MATRIX_A  =$9908b0df;  // constant vector a
+  MT19937UPPER_MASK=$80000000;  // most significant w-r bits
+  MT19937LOWER_MASK=$7fffffff;  // least significant r bits
+
+{ Tempering parameters }
+  TEMPERING_MASK_B=$9d2c5680;
+  TEMPERING_MASK_C=$efc60000;
+
+
+VAR
+  mt : tMT19937StateArray;
+  mti: longint=MT19937N+1; // mti=MT19937N+1 means mt[] is not initialized
+
+{ Initializing the array with a seed }
+procedure sgenrand_MT19937(seed: longint);
+var
+  i: longint;
 begin
 begin
-    if (InitialSeed) OR (RandSeed <> OldRandSeed) then
-    Begin
-    { This is a pretty complicated affair                             }
-    {  Initially we must call NewSeed when RandSeed is initalized     }
-    {  We must also call NewSeed each time RandSeed is reinitialized  }
-    {  DO NOT CHANGE THE ORDER OF DECLARATIONS IN THIS BLOCK          }
-    {  UNLESS YOU WANT RANDON TO CRASH OF COURSE (CEC)                }
-      InitialSeed:=FALSE;
-      OldRandSeed:=RandSeed;
-      NewSeed;
+  mt[0] := seed;
+  for i := 1 to MT19937N-1 do
+    begin
+      mt[i] := 1812433253 * (mt[i-1] xor (mt[i-1] shr 30)) + i;
+      { See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. }
+      { In the previous versions, MSBs of the seed affect   }
+      { only MSBs of the array mt[].                        }
+      { 2002/01/09 modified by Makoto Matsumoto             }
     end;
     end;
-    Inc(RandSeed);
-    RandSeed := (RandSeed * 706) mod 500009;
-    OldRandSeed:=RandSeed;
-    INC(Seed2);
-    Seed2 := (Seed2 * 774) MOD 600011;
-    INC(Seed3);
-    Seed3 := (Seed3 * 871) MOD 765241;
-    Random :=
-      frac(RandSeed/500009.0 +
-           Seed2/600011.0 +
-           Seed3/765241.0);
+  mti := MT19937N;
 end;
 end;
 
 
-Function internRandom(l : Cardinal) : Cardinal;
+
+function genrand_MT19937: longint;
+const
+  mag01 : array [0..1] of longint =(0, MT19937MATRIX_A);
+var
+  y: longint;
+  kk: longint;
 begin
 begin
-    if (InitialSeed) OR (RandSeed <> OldRandSeed) then
-      Begin
-      { This is a pretty complicated affair                             }
-      {  Initially we must call NewSeed when RandSeed is initalized     }
-      {  We must also call NewSeed each time RandSeed is reinitialized  }
-      {  DO NOT CHANGE THE ORDER OF DECLARATIONS IN THIS BLOCK          }
-      {  UNLESS YOU WANT RANDOM TO CRASH OF COURSE (CEC)                }
-        InitialSeed:=FALSE;
-        OldRandSeed:=RandSeed;
-        NewSeed;
-      end;
-    Inc(RandSeed);
-    RandSeed := (RandSeed * 998) mod 1000003;
-    OldRandSeed:=RandSeed;
-    if l<>0 then
-      begin
-        internRandom := RandSeed mod l;
-      end
-    else internRandom:=0;
+  if (mti >= MT19937N) or
+     (randseed <> oldrandseed) { generate MT19937N longints at one time }
+  then begin
+     if mti = (MT19937N+1) then  // if sgenrand_MT19937() has not been called,
+       begin
+         sgenrand_MT19937(randseed);   // default initial seed is used
+         oldrandseed := randseed;
+       end;
+     for kk:=0 to MT19937N-MT19937M-1 do begin
+        y := (mt[kk] and MT19937UPPER_MASK) or (mt[kk+1] and MT19937LOWER_MASK);
+        mt[kk] := mt[kk+MT19937M] xor (y shr 1) xor mag01[y and $00000001];
+     end;
+     for kk:= MT19937N-MT19937M to MT19937N-2 do begin
+       y := (mt[kk] and MT19937UPPER_MASK) or (mt[kk+1] and MT19937LOWER_MASK);
+       mt[kk] := mt[kk+(MT19937M-MT19937N)] xor (y shr 1) xor mag01[y and $00000001];
+     end;
+     y := (mt[MT19937N-1] and MT19937UPPER_MASK) or (mt[0] and MT19937LOWER_MASK);
+     mt[MT19937N-1] := mt[MT19937M-1] xor (y shr 1) xor mag01[y and $00000001];
+     mti := 0;
+  end;
+  y := mt[mti]; inc(mti);
+  y := y xor (y shr 11);
+  y := y xor (y shl 7)  and TEMPERING_MASK_B;
+  y := y xor (y shl 15) and TEMPERING_MASK_C;
+  y := y xor (y shr 18);
+  Result := y;
 end;
 end;
 
 
+
 function random(l:cardinal): cardinal;
 function random(l:cardinal): cardinal;
 begin
 begin
-  random := trunc(random()*l);
+  random := cardinal((int64(cardinal(genrand_MT19937))*l) shr 32);
 end;
 end;
 
 
 function random(l:longint): longint;
 function random(l:longint): longint;
 begin
 begin
-  random := trunc(random()*l);
+  random := longint((int64(cardinal(genrand_MT19937))*l) shr 32);
 end;
 end;
 
 
-Procedure NewSeed;
+function random: extended;
 begin
 begin
-    randseed := randseed mod 1000003;
-    Seed2 := (internRandom(65000) * internRandom(65000)) mod 600011;
-    Seed3 := (internRandom(65000) * internRandom(65000)) mod 765241;
+  random := cardinal(genrand_MT19937) * (1.0/(int64(1) shl 32));
 end;
 end;
 
 
-
 {****************************************************************************
 {****************************************************************************
                             Memory Management
                             Memory Management
 ****************************************************************************}
 ****************************************************************************}
@@ -768,7 +845,11 @@ end;
 
 
 {
 {
   $Log$
   $Log$
-  Revision 1.43  2003-09-14 11:34:13  peter
+  Revision 1.44  2003-10-26 18:46:02  jonas
+    * replaced random generator with the Mersenne twister, which is about
+      3.5 times faster
+
+  Revision 1.43  2003/09/14 11:34:13  peter
     * moved int64 asm code to int64p.inc
     * moved int64 asm code to int64p.inc
     * save ebx,esi
     * save ebx,esi