Author Topic: Mersenne Twister - Random() replacement  (Read 1540 times)

0 Members and 1 Guest are viewing this topic.

Offline tk

  • Soldier
  • **
  • Posts: 235
Mersenne Twister - Random() replacement
« on: April 16, 2010, 10:59:58 am »
Script Name: Mersenne Twister Random Number Generator
Script Description: Fast pseudorandom number generator, replacement for built-in random() function
Author: tk1
Compile Test: Passed
Core Version: 2.6.4
Hosted by: Soldat Central - http://soldatcentral.com/

Full Description:
Since quality of pseudorandom numbers generated by built in Random() is very low I decided to replace it with another generator in my scripts. I found similar code here and made a compatibile version for the scriptcore.

function RandInt(min, max: integer): Integer;
function RandFlt(min, max: Single): Single;

Code: [Select]
// Mersenne Twister Random Number Generator

const // don't touch
  MT_N = 624;
  MT_M = 397;
  MATRIX_A   = $9908b0df;
  UPPER_MASK = $80000000;
  LOWER_MASK = $7fffffff;
  TEMPERING_MASK_B = $9d2c5680;
  TEMPERING_MASK_C = $efc60000;

var
  mt: array[0..MT_N - 1] of Int64;
  mti: LongInt;

procedure MTInit(seed: LongWord);
begin
  mt[0] := seed and $ffffffff;
  mti := 1;
  while(mti<MT_N)do
  begin
    mt[mti] := ((69069 * mt[mti-1]) and $ffffffff);
    mti := mti + 1;
  end;
end;

function GenRandIntMT: LongWord;
var
  mag01: array[0..1] of LongWord;
  y: LongWord;
  kk: LongInt;
begin
  mag01[0]:=$0;
  mag01[1]:=MATRIX_A;
  if (mti >= MT_N) then begin
    if (mti = (MT_N+1))then
      MTInit(4357);
    kk := 0;
    while(kk<(MT_N-MT_M))do begin
      y := (mt[kk]and UPPER_MASK)or(mt[kk+1]and LOWER_MASK);
      mt[kk] := mt[kk+MT_M] xor (y shr 1) xor mag01[y and $1];
      kk := kk + 1;
    end;
    while(kk<(MT_N-1))do begin
      y := (mt[kk]and UPPER_MASK)or(mt[kk+1]and LOWER_MASK);
      mt[kk] := mt[kk+(MT_M-MT_N)] xor (y shr 1) xor mag01[y and $1];
      kk := kk + 1;
    end;
    y := (mt[MT_N-1]and UPPER_MASK)or(mt[0]and LOWER_MASK);
    mt[MT_N-1] := mt[MT_M-1] xor (y shr 1) xor mag01[y and $1];
    mti := 0;
  end;
  y := mt[mti];
  mti := mti + 1;
  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;

function RandInt(min, max: integer): Integer;
begin
Result := max - min + 1;
max := GenRandIntMT;
if max < 0 then max := -max;
Result := min + max mod Result;
end;

function RandFlt(min, max: Single): Single;
begin
Result := min + (max - min) * GenRandIntMT * 2.3283064370807974e-10;
end;

// generator must be initialized with a nonzero value on start
procedure ActivateServer();
var
i: integer;
begin
repeat
i := Random(-$FFFFFF, $FFFFFF);
until (i <> 0);
MTInit(i);
end;



(Size 1023 B)
- http://soldatcentral.com/index.php?page=script&f=185 -


** Script hosted by Soldat Central! Please visit the author's script page and Rate this script **
« Last Edit: April 20, 2010, 07:32:00 am by tk »

Offline Hacktank

  • Camper
  • ***
  • Posts: 462
  • Soldat Scripter
    • HTZRPG
Re: Mersenne Twister - Random() replacement
« Reply #1 on: April 16, 2010, 11:08:39 am »
I too have noticed sometimes random() can return the same number many times in a row if called in fast succession. This looks like a good solution.


Offline DorkeyDear

  • Veteran
  • *****
  • Posts: 1507
  • I also go by Curt or menturi
Re: Mersenne Twister - Random() replacement
« Reply #2 on: April 16, 2010, 11:12:38 am »
procedure MTInit(seed: LongWord); - if we put the same seed in again, we would get the same random numbers?
Code: [Select]
MTInit(2); A := GenRandIntMT();
MTInit(2); B := GenRandIntMT();
if (A = B) then WriteLn('EqUaL! equal');

I suggest function RandFlt(min, max: Single): Double; or even possible extended (or all three in different functions :P but thats probably unnecessary)

Good job.
« Last Edit: April 16, 2010, 11:14:53 am by DorkeyDear »

Offline tk

  • Soldier
  • **
  • Posts: 235
Re: Mersenne Twister - Random() replacement
« Reply #3 on: April 16, 2010, 11:20:47 am »
Quote
procedure MTInit(seed: LongWord); - if we put the same seed in again, we would get the same random numbers?
Yes it generates series of numbers based on a seed, so if the seeds are the same, both series will be the same. However that is not a problem since we initialize the generator with a random value only once in ActivateServer. You also don't have to initialize the generator more often because the period is 219937-1 long.

Quote
I suggest function RandFlt(min, max: Single): Double; or even possible extended (or all three in different functions  but thats probably unnecessary)
I doubt someone would need double or extended quality of the float number, but modifying the result of the RandFlt function is not a problem if someone needs to.
« Last Edit: April 16, 2010, 11:25:27 am by tk »

Offline Swompie

  • Camper
  • ***
  • Posts: 390
Re: Mersenne Twister - Random() replacement
« Reply #4 on: April 16, 2010, 11:21:03 am »
Did I allign you to release this since I asked you how you did that?

Good job :] How long did you need for this? Looks uber-complex for me.

Offline tk

  • Soldier
  • **
  • Posts: 235
Re: Mersenne Twister - Random() replacement
« Reply #5 on: April 16, 2010, 11:27:40 am »
Yes kind of.
And I didn't write the entire generator myself, I just rewrote some code to be compatibile with scriptcore and added RandInt and RandFlt functions to that.

Offline dnmr

  • Camper
  • ***
  • Posts: 315
  • emotionally handicapped
Re: Mersenne Twister - Random() replacement
« Reply #6 on: April 16, 2010, 12:21:35 pm »
sick script. Five stars =)