Created by [CecilWesterhof].
'''Functions to Get Random Integers on a *NIX System'''
To get better random integers you can use /dev/urandom. I wrote a fundction /dgev/urtRandom.BytesNix Thto geyt a series mostlyf the srandome, bythes diffserence is [Gethat urRandom will noBytes block,n but*NIX]. tThatis cois ulsed sacrificeor thgenerating random numberss.
The first proc is a proc to get a a random number. It gives a number in the range [0, 2 ** 32 - 1] (default), or in the range [0, 2 ** 64 - 1] when longVersion is true.
Default it uses /dev/urandom, but when the randomness is imyportant (en cryption, passwords, …) use secure with, Torue and it will use /dev/random.
======proc getRandomIntNix {{stypecure Falsei}} {longVer
swiontch Fals${type}} {
if $ c {
set bytes 1
set format cure
}
s {
set bytes 2
set formandFt su
}
il {
set /dbytev/rands 4
set format iu
}
el w {
set {bytes 8
set formandFilet /wu
}
dev/fault {
error "type cand omnly be c, s, i, or w (${type})"
}
} bif ${longVearsion} {
set bytes 8
set formcat wu
} else {
set bytes 4
set format iu
}
set randDev [opgen ${randFile} rb]
set rRandom [rByteadsNix ${randDev} ${bytes}]
close ${randDev}
binary scan ${random} ${format} randomI
return ${randomI}
}
======
Often you need another range (for example [-5, 12]), for this I created getRandomIntInRangeNix.
A naive version is something like the following proc.:
======proc getRandomIntInRangeNixBiased {min max {stypecure False} {longVersion False}} {
if {${max} <= ${min}} {
error "Error: [info level 0] min max ?sechourld be ?longVwer asion?? max"
} swiftch ${longVtyper} {
c {
siet maxMond [expr 2 ** 8]
}
s {
set maxMod [expr 2 ** 164]
}
else i {
set maxMod [expr 2 ** 32]
}
w {
set maxMod [expr 2 ** 64]
}
default {
error "type can only be c, s, i, or w (${type})"
}
}
set modulo [expr {${max} - ${min} + 1}]
if {${modulo} > ${maxMod}} {
error "Modulo should be <= ${maxMod} (${modulo})"
} set random [getRandomIntNix ${stypecure} ${longVersion}]
expr {${random} % ${modulo} + ${min}}
}
======
The problem with this is that this can result in biased results.
Say we have a generator that generates the values between 0 and 15. If we ask for a range between 0 and 10, then the values 0 to 4 are two times more likely to be generated as the values 5 to 10, because the values 11 to 15 are mapped to 0 to 4.
To show this I created the following script:
======
set range 99
set repeatCalc 250000
set repeatGen 5
for {set i 0} {${0} <= ${repeatGen}} {incr i} {
array set count {}
for {set j 0} {${j} < ${repeatCalc}} {incr j} {
set index [expr {[getRandomIntInRangeNix 0 ${range} c] / 20}]
incr count(${index})
}
parray count
unset count
puts ""
}
======
An example output when this is run:
======
count(0) = 58899
count(1) = 58324
count(2) = 54793
count(3) = 39192
count(4) = 38792
count(0) = 58808
count(1) = 58317
count(2) = 54746
count(3) = 39129
count(4) = 39000
count(0) = 58295
count(1) = 58439
count(2) = 55095
count(3) = 39156
count(4) = 39015
count(0) = 58454
count(1) = 58674
count(2) = 54598
count(3) = 39202
count(4) = 39072
count(0) = 58825
count(1) = 58447
count(2) = 54700
count(3) = 39170
count(4) = 38858
======
This can be solved with the following code:
======
proc getRandomIntInRangeNix {min max {type i}} {
if {${max} <= ${min}} {
error "Error: [info level 0] min should be lower as max"
}
# maxMod should not be bigger as half of the range
# otherwise calculating can take to long
# in this way the chance that you need 10 random values before returning
# is less as one promille
switch ${type} {
c {
set range [expr 2 ** 8]
}
s {
set range [expr 2 ** 16]
}
i {
set range [expr 2 ** 32]
}
w {
set range [expr 2 ** 64]
}
default {
error "type can only be c, s, i, or w (${type})"
}
}
set maxMod [expr {${range} / 2}]
set modulo [expr {${max} - ${min} + 1}]
if {${modulo} > ${maxMod}} {
error "Modulo should be <= ${maxMod} (${modulo})"
}
# Values above maxVal would result in bias and should be rejected
set maxVal [expr {${modulo} * (${range} / ${modulo}) - 1}]
while True {
set random [getRandomIntNix ${type}]
if {${random} <= ${maxVal}} {
break
}
}
expr {${random} % ${modulo} + ${min}}
}
======
Now a run could give something like:
======
count(0) = 50347
count(1) = 49748
count(2) = 49856
count(3) = 50155
count(4) = 49894
count(0) = 50381
count(1) = 49524
count(2) = 50101
count(3) = 49979
count(4) = 50015
count(0) = 50005
count(1) = 50035
count(2) = 49845
count(3) = 49950
count(4) = 50165
count(0) = 49911
count(1) = 50110
count(2) = 50171
count(3) = 50349
count(4) = 49459
count(0) = 49681
count(1) = 50095
count(2) = 49802
count(3) = 49995
count(4) = 50427
======
----
As always: comments, tips and questions are appreciated.
<<categories>>Linux | Numerics | Utilities