SL Psuedo Random is horrible!
|
|
Chri5 Somme
:)
Join date: 18 Nov 2005
Posts: 204
|
05-13-2006 14:56
Can someone please answer the follow...
Why is it that for ridiculous things like llRound(llFrand(10000)) i can get numbers like 12 three times in a row or 5 four times in a row, silly little things like that. How can random be random if you get numbers like that with such great odds, Second Life Psuedo Random is probably the worst randomizer i've ever seen. I was making a dice game, I used llRound(llFrand(6-1) + 1) and somehow i rolled 3 and 4 seven times in a row, this always happens.
Worst random i've ever seen, ever.
|
|
Chri5 Somme
:)
Join date: 18 Nov 2005
Posts: 204
|
05-13-2006 15:11
Here's something else,
dice1 = llRound(llFrand(6 - 1) + 1); dice2 = llRound(llFrand(6 - 1) + 1); dicetotal = dice1 + dice2;
After eiight turns in a row, i managed to roll a total of 7 eight times in a row.
I think SL plays favorite numbers, it picks the most common losing numbers in the casino world and when it see's a chance for it, it'll randomize those numbers. It wouldn't be hard either, if they wrote a method in the SL source that randomized losing numbers in casino games when it saw a chance, it would. Thats my conspiracy theory and it's pretty believable too because after all, who would trust a linden lab employee? not me, not a lot of ppl.
Market = Garbage
Linden = selling linden's soon, ruining the market because of their greed.
Employee's = liars
|
|
Francis Chung
This sentence no verb.
Join date: 22 Sep 2003
Posts: 918
|
05-13-2006 15:15
Just as a note - you're not using llFrand() correctly.
llFrand(x) returns a value on the interval [0..x)
If you want an integer between [0..n] you should use llFloor(llFrand(n+1)).
I remember thinking something was funny about llFrand(), so I histogrammed the results, as well as the difference between consecutive results, and the distrubution was more or less correct.
For what it's worth, I always use llFloor( llFrand(1) * (n+1) ), but it's probably just a superstition of mine.
I remember Xylor was saying something was funny about the list shuffle/randomization, and he ended up calling the built in list shuffle command 7 times to get good randomization.
_____________________
-- ~If you lived here, you would be home by now~
|
|
Chri5 Somme
:)
Join date: 18 Nov 2005
Posts: 204
|
05-13-2006 17:30
From: Francis Chung Just as a note - you're not using llFrand() correctly.
llFrand(x) returns a value on the interval [0..x)
If you want an integer between [0..n] you should use llFloor(llFrand(n+1)).
I remember thinking something was funny about llFrand(), so I histogrammed the results, as well as the difference between consecutive results, and the distrubution was more or less correct.
For what it's worth, I always use llFloor( llFrand(1) * (n+1) ), but it's probably just a superstition of mine.
I remember Xylor was saying something was funny about the list shuffle/randomization, and he ended up calling the built in list shuffle command 7 times to get good randomization. same results, SL really needs to revamp their random. It is utterly horrible! It makes me want to cry when I roll a seven 15 times in a row!
|
|
Ardith Mifflin
Mecha Fiend
Join date: 5 Jun 2004
Posts: 1,416
|
05-13-2006 21:27
From: Chri5 Somme same results, SL really needs to revamp their random. It is utterly horrible! It makes me want to cry when I roll a seven 15 times in a row! Then create your own. It's not that difficult to do. There are plenty of functions which you could call to obtain a seed if you're unhappy with llFrand. llGetTime has more than enough randomness for basic RNG.
|
|
Ardith Mifflin
Mecha Fiend
Join date: 5 Jun 2004
Posts: 1,416
|
05-13-2006 21:58
I threw together the really crude PRNG below. Give it a whirl. If you're still not happy, try implementing an algorithmic PRNG. //Generate a pseudorandom variable using llGetTime() as the seed. Other functions which generate large, unique numbers might be used. //The function rand takes one input, n. The function will generate a pseudorandom number between 1 and n, inclusive. //Other methods include the linear congruence method, or the Mersenne Twister if you're doing something along the lines of cryptography.
integer rand(integer n) { list seed_list = llParseString2List((string)llGetTime(), ["."], [" "]); integer seed = (integer)(llList2String(seed_list, 0) + llList2String(seed_list, 1)); integer result = (seed % n) + 1; return result; }
default { touch_start(integer total_number) { llSay(0, (string)rand(20)); } }
|
|
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
|
05-14-2006 00:37
From: Chri5 Somme same results, SL really needs to revamp their random. It is utterly horrible! It makes me want to cry when I roll a seven 15 times in a row! You do know that 7 has the highest probability when adding the result of two real die? While improbible, of any of the serries that can come up, 7 is the most likely.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river. - Cyril Connolly
Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence. - James Nachtwey
|
|
Draco18s Majestic
Registered User
Join date: 19 Sep 2005
Posts: 2,744
|
05-14-2006 01:52
From: Strife Onizuka You do know that 7 has the highest probability when adding the result of two real die? While improbible, of any of the serries that can come up, 7 is the most likely. Exactly what I was thinking through this whole thread. A 7 on two d6 is a 1 in 6 probability. A 5 or 8 are both 1 in 9....try rolling some real dice sometime (the average roll on 2d6 is 7!).
|
|
Introvert Petunia
over 2 billion posts
Join date: 11 Sep 2004
Posts: 2,065
|
05-14-2006 06:24
Back around version 1.3 I was curious as to how good llFrand was, so I grabbed 50,000 returns and ran them through the venerable DIEHARD analysis suite. Although my sample size was not large enough to demonstrate cyptographic level randomness, it did show more than ample randomness for a card game. Given that the SL servers run on Linux boxen which have the /dev/random entropy pool available, I suspect that llFrand was implemented using that because it was convenient and good. I haven't tested since that version but I see no reason for them to have changed. Given that there are only 36 ways for two dice to fall and one sixth of them yield a sum of 7 along with the random clustering effect a run of sevens every once in a while should not be surprising. And what Francis said.
|
|
Francis Chung
This sentence no verb.
Join date: 22 Sep 2003
Posts: 918
|
05-14-2006 10:13
Okay, so I got a bit curious about this. While 7 is the most likely, the chances of throwing it fifteen times in a row is really small. I'm roughing out the numbers a bit, but I had a go at calculating the probability of that happening. If you throw a pair of dice a hundred times, the chances that you'll throw anything fifteen (or more) times in a row is approximately 1 in 5 billion. Even if I'm off by an order of magnitude or two, I hope we can agree that the chances of this happening are exceedingly unlikely. I dug out my old histogram code, see if I could demonstrate that LSL's random number generator works (or doesn't). Results are pretty much as I remember when I ran it last time. Sorry that this code isn't particularly readable - I wrote it just to play with values, not to have anyone else read it. // Francis wuz here
integer BUCKETS = 10; integer TRIES = 100000;
list distribution ; list deltaDistribution;
string asciiScale( integer _numEntries ) { integer valuePerTick = TRIES / BUCKETS / 54 + 1; integer num = _numEntries; string s; while ( num > valuePerTick ) { num -= valuePerTick; s += "="; }
s += ">";
return s; }
default { state_entry() { float last; float cur; integer i; // Initialize list for ( i=0; i<BUCKETS; i++ ) distribution += [0]; for ( i=0; i<2*BUCKETS; i++ ) deltaDistribution += [0];
last = llFrand(1);
// Run distribution llOwnerSay( "Populating with random numbers.." ); llResetTime(); for ( i=0; i<TRIES; i++ ) { integer bucket; integer deltaBucket;
cur = llFrand(1);
bucket = llFloor( cur * BUCKETS ); distribution = llListReplaceList( distribution, [llList2Integer(distribution,bucket)+1], bucket, bucket );
deltaBucket = llFloor( (1.0+cur-last) * BUCKETS ); deltaDistribution = llListReplaceList( deltaDistribution, [llList2Integer(deltaDistribution,deltaBucket)+1], deltaBucket, deltaBucket );
last = cur; if ( i%(TRIES/10) == 0 ) { llOwnerSay( (string)(i/(TRIES/10)*10) + "% complete in " + (string)llGetTime() + " seconds." ); } // last = llFrand(1); } llOwnerSay( "Completed in " + (string)llGetTime() + " seconds." );
// Print histogram results llOwnerSay( "Histogram Results:" ); for ( i=0; i<BUCKETS; i++ ) { float low = ((float)i) / BUCKETS; float high = ((float)(i+1)) / BUCKETS; string s;
s = asciiScale( llList2Integer(distribution,i) ); llOwnerSay( "[" + (string)low + ", " + (string)high + "]: " + s + (string)llList2Integer(distribution,i) ); }
// Print delta histogram results llOwnerSay( "Histogram Delta Results:" ); for ( i=0; i<2*BUCKETS; i++ ) { float low = ((float)i) / BUCKETS - 1.0; float high = ((float)(i+1)) / BUCKETS - 1.0; string s;
s = asciiScale( llList2Integer(deltaDistribution,i) ); llOwnerSay( "[" + (string)low + ", " + (string)high + "): " + s + (string)llList2Integer(deltaDistribution,i) ); }
} }
To summarize: I call llFrand(1) 100000 times, and graph the results. The first histogram is simply mapping the distribution on 0.1 intervals. As we expect, the distribution is pretty even. For the second graph, I map the difference between two consecutive calls to llFrand(1), and then histogramming the results on 0.1 intervals. As you can see, the results are pretty much we expect, the distribution drops linearly, centered about zero. I would test with more than 100000 calls, but the runtime of this script is already around 40 minutes in a very fast sim. (Didn't someone benchmark LSL as having the equivalent compute power of a TRS-80?) This is about 10^5 times slower than my laptop. From this, I have to conclude that llFrand() does indeed do what it claims. However, I've only tested llFrand() with a value of 1.0. This is the reason for my superstition - if I wanted to calculate llFrand(6), I would write it 6*llFrand(1). Histogram Results: [0.0, 0.1): ===================================================>9891 [0.1, 0.2): ===================================================>9998 [0.2, 0.3): ===================================================>9844 [0.3, 0.4): ====================================================>10048 [0.4, 0.5): ===================================================>9976 [0.5, 0.6): ===================================================>9998 [0.6, 0.7): ====================================================>10113 [0.7, 0.  : ====================================================>10029 [0.8, 0.9): ====================================================>10098 [0.9, 1.0): ====================================================>10005 Histogram Delta Results: [-1.0, -0.9): ==>497 [-0.9, -0.  : =======>1477 [-0.8, -0.7): =============>2503 [-0.7, -0.6): ==================>3428 [-0.6, -0.5): ========================>4514 [-0.5, -0.4): =============================>5504 [-0.4, -0.3): ==================================>6414 [-0.3, -0.2): ========================================>7455 [-0.2, -0.1): ==============================================>8635 [-0.1, 0.0): ===================================================>9488 [0.0, 0.1): ===================================================>9564 [0.1, 0.2): ==============================================>8589 [0.2, 0.3): ========================================>7597 [0.3, 0.4): ==================================>6431 [0.4, 0.5): =============================>5555 [0.5, 0.6): ========================>4479 [0.6, 0.7): ==================>3498 [0.7, 0.  : ============>2404 [0.8, 0.9): ========>1497 [0.9, 1.0): ==>471 PS. That's neat Introvert, the diehard tests - I never heard of that before.
_____________________
-- ~If you lived here, you would be home by now~
|
|
Lex Neva
wears dorky glasses
Join date: 27 Nov 2004
Posts: 1,361
|
05-14-2006 10:34
It seems to me that any random algorithm that doesn't occasionally cluster and seem "not-random" isn't truly random ;)
Anyway, looking at this:
<code> llRound(llFrand(6-1) + 1) </code>
That's pretty messed up. Here's the probability distribution of hitting each number, assuming a truly random llFrand():
1: 8.333% 2: 16.666% 3: 16.666% 4: 16.666% 5: 16.666% 6: 8.333%
(I think. I just woke up.)
So not only is a normal roll of two six-sided dice going to be biased toward a sum of 7, but you've already biased these dice. I'm not sure what that 6-1 part is supposed to do, but it's definitely not right. Try llFloor(llFrand(6)) + 1.
|
|
Cross Lament
Loose-brained Vixen
Join date: 20 Mar 2004
Posts: 1,115
|
05-14-2006 12:50
Don't you want to do llFloor( llFrand( 6 ) ) + 1 instead?
_____________________
- Making everyone's day just a little more surreal -
Teeple Linden: "OK, where did the tentacled thing go while I was playing with my face?"
|
|
Talarus Luan
Ancient Archaean Dragon
Join date: 18 Mar 2006
Posts: 4,831
|
05-14-2006 13:27
Aye, you want to discard/truncate the fractional part, not round it. Rounding skews the distribution to (10%,20%,20%,20%,20%,10%). I think this is a case of programmer error, not a problem with llFrand() itself. 
|
|
Chri5 Somme
:)
Join date: 18 Nov 2005
Posts: 204
|
05-14-2006 14:46
yea i was just using from example on the SL scripting wiki. Thank you for all of your input.
|
|
Introvert Petunia
over 2 billion posts
Join date: 11 Sep 2004
Posts: 2,065
|
the question that won't leave me alone
05-14-2006 20:27
This is math done by a math flunkie, feel free to ignore. So after reading Francis' post I was wondering how often one should see runs of the same dice rolls. Fortunately, my Bayesian calculus is still working so I calculate the probability of a run of 15 sevens in a row as as (1/6)^15 or 2e-12. Unfortunately, my recall of Poisson distributions is really bad so I had to fake it. You see, the simplistic Baysian probability presumes that you are starting from the beginning of a list of random numbers, but you aren't actually, you are starting somewhere in the middle of a (conceptually) infinite list *. That should alter the probability but I'm not sure exactly how. So I used brute force (not in LSL) to find an empirical answer. My definition of a run of length N is (possibly overlapping) consecutive rolls of a target sum of two dice. Thus, the sequence {... 5, 9, 7, 7, 7, 12, 7, 7, 7, 7, 10 ...} would count as three runs of 3 sevens (where the second group is counted as a hit at both the third and fourth 7). The results surprised me as being far more likely than I expected. runs of length 4 fives in 1 Mega-rolls: 20 runs of length 3 twelves in 1 Mega-rolls: 2 runs of length 7 sevens in 1 Mega-rolls: 2 runs of length 7 sevens in 1 Giga-rolls: 572 Perhaps I'll get a better handle on this after relearning Poisson distributions (and some sleep). *While coding my 12 line program to run this I found that my claim above was false. Linux does provide the entropy pool, but the glibc random number functions use a standard LCRNG with a period of repetition of 2^32. Again, not cryptographically strong but certainly good enough for table games.
|
|
Eloise Pasteur
Curious Individual
Join date: 14 Jul 2004
Posts: 1,952
|
05-15-2006 09:20
Just to add my twopennyworth, I set up a script that dealt 100,000 cards at random (script below). It counted how many of each card there were, how often there were repeats and the longest run of the same card being drawn twice. The output was: 1927, 1971, 1829, 1838, 1910, 1917, 1912, 1842, 1846, 1973, 1930, 1890, 1905, 1885, 1937, 1984, 1858, 1981, 1871, 2016, 1896, 1999, 1842, 1919, 1952, 1930, 1867, 1933, 1912, 1963, 1957, 1944, 1928, 1992, 1908, 1956, 1964, 1909, 1959, 1920, 1923, 1905, 1876, 1934, 1899, 1854, 1912, 1980, 1941, 1930, 2033, 1941 runs: 1879 longest run: 2 Expectation: 1923.076 each. Chi-squared suggests that this is no different to expected (p=0.2), the number of repeats is lower than expectation (also 1923.076 after all) and repeating the same draw twice (however often) must be expected. It takes about 5 hours in a reasonably normal sim to run through it all... and I've deleted my email address too. There are places it could be neater, bits got bodged on, but it should do the trick I think, there's no obvious areas it's biasing anything that I can see. list results = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; integer longestRun=0; integer currRun=0; integer runs=0; integer last=0; default { touch_start(integer total_number) { llSay(0, "Starting."); integer i; for(i=0; i<100000; ++i) { integer rand=(integer)llFloor(llFrand(52.0)+1.0); integer change=llList2Integer(results, rand); if(rand==last) { currRun++; if(currRun>longestRun) { longestRun=currRun; } runs++; } else { currRun=0; last=rand; } change++; results=llListReplaceList(results, [change], rand, rand); if(i%1000==0) { llOwnerSay((string)i); } } llSay(0, "Done"); llEmail(**************, "random numbers", llDumpList2String(results, ", ")+"\nruns: "+(string)runs+" longest run: "+(string)longestRun); for(i=0; i<52; ++i) { llOwnerSay((string)i+": "+llList2String(results, i)); } llOwnerSay("Runs: "+(string)runs); llOwnerSay("Longest: "+(string)longestRun); } }
|
|
Lex Neva
wears dorky glasses
Join date: 27 Nov 2004
Posts: 1,361
|
05-15-2006 10:12
From: Talarus Luan Aye, you want to discard/truncate the fractional part, not round it. Rounding skews the distribution to (10%,20%,20%,20%,20%,10%). I think this is a case of programmer error, not a problem with llFrand() itself.  Hah... that's what I tried to write first, too. Way to be sleepy, Lex. Anyway, the fact remains that the outer two are skewed.
|