Difference between revisions of "Pseudo Random Number Generator"

From Second Life Wiki
Jump to navigation Jump to search
Line 28: Line 28:
integer lslPRNGState2      = 0;
integer lslPRNGState2      = 0;
integer lslPRNGState3      = 0;
integer lslPRNGState3      = 0;
 
integer lslPRNGPosition    = 0;
integer lslPRNGPosition    = 0;
integer lslPRNGSeed        = 0;
integer lslPRNGSeed        = 0;
 
integer lslPRNGTwiddle(integer a, integer b) {
integer lslPRNGTwiddle(integer a, integer b) {
     integer c = (((a & 0x80000000) | (b & 0x7FFFFFFF)) >> 1);
     integer c = (((a & 0x80000000) | (b & 0x7FFFFFFF)) >> 1);
Line 37: Line 37:
     return c;
     return c;
}
}
 
lslPRNGGenState() {
lslPRNGGenState() {
     lslPRNGState0 = lslPRNGState2 ^  
     lslPRNGState0 = lslPRNGState2 ^  
Line 49: Line 49:
     lslPRNGPosition = 0;
     lslPRNGPosition = 0;
}
}
 
lslPRNGGenStateSeed(integer seed) {
lslPRNGGenStateSeed(integer seed) {
     lslPRNGState0 = seed;
     lslPRNGState0 = seed;
Line 60: Line 60:
     lslPRNGPosition = 4;
     lslPRNGPosition = 4;
}
}
 
integer lslPRNGRandomInteger(integer seed) {
integer lslPRNGRandomInteger(integer seed) {
     if (seed != lslPRNGSeed) {
     if (seed != lslPRNGSeed) {
         lslPRNGGenStateSeed(seed);
         lslPRNGGenStateSeed(seed);
         lslPRNGSeed = seed;
         lslPRNGSeed = seed;
       
         lslPRNGGenState();
         lslPRNGGenState();
     } else if (lslPRNGPosition >= 4) lslPRNGGenState();
     } else if (lslPRNGPosition >= 4) lslPRNGGenState();
   
     integer x;
     integer x;
     if (lslPRNGPosition == 0)      x = lslPRNGState0;
     if (lslPRNGPosition == 0)      x = lslPRNGState0;
Line 75: Line 75:
     else if (lslPRNGPosition == 3) x = lslPRNGState3;
     else if (lslPRNGPosition == 3) x = lslPRNGState3;
     ++lslPRNGPosition;
     ++lslPRNGPosition;
   
     x = x ^ ((x >> 11) & 0x001FFFFF);
     x = x ^ ((x >> 11) & 0x001FFFFF);
     x = x ^ ((x << 7) & 0x9D2C5680);
     x = x ^ ((x << 7) & 0x9D2C5680);
Line 81: Line 81:
     return x ^ ((x >> 18) & 0x00002FFF);
     return x ^ ((x >> 18) & 0x00002FFF);
}
}
 
// Returns a random float between 0.0 (inclusive) and 1.0 (exclusive)
// Returns a random float between 0.0 (inclusive) and 1.0 (exclusive)
float lslPRNGRandomFloat(integer seed) {
float lslPRNGRandomFloat(integer seed) {
Line 89: Line 89:
     if (b < 0) b = -b;
     if (b < 0) b = -b;
     else if (b == 0) return 0.0;
     else if (b == 0) return 0.0;
   
     t %= b;
     t %= b;
     return (float)t / (float)b;
     return (float)t / (float)b;
}
}  
 
// Returns a completely random key
key lslPRNGRandomKey(integer seed) {
    string k = lslIntegerToHex(lslPRNGRandomInteger(seed)) +
        lslIntegerToHex(lslPRNGRandomInteger(seed)) +
        lslIntegerToHex(lslPRNGRandomInteger(seed)) +
        lslIntegerToHex(lslPRNGRandomInteger(seed));
       
    return (key)llInsertString(
        llInsertString(
            llInsertString(
                llInsertString((k = "") + k, 8,"-"),
                13,
                "-"
            ),
            18,
            "-"
        ),
        23,
        "-"
    );
}
 
 
 
// Returns a completely random key
// Returns a completely random key
key lslPRNGRandomKey(integer seed) {
key lslPRNGRandomKey(integer seed) {
Line 124: Line 100:
         lslIntegerToHexFast(lslPRNGRandomInteger(seed)) +  
         lslIntegerToHexFast(lslPRNGRandomInteger(seed)) +  
         lslIntegerToHexFast(lslPRNGRandomInteger(seed));
         lslIntegerToHexFast(lslPRNGRandomInteger(seed));
       
     return (key)llInsertString(
     return (key)llInsertString(
         llInsertString(
         llInsertString(
Line 139: Line 115:
     );
     );
}
}
 
string lslByteHexChars = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
string lslByteHexChars = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
string lslIntegerToHexFast(integer i) {
string lslIntegerToHexFast(integer i) {
Line 151: Line 127:
     return s + llGetSubString(lslByteHexChars, t, ++t);
     return s + llGetSubString(lslByteHexChars, t, ++t);
}
}
 
default {
default {
     state_entry() {
     state_entry() {

Revision as of 05:09, 3 September 2008

Description

Generating random values can be useful in many applications, however all we are supplied with in LSL is a relatively inflexible floating-point number generator.

To this end I (Haravikk Mistral) set-out to create a more useful random number generator based on a Mersenne Twister Pseudo Random Number Generator (PRNG). The implementation in LSL uses a much smaller state of only four values, however it gives much the same distribution as the standard implementation which uses an array of some 624 values as its state, which of course is unfeasible to implement in LSL.

Usage

Usage is relatively simple, and this library comes with some helper methods so that you can quickly generate random LSL integer, float, and key values, providing a seed to help generate them even more randomly. If you use this PRNG to aid in communications between objects, then you must ensure they use the same seed value(s), thus an algorithm for determining these will be required, but could be as simple as a counter for each message sent/received.

The only methods you are ever likely to use are:

integer lslPRNGRandomInteger(integer seed) Generates a pseudo-random integer using the given seed.
float lslPRNGRandomFloat(integer seed) Generates a pseudo-random float with range [0.0, 1.0) using the given seed.
key lslPRNGRandomKey(integer seed) Generates a pseudo-random key using the given seed.
string lslIntegerToHex(integer i) Allows you to output an integer value as hex, useful for Cryptography.

Script

<lsl>integer lslPRNGState0 = 0; integer lslPRNGState1 = 0; integer lslPRNGState2 = 0; integer lslPRNGState3 = 0;

integer lslPRNGPosition = 0; integer lslPRNGSeed = 0;

integer lslPRNGTwiddle(integer a, integer b) {

   integer c = (((a & 0x80000000) | (b & 0x7FFFFFFF)) >> 1);
   if (b & 1) c = c ^ 0x9908B0DF;
   return c;

}

lslPRNGGenState() {

   lslPRNGState0 = lslPRNGState2 ^ 
       lslPRNGTwiddle(lslPRNGState0, lslPRNGState1);
   lslPRNGState1 = lslPRNGState1 ^ 
       lslPRNGTwiddle(lslPRNGState1, lslPRNGState2);
   lslPRNGState2 = lslPRNGState0 ^ 
       lslPRNGTwiddle(lslPRNGState2, lslPRNGState3);
   lslPRNGState3 = lslPRNGState3 ^ 
       lslPRNGTwiddle(lslPRNGState3, lslPRNGState0);
   lslPRNGPosition = 0;

}

lslPRNGGenStateSeed(integer seed) {

   lslPRNGState0 = seed;
   lslPRNGState1 = 1812433253 * 
       (lslPRNGState0 ^ (lslPRNGState0 >> 30)) + 1;
   lslPRNGState2 = 1812433253 * 
       (lslPRNGState1 ^ (lslPRNGState1 >> 30)) + 2;
   lslPRNGState3 = 1812433253 * 
       (lslPRNGState2 ^ (lslPRNGState2 >> 30)) + 3;
   lslPRNGPosition = 4;

}

integer lslPRNGRandomInteger(integer seed) {

   if (seed != lslPRNGSeed) {
       lslPRNGGenStateSeed(seed);
       lslPRNGSeed = seed;

       lslPRNGGenState();
   } else if (lslPRNGPosition >= 4) lslPRNGGenState();

   integer x;
   if (lslPRNGPosition == 0)      x = lslPRNGState0;
   else if (lslPRNGPosition == 1) x = lslPRNGState1;
   else if (lslPRNGPosition == 2) x = lslPRNGState2;
   else if (lslPRNGPosition == 3) x = lslPRNGState3;
   ++lslPRNGPosition;

   x = x ^ ((x >> 11) & 0x001FFFFF);
   x = x ^ ((x << 7) & 0x9D2C5680);
   x = x ^ ((x << 15) & 0xEFC60000);
   return x ^ ((x >> 18) & 0x00002FFF);

}

// Returns a random float between 0.0 (inclusive) and 1.0 (exclusive) float lslPRNGRandomFloat(integer seed) {

   integer t = lslPRNGRandomInteger(seed);
   if (t < 0) t = -t;
   integer b = lslPRNGRandomInteger(seed);
   if (b < 0) b = -b;
   else if (b == 0) return 0.0;

   t %= b;
   return (float)t / (float)b;

}

// Returns a completely random key key lslPRNGRandomKey(integer seed) {

   string k = lslIntegerToHexFast(lslPRNGRandomInteger(seed)) + 
       lslIntegerToHexFast(lslPRNGRandomInteger(seed)) + 
       lslIntegerToHexFast(lslPRNGRandomInteger(seed)) + 
       lslIntegerToHexFast(lslPRNGRandomInteger(seed));

   return (key)llInsertString(
       llInsertString(
           llInsertString(
               llInsertString((k = "") + k, 8,"-"),
               13,
               "-"
           ),
           18,
           "-"
       ),
       23,
       "-"
   );

}

string lslByteHexChars = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; string lslIntegerToHexFast(integer i) {

   integer t = ((i >> 24) & 0xFF) << 1;
   string s = llGetSubString(lslByteHexChars, t, ++t);
   t = ((i >> 16) & 0xFF) << 1;
   s += llGetSubString(lslByteHexChars, t, ++t);
   t = ((i >> 8) & 0xFF) << 1;
   s += llGetSubString(lslByteHexChars, t, ++t);
   t = (i & 0xFF) << 1;
   return s + llGetSubString(lslByteHexChars, t, ++t);

}

default {

   state_entry() {
       integer seed = llGetUnixTime();
       llOwnerSay("Seed = "+(string)seed);
       llOwnerSay("Random integer = "+(string)lslPRNGRandomInteger(seed));
       llOwnerSay("Random float = "+(string)lslPRNGRandomFloat(seed));
       llOwnerSay("Random key = "+(string)lslPRNGRandomKey(seed));
   }

}</lsl>