Snippets Project Page
Author: maroon
Added: 5y
Updated: Never
mIRC: 6.35 & up
Hits: 1,407
Downloads: 13
Review: entropy
Size: 10.11KB
1 0
Login to vote.
randfix
v1.0
substitute for $rand
eliminates modulo and minimizes swiss cheese bias bugs in $rand through v7.54
allows negative numbers in the range
allows much larger range of random numbers
includes alias to calculate standard deviation for series of numbers
Download
JSON
▲ Review
▼ Source
/* { $randfix( N1,N2 | char1,char2 ) by maroon v1.0 Can be pasted into any existing script If using the optional :START: and :CONNECT: initialization, these can safely be inserted inside existing event handlers same syntax as $rand except: 1. is 'more random' (at least through v7.54) 2. increases range from 46.5bits (14 9's) to 53bits 3. permits N1 or both N1/N2 being negative 4. valid range is N1 >= -2^53, N2 and range size as high as 2^53-1 5. smooths out modulo and granularity bias in $rand Incompatibilities: 1. Doesn't support using numeric portion of non-numeric parameters. $rand(1x,99x) returns random number from 1-99. $randfix(1x,99x) returns random codepoint from $asc(1) through $asc(9). 2. Negative numbers are now valid numeric parameters. $rand(-5,5) returns null, while $randfix returns random number from that range. $rand(-1,-10) always returns a hyphen while $randfix returns random number from that range. This is a work-around to address the $rand bugs reported here: https://forums.mirc.com/ubbthreads.php/topics/264732/$rand_bias#Post264732 The $rand identifier produces an uneven distribution at largest ranges, and at least as low as 2^24 range has swiss-cheese holes in the frequency distribution, having output numbers which are either too-rare, too-common, or impossible. These holes become larger as the range size increases. This alias offers 3 methods of creating a 53bit value used as input to the rand function. To avoid the swiss-cheese granularity issue, Methods #1 and #2 simulate a 53bit number by joining several $rand() outputs together. Assuming there are not impossible combinations of consecutive outputs from small $rand ranges, assembling them into a 53bit number should make all 53-bit numbers possible from $randfix(0,$calc(2^53-1)). Method#2 performs an XOR of each 53bit number against the prior 53bit number, ensuring that all 53bit numbers are possible. Method#3 collects entropy with each use, then stores it in a sha512 hash, avoiding the impossibity of consecutive 53bit values. To avoid adding a modulo bias, $randfix discards a portion of the 53-bit number range which would be extra inputs not available to all outputs, continuing to fetch 53-bit numbers until obtaining a qualifying number. There is a commented scriptline offering an optional $3 parameter which lets you force the 1st 53bit number to be a specific value. You can use this parameter to test that $randfix is correctly identifying the range of 53bit numbers which should be discarded. (By whether the output is fixed or random) The default $get_53bit_rand identifier is the fastest of several choices offered. You can edit this alias to use a different type of $get_53bit_rand alias, or make extra copies of this alias each using different 'get' aliases for different tasks. Random character from printable ASCII range: //echo -a $randfix(!,~) Random RGB color: //echo -a $rgb($randfix(0,$calc(2^24-1))) } */ on *:START:.timer -om 10 $randfix(999,9999) noop Initializing $ $+ randfix(1,9) on *:CONNECT:.timer -om 10 $randfix(999,9999) noop Initializing $ $+ randfix(1,9) alias randfix { if (($1 == $null) || ($2 == $null)) goto syntax if (($1 !isnum) || ($2 !isnum)) var %lo $asc($1) , %hi $asc($2) , %num 0 else var %lo $int($1) , %hi $int($2) , %num 1 if (%lo > %hi) var %lo $v2 , %hi $v1 var %diff %hi - %lo , %out_range %diff + 1 , %max.val 9007199254740991 if ((%hi > %max.val) || (%diff > %max.val) || (%lo < -9007199254740992)) goto syntax var %throwaway_above $calc(%max.val - (((%max.val % %out_range) + 1) % %out_range) ) var %int53 $get_53bit_rand ;if ($3 isnum) var -s %int53 $int($3) | else var %int53 $get_53bit_rand while (%int53 > %throwaway_above) { var %int53 $get_53bit_rand } if (%num) return $calc(%lo + %int53 % %out_range) return $chr($calc(%lo + %int53 % %out_range)) :syntax echo -sc info2 *$randfix( N1,N2 |char1,char2) by maroon $+ $chr(169) MinN1:-2^53 MaxN2/range.max:+2^53-1 halt } ; These 3 are alternate methods of generating 53-bit random numbers. ; #1 assumes the modulo and frequency distribution bias are negligible at 12bit range or less ; #2 calculates 53-bit random value same way as #1, but returns the XOR of the current ; random value against those from the prior random call. ; #3 uses sha512 string taking entropy from $rand and several other identifiers ; it also uses the prior random call's sha512 digest, adding entropy with each call alias get_53bit_rand { return $calc($r(0,255) + $r(0,2047) * 256 + $r(0,2047) * 524288 + $r(0,2047) * 1073741824 + $r(0,4095) * 2199023255552) } ; 53bit value from joining bit sizes 12:11:11:11:8 ; Note that all the above random inputs affect the calculation when the range size ; is NOT a factor of 256, i.e. ranges 0-1, 1-8, 0-127 alias get_53bit_rand-2 { var %a $xor(0 $+ $hget(randfix,a),$calc($r(0,255) + $r(0,2047) * 256 + $r(0,2047) * 524288)) var %b $xor(0 $+ $hget(randfix,b),$calc($r(0,2047) + $r(0,4095) * 2048)) hadd -m1 randfix a %a | hadd randfix b %b return $calc(%a + %b * 1073741824) } ; XOR's 53bit value against prior 53bit value. Must use separate XOR's due to ; $xor's 32-bit limitation. a=30bits 11:11:8 b=23bits 12:11 alias get_53bit_rand-3 { var %nonce 0 $+ $hget(randfix,nonce) + 1 var %a $sha512(%nonce $r(0,99999999999999) $ticks $eventid $active $ctime $ial(*,$r(1,$ial(*,0))) $hget(randfix,sha512)) hadd -m1 randfix nonce %nonce | hadd randfix sha512 %a var %offset $calc(1+$base($right(%a,1),16,10) * 7) var %r2 4503599627370496 | if ($mid(%a,127,1) isnum 0-7) var %r2 0 var %r1 $base($mid(%a,%offset,13),16,10) + %r2 return %r1 } ; XOR's 53bit value against prior 53bit value. Must use separate XOR's due to ; $xor's 32-bit limitation. a=30bits 11:11:8 b=23bits 12:11 ; final nibble of digest used to decide which 12 hex digits are used as 52 bits ; the next-to-last nibble is the 13th bit ; An optional way to gather entropy for Method#3's sha512 value is to hash all the text ; logfiles for all open channels on the current network: ; alias randfixentropy var %i $chan(0) , %a $hget(randfix,sha512) | while (%i) { if ($chan(%i).logfile) var %a %a $sha512($v1,2) | dec %i } | hadd -m1 randfix sha512 $sha512($ticks %a) ; randbiasdemo v1.1 by maroon. Demonstrates the modulo bias in $rand through v7.54 ; Due to the task requiring several hours, this does not test for the granularity issue where ; many values in ranges as small as 24bits are either rare or impossible. ; At default .996, the 1st pocket has double the count of the others. ; at $1 == .66 the last half of all pockets have half the count of the 1st half of pockets. ; same bias exists in v6.35 except the valid range of random numbers is smaller ; for v6.35 9,14 must be changed to 9,12 and // floor divide changed to / ; You can edit the alias to change $rand to $randfix, which can also be edited to use any of ; the 3 versions of get_53bit_rand*. Beware the scripted $randfix replacement aliases is ; slower than $rand, but this is not a hardship for many scripts which use $rand rarely. alias randbiasdemo { var %pct 0.996 | if (($1 > 0) && ($1 <= 1)) var %pct $1 var -s %pockets 256 , %array $str(0 $chr(32),%pockets) , %i 25600 , %max $str(9,14) * %pct , %div %max / %pockets , %ticks $ticks while (%i) { var %t $calc(1+ ($randfix(0,%max) // %div)) , %a $gettok(%array,%t,32) + 1 , %array $puttok(%array,%a,%t,32) dec %i } echo -a time: $calc($ticks - %ticks) $+ ms echo -a %array = $calc($replace(%array,$chr(32),+)) echo -a $stddev(%array) } ; $stddev 1.0 by maroon ; takes space-delimited list of numbers ; The .prop is a format string delimited by periods. token beginning with 'label' ; is followed by literal text. numeric tokens round the next value's fraction. ; Inaccuracies can occur due to sums exceeding 2^53 ; ; Valid keywords: sum mean stddev stderr ; sum = sum of all numbers ; mean = arithmetic mean. sum of values / count of values ; stddev = standard deviation ; stderr = 'standard error of the mean' ; examples ; as calculated by https://www.calculator.net/standard-deviation-calculator.html?numberinputs=1%2C+1%2C+2%2C+3%2C+5%2C+8%2C+13&x=76&y=12 ; //echo -a $stddev(1 1 2 3 5 8 13) ; //echo -a $stddev(1 1 2 3 5 8 13).labelcount:.count.labelsum:.sum.labelPopulationVar:.3.popvar.labelSampleVar:.samplevar.labelPopStd-Dev:.popstddev.labelSampleStd-Dev:.samplestddev.labelStdError:.2.stderr alias stddev { tokenize 32 $1- | if ($0 < 2) return | var %sum 0 , %sum.of.squares 0 var %j $0 | while (%j) { inc %sum $gettok($1-,%j,32) | dec %j } var %mean %sum / $0 var %j $0 | while (%j) { inc %sum.of.squares $calc( ($gettok($1-,%j,32) - %mean)^2 ) dec %j } var %pop.variance $calc(%sum.of.squares / ($0 )) var %sample.variance $calc(%sum.of.squares / ($0 -1)) var %pop.std.dev $calc((%sum.of.squares / ($0 ))^.5) var %sample.std.dev $calc((%sum.of.squares / ($0 -1))^.5) var %std.err $calc(%sample.std.dev / ($0 ^ .5) ) if ($prop == $null) return count: $0 sum: %sum mean: %mean popvar: %pop.variance pop.stddev: %pop.std.dev sample.var: %sample.variance sample.std.dev: %sample.std.dev std.err: %std.err var %vals $0 %sum %mean %pop.variance %sample.variance %pop.std.dev %sample.std.dev %std.err, %i 0 , %t $numtok($prop,46) , %show , %round 99 while (%i < %t) { inc %i | var %a $gettok($prop,%i,46) | if (%a isnum) var %round %a elseif (label* iswm %a) var %show %show $mid(%a,6) elseif ($findtok(count sum mean popvar samplevar popstddev samplestddev stderr,%a,1,32)) { var %show %show $round($gettok(%vals,$v1,32),%round) | var %round 99 } } return %show }
Changelog:
0
0