Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- analysis of "random" number generator
- -- ... Seed values?
- -- .... oh god no
- local ran = math.random()
- local r = math.random()
- local ra = math.random(1,15)
- function genNumber()
- local rand = nil
- -- Programming error: Could just use "return value" instead of setting a
- -- local variable
- if ra == 1 then
- -- rand(1, 9) * 10 + r, typed as a string
- -- Extremely badly distributed, as it only returns 9 distinct values.
- rand = math.random(1,9)..r
- elseif ra == 2 then
- -- Bell-curve distribution, due to addition of two evenly distributed
- -- variables.
- -- Returns one distinct value.
- rand = r+ran
- elseif ra == 3 then
- -- Bell-curve distribution, due to subtraction of two evenly distributed
- -- variables.
- -- Returns one distinct value.
- rand = r-ran
- elseif ra == 4 then
- -- I-have-no-idea distribution. Limited to range [0, 1]
- -- Still only returns one distinct value
- rand = r*ran
- elseif ra == 5 then
- -- Another I-have-no-idea distribution.
- -- Returns only one distinct number
- rand = r/ran
- elseif ra == 6 then
- -- Another I-have-no-idea distribution.
- -- Returns only one distinct number
- rand = math.pow(r,ran)
- elseif ra == 7 then
- -- ....??
- -- Returns only one distinct number
- rand = math.sqrt(r)
- elseif ra == 8 then
- -- Always 1
- rand = math.ceil(r)
- elseif ra == 9 then
- -- Always 0
- rand = math.floor(r)
- elseif ra == 10 then
- -- Returns only one distinct number still
- rand = r
- else
- -- Really...? Returns very high numbers. Still only one distinct value
- rand = ra/r
- end
- -- Conclusion: Total of 23 distinct return values... assuming that ra is
- -- randomly chosen... Since it's only chosen once per library load, as
- -- well as the other values, this function only ever returns one value.
- -- See: http://xkcd.com/221/
- return rand
- end
- function genBetween(min,max)
- -- Oh hey, this one is actually random every call! :D
- -- I can actually analyze this as a random function!
- local b = math.random(min,max)
- local be2 = math.random(min,max)
- local be3 = math.random(1,15)
- -- Programming error: be4 is not local, and will leak into the environment.
- be4 = nil
- if be3 == 1 then
- -- Horrible distribution. As it's generated via string concatation, the
- -- "lower half" of the distribution is confined in a way that is... hard
- -- to mathematically qualify.
- -- Probably translates to: be2*(10^ceil(ln(b)/ln(10))) + b
- be4 = be2..b
- elseif be3 == 2 then
- -- Bell curve distribution. Has a range of [min*2,max*2]
- be4 = b+be2
- elseif be3 == 3 then
- -- Bell curve distribution. Has a range of [0, max-min]
- be4 = b-be2
- elseif be3 == 4 then
- -- Screwed up distribution. Has a range of [min*min, max*max]
- be4 = b*be2
- elseif be3 == 5 then
- -- I'm not even going to try and analyse this. Why would you ever use an
- -- division operation on random numbers, as part of a generator?
- -- Also, this can return NaN, and other nastiness in the case of an range
- -- containing zero.
- be4 = b/be2
- elseif be3 == 6 then
- -- This is liable to produce very very huge values.
- -- Values like "inf" which are not very friendly to the code that comes
- -- after this.
- be4 = math.pow(b,be2)
- elseif be3 == 7 then
- -- This operation makes no sense, and doesn't even have a chance of adding
- -- any entropy to b.
- be4 = math.sqrt(b)
- elseif be3 == 8 then
- -- b is an integer value. This has no effect.
- be4 = math.ceil(b)
- elseif be3 == 9 then
- -- b is an integer value. This has no effect.
- be4 = math.floor(b)
- elseif be3 == 10 then
- -- ...??
- be4 = b
- else
- -- Identical to be3 == 5
- -- Again, has a weird distribution that I won't try to qualify.
- be4 = b/be2
- end
- -- Huge programming error:
- -- All of this is horrible.
- -- Replace it with: return ((math.floor(be4)-min)%(max-min))+min
- -- This solves this function returning floating point values (!!!), as well
- -- as it returning values outside the stated range. It does not solve the
- -- problem that you've utterly screwed up the random function.
- if be4 > max then
- -- The value can very well exceed max*2 at this point, and this won't help
- -- in the majority of cases.
- -- You want the modulo operation here.
- local be5 = be4-max
- -- Because the else clause returns just be4, this is broken. The obvious
- -- use of this function will get "max" back, which is clearly not what
- -- is intended. Instead, the "random" value is stuck in the second return
- -- slot.
- -- Effectively, you have to do this to get the true "random" value:
- -- local a, b = genBetween(min, max)
- -- if b then
- -- a = b
- -- end
- return max, be5
- else
- -- You arn't going to try to do anything with values under min!?!?
- return be4
- end
- end
- -- To illustrate what's wrong with this code, here's a graph of frequency of
- -- various return values, in the range [0, 255], with my modification so that
- -- the return values were even in the right range:
- -- http://www.chartgo.com/share.do?id=8c8b8a22aa
- -- Entropy analysis on a similar sample:
- --[[
- Entropy = 6.125075 bits per byte.
- Optimum compression would reduce the size
- of this 4084095 byte file by 23 percent.
- Chi square distribution for 4084095 samples is 76378531.33, and randomly
- would exceed this value 0.01 percent of the times.
- Arithmetic mean value of data bytes is 62.9531 (127.5 = random).
- Monte Carlo value for Pi is 3.802550971 (error 21.04 percent).
- Serial correlation coefficient is 0.001314 (totally uncorrelated = 0.0).
- ]]--
- -- Now let's compare with math.random!
- --[[
- Entropy = 7.999952 bits per byte.
- Optimum compression would reduce the size
- of this 4194304 byte file by 0 percent.
- Chi square distribution for 4194304 samples is 276.68, and randomly
- would exceed this value 25.00 percent of the times.
- Arithmetic mean value of data bytes is 127.4833 (127.5 = random).
- Monte Carlo value for Pi is 3.137709749 (error 0.12 percent).
- Serial correlation coefficient is 0.000921 (totally uncorrelated = 0.0).
- ]]--
- -- Well then... looks like this version is less random.
- -- I would have tried to analyse the original version too, but, that would be
- -- extremely difficult due to the extreme range of numbers returned... because
- -- genBetween generates a huge number of values outside the supposed "min" and
- -- "max" values. Whoops!!!
- -- The thing is, randomness doesn't work like this. For one, to be "useful",
- -- the numbers must be "evenly distributed". That is, that graph above? A truly
- -- random sequence would have a flat line, as there's no bias towards any
- -- particular value.
- -- To be useful, randomness must be predictable in some quality, that is, it
- -- returns a known set of values (either a floating point value between 0 and
- -- 1, or an integer value between two chosen points), while having an even
- -- distribution between those values, while still being unpredictable, as far
- -- as knowing /which value/ will be chosen goes.
- -- As far as combining values goes, this doesn't help randomness, because, you
- -- can't pull information/randomness out of thin air. It has to come from
- -- somewhere. In this case, Java uses an PRNG called an LCG to generate its
- -- random numbers. It's very not random, and has 32 bits of state. Pulling more
- -- values from it does not magically make a "more random" value.
- -- Further reading:
- -- https://en.wikipedia.org/wiki/Entropy_(information_theory)
- -- https://en.wikipedia.org/wiki/Information_theory
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement