An RNG that runs in your brain
hillelwayne.com
hillelwayne.com
Fame is relative, for my part Marsaglia is most famous for The Ziggurat Method for Generating Random Variables and numerous other PRNG delights .. the testsuite comes way down the list.
* https://www.jstatsoft.org/article/view/v005i08
* https://en.wikipedia.org/wiki/Ziggurat_algorithm
Once upon a time I made a branchless generator of large blocks of random points on S2 (the surface of a unit sphere) for an astrophysics sim, it was a fun exercise
Edit: removed a bit because I confused the polar with the ratio method
Was it “generate a multivariate Gaussian distribution and then divide each point by its norm”?
For N=3, there is a method by Marsaglia: https://doi.org/10.1214/aoms/1177692644 For N=2, there is a method by von Neumann: https://mcnp.lanl.gov/pdf_files/nbs_vonneumann.pdf
All of these use rejection sampling, so they are not branchless.
It's a twofer, first there's a choice of algo to choose an unbiased unit vector (or point on sphere), then there's implementing it on a particular vector RISC architecture to avoid stutters from branch prediction gone awry - constant steady performance being desirable (in this and all the other parts of the larger program).
"Branchless" decisions are possible;
Compute D = decision = 0 or 1 as result (no branch, just arithmetic)
Compute R = result = DA + (!D)B = result A OR result B depending on D with no jumps or decision penalties.
It's an implementation hack and one dependant on architecture, some chips are fancy enough to pursue two branch in parallel and using the correct choice without have to suffer a time costly pipeline flush for path better not taken.
DSP architectures have long pipes and consistent timing, complex arithmetic statements (once setup) take a single clock cycle; interate result vector = modulo scalar vector1 * input vector1 + modulo scaler vector2 * input vector2 type FFT "atomics" are a single clock .. you can push inputs once per clock and extract ouputs once per clock (with a delay of time to fill pipe).
I have also used the diehard suite a while back to convince myself that a random mixer thing I came up with was suitably random. Basically I wrote a thing to read from various sources of entropy, bleach them a bit and then mix them all together in a way that was extremely chaotic before feeding them into the kernel entropy pool. This was because it used to be a problem (back circa 1998 or so) that if you had headless servers serving a lot of ssl sessions they would run out of entropy and block from time to time. At the time, diehard had been implemented in fortran and then compiled to C using a gnu fortran transpiler so it was completely impossible to understand what each of the tests were actually doing.
On the topic of tfa, one of the most practical “RNG in your brain” techniques I heard of was suggested by the poker player Dan Harrington in his books, which is for sampling at random with some probability between some discrete choices. So say you have something you want to do at random 1/3rd of the time and the other 2/3rds of the time you’re going to do another thing[1], the way he says to do this is base it on your watch face. So you decide [2] that if the second hand of your watch is between 1 and 20 then you will do thing A and if it’s between 20 and 60 you’ll do thing B. Then you look and you do whatever thing. The good part about this is clearly even if people know you’re doing it they can’t exploit it because they don’t know the set of choices you’ve given yourself, what the boundaries are where you choose between strategies etc. It doesn’t work if you need to sample rapidly but in the context he gave it (tournament poker) you would have a random amount of time more than a minute between decisions anyway so its fine in that context.
[1] From a game theory point of view, it can be shown that in games with randomness and hidden information like poker, the optimal strategy is a Nash equilibrium in mixed strategies which means you need to sample a probability distribution to choose your actions in this way.
[2] before looking. This is important.
A nice blog on this: https://stackedboxes.org/2017/05/01/acklams-normal-quantile-...
[0]https://en.wikipedia.org/wiki/Solitaire_%28cipher%29
[1]https://digitalambler.com/2020/05/08/how-to-construct-the-sh...
Related : how to debias a biased coin[2] (aka. a Von Neumann extractor).
It is not exactly the same (randomness extractor vs. PRNG), but there are similarities[3].
[2]https://carlos.bueno.org/2011/10/fair-coin.html
[3]https://en.wikipedia.org/wiki/Randomness_extractor
I want to take this opportunity to recommend Hillel's writing and depth of analysis (on top of the quirkiness of doing it in a weird lang). Compared to the equivalent answers on stackoverflow[4], there is just no comparison.
[4]https://stackoverflow.com/questions/3919597/is-there-a-pseud...
A question, though. The author writes:
> So what other numbers work? ... if n is a good multiplier, then the period of the orbit starting from 1 should be 10n − 2.
> (2 3 6 11 15 18 23 27 38 39 42 50 51 62 66 71)
> The last interesting number is 18. It has a respectable period of 178 and has every possible digit transition. The downside is that you have to learn the 18 times-table. This isn’t too bad: I internalized it with maybe 10 minutes of practice.
Why is 18 the "last" interesting number? The largest number on the list, 71, has a period of 708 (10n − 2). Given that he is willing to memorize the first 10 multiples of a 2-digit number, why didn't he choose 71 over 18, which has a period of only 178?
The 2-digit transitions for 71 are pretty evenly distributed:
7 7 7 7 7 7 7 7 7 7
7 8 7 7 7 7 7 7 7 7
7 7 8 7 7 7 7 7 7 7
7 7 7 8 7 7 7 7 7 7
7 7 7 7 8 7 7 7 7 7
7 7 7 7 7 8 7 7 7 7
7 7 7 7 7 7 8 7 7 7
7 7 7 7 7 7 7 8 7 7
7 7 7 7 7 7 7 7 8 7
7 7 7 7 7 7 7 7 7 7One thing this article didn't discuss is choosing a seed, I think humans are better at coming up with a single random number than with a sequence of random numbers, but I still like to incorporate some "randomness" from the environment.
You can e.g. add the count of something you don't know the count of until you count it to the seed. For example: floor tiles in a room, books with the color red in a book shelve, ... you can obviously also throw a coin or something like that, but that's a more involved action.
If you are really dedicated, then you could mix in some environmental randomness after ever n iterations.
Come to think of it, I rarely need to choose between more than 10 alternatives so it's realistic to get at least two somewhat random alternatives per minute out of this idea.
Thinking about it, I wonder if both people picking multiple numbers and adding them all up mod N would improve things.
I bet that our bias against clustering and repetition when creating a pseudo-random list of numbers would mean that a list would be less random than an individual pick. As a simple example, if picking two numbers, people would probably be more likely to pick one even and one odd number than two even or odd numbers, since a mix of even and odd feels more authentically random, resulting in an excess of odd sums.
As an alternative, what if each person created a short list and then blindly chose one number from the other person's list?
lets say you need to pick a number from 1 to 20, if you take 1235 and mod 22. mod is easy to do in your head as you can just subtract in steps until you get there. so 50 times 22 is 1100, we are left with 135. 5 * 22 is 110 so we are left with 25. 25 - 22 is 3.
I picked 3 and I would be hard pressed to guess it would end up 3.
>>> import collections, random, pprint
>>> pprint.pprint(
sorted(
list(
collections.Counter(
[
((random.randint(1,10000000) % 22) % 20)
for x in range(10000000)
]
).items()
)
)
)
[(0, 908920),
(1, 908264),
(2, 454167),
(3, 456019),
(4, 454551),
(5, 455183),
(6, 454127),
(7, 454308),
(8, 454939),
(9, 454602),
(10, 454963),
(11, 453117),
(12, 454046),
(13, 453812),
(14, 456243),
(15, 455025),
(16, 454101),
(17, 455072),
(18, 454409),
(19, 454132)]The other one I use sometimes is the powers of 2 mod 100 and use the leading digit. This gives a cycle length of 20 with a flat distribution.
> (1, 1, * + * ... Inf)[^42].pick mod 10
I can recite pi faster than a person can make up random digits. Try it some time and see how fast you start to totally run out of randomness.
> Choose a 2-digit number, say 23, your “seed”.
> Form a new 2-digit number: the 10’s digit plus 6 times the units digit.
Here's where the ambiguity is, I interpreted this as (tens_digit+6)*units_digit so I did (2+6)*3=24
> The example sequence is 23 –> 20 –> 02 –> 12 –> 13 –> 19 –> 55 –> 35 –> …
24 != 20 so I had to walk back and figure out that they in fact meant tens_digit+(6*units_digit)
I'm sure there are people who will get angry if they get two songs in a row from the same artist on a "random" playlist, but I'll much rather have that than the pseudo-random Spotify nonsense that will only play similar tracks next to each other or just ignore 90% of the playlist.
Given a sequence, and asked to grade how closely it fits sampling from uniform random, we typically get it very wrong.
For example we underestimate how often streaks or clustering happens in uniform data.
We are wired to find patterns. This gives us an advantage for some tasks, but for assessing the randomness of data, makes us very bad.
We (the class) wanted to go outside instead of having a regular lecture. He made a deal that if we win his game we go outside, otherwise we had to be extra focused for the rest of the period.
Everyone in the class had to pick a random number between 1 and 100 and write it down. He told us that he would get one guess for each pupil (around 30) and if he managed to guess all numbers he would win. If any number was left we go outside.
Knowing how bad humans (and especially 12 year olds) are at picking random numbers, and knowing which numbers were mostly picked, he guessed all numbers with still a handful of guesses left.
That from a teacher's perspective seems like a fascinating idea to try out.
E.g. teacher picking 1-30 and then each student has 0.3 odds of picking 1-30 or 31-100.
The issue is I think all it would take to beat the teacher is one unusual student.
Then it would be 1 to the power of 100.
I guess I should've clarified that the 0.3 refers to being able to choose 30 out of 100 numbers?
(Incidentally, even with N=99 the probability is 0.37 ≈ 1/e, and the probability is lowest at N=37 ≈ 100/e. This is not a coincidence.)
The teacher picks 30 numbers out of 100. Then each student (independently) picks one number. If random, that is 0.3 ^ 30. Obviously, the students are not picking random.
If I had to pick for the teacher:
multiples of 10: 10,20,30,40,50,60,70,80,90
double digits: 11,22,33,44,55,66,77,88,99
Not sure where to go next.
i would pick a 3-7, 30-37, 70-77, then some other fews from there like 1, 100, 50 etc
⸻
1. Spoiler: there was a divide by zero in his proof.
2. Why? Because it’s near the middle of the interval and is prime, two things that will happen if you unthinkingly just grab for a number.
> The number 47 makes frequent recurrences in dialogues and on computer screens in Star Trek.
> The origin of the significance of 47 can be traced to Star Trek: The Next Generation and Star Trek: Voyager writer Joe Menosky, who attended Pomona College in California. There is a club at Pomona called The 47 Society, which claims that there exists a mathematical proof that all numbers are equal to 47.
> Joe Menosky first started including references to 47 in his scripts in the fourth season of TNG, and the in-joke quickly caught on among the rest of the staff. Since then, references to 47 have been included in many episodes and movies of all the modern series.”