Skip to content

Instantly share code, notes, and snippets.

@tusing
Last active March 30, 2018 19:06
Show Gist options
  • Save tusing/3de5682785a34fbda67785a4a3256050 to your computer and use it in GitHub Desktop.
Save tusing/3de5682785a34fbda67785a4a3256050 to your computer and use it in GitHub Desktop.
Mental-math random number
"""
by tusing
MOTIVATION:
Wanted a fast way to come up with reasonably pseudo-random numbers mentally.
Humans have a bias when guessing numbers: we tend to guess 7 and 17 most often:
http://scienceblogs.com/cognitivedaily/2007/02/05/is-17-the-most-random-number/
ALGORITHM:
1. Start with a seed (1). The seed serves to "balance" our output distribution,
whereas the rest of the algorithm allows for a relatively random output order.
2. Start with a mod space (must be odd). If you want an even mod space,
start with an odd one and take the first n-1 values.
3. Think of any normal English word or sentence. Lowercase letters only.
4. Add up all the letters in sentence (indexed by their order in the alphabet).
5. Increment the seed and add it to this sum.
6. Mod the sum by the space you want the random number to be in.
"""
from collections import Counter
from random import randint
import argparse
parser = argparse.ArgumentParser()
parser.add_argument(
"-m", "--mod", help="mod space of random numbers to be generated", type=int, default=11)
parser.add_argument(
"-ns", "--noseed", help="disable seed", action="store_true", default=False)
args = parser.parse_args()
ORD_OFFSET = 97
MOD_SPACE = args.mod
BAR_SIZE = 10
SEED = -1
if args.noseed:
print("WARNING: No seed being used!")
if args.mod % 2 == 0:
print("WARNING: Non-odd mod space being used!")
# "The Complete Works of William Shakespeare"
# https://ocw.mit.edu/ans7870/6/6.006/s08/lecturenotes/files/t8.shakespeare.txt
with open("shakespeare.txt", 'r') as file:
text = file.read().lower().split(' ')
def sum_word(word):
global SEED
total = 0
for char in word:
num = ord(char) - 96
if num >= 1 and num <= 26:
total += num
if total > 0:
SEED += 1
return total + SEED * (not args.noseed)
return 0
sums = [sum_word(word) for word in text if sum_word(word) > 0]
cnt_mod = Counter()
order = []
for num in sums:
order.append(num % MOD_SPACE)
cnt_mod[num % MOD_SPACE] += 1
for i in range(MOD_SPACE):
print(str(i).zfill(2),
('-' * int(1.0 * BAR_SIZE *
cnt_mod[i] / max(cnt_mod.values()))).ljust(BAR_SIZE),
'(' + str(cnt_mod[i]) + ')')
min_v, max_v = min(cnt_mod.values()), max(cnt_mod.values())
print(str(100 * (max_v - min_v) / max_v)
[:4] + "%", "difference between likelihoods with this algorithm")
# Compare against a computer pRNG
cnt_true = Counter()
true_rand = [randint(1, MOD_SPACE) for i in range(len(sums))]
for v in true_rand:
cnt_true[v % MOD_SPACE] += 1
min_v, max_v = min(cnt_true.values()), max(cnt_true.values())
print(str(100 * (max_v - min_v) / max_v)
[:4] + "%", "difference between likelihoods with a real (computer) pRNG")
$ python random_mental.py -m 5
00 --------- (179139)
01 --------- (178631)
02 --------- (178807)
03 ---------- (179345)
04 --------- (178995)
0.39% difference between likelihoods with this algorithm
0.24% difference between likelihoods with a real (computer) pRNG
$ python random_mental.py -m 11
00 --------- (80838)
01 --------- (81456)
02 --------- (81331)
03 --------- (80977)
04 --------- (81609)
05 --------- (81460)
06 --------- (81929)
07 --------- (80933)
08 --------- (80954)
09 --------- (81305)
10 ---------- (82125)
1.56% difference between likelihoods with this algorithm
0.84% difference between likelihoods with a real (computer) pRNG
$ python random_mental.py -m 21
00 --------- (42606)
01 ---------- (42966)
02 --------- (42731)
03 --------- (42390)
04 --------- (42676)
05 --------- (42924)
06 --------- (42167)
07 --------- (42549)
08 --------- (42477)
09 --------- (42776)
10 --------- (42291)
11 --------- (42686)
12 --------- (42395)
13 --------- (42465)
14 --------- (42833)
15 --------- (42640)
16 --------- (42545)
17 --------- (42763)
18 --------- (42738)
19 --------- (42693)
20 --------- (42606)
1.85% difference between likelihoods with this algorithm
1.61% difference between likelihoods with a real (computer) pRNG
Not using a seed and/or odd mod space, for funsies!
==============
$ python random_mental.py -m 10
WARNING: Non-odd mod space being used!
00 --------- (102566)
01 ------- (76111)
02 --------- (102838)
03 ------- (76295)
04 --------- (102490)
05 ------- (76573)
06 --------- (102520)
07 ------- (75969)
08 ---------- (103050)
09 ------- (76505)
26.2% difference between likelihoods with this algorithm
1.14% difference between likelihoods with a real (computer) pRNG
$ python random_mental.py -ns
WARNING: No seed being used!
00 ------- (75136)
01 --------- (90158)
02 -------- (84849)
03 ------ (69769)
04 ------ (61337)
05 --------- (93371)
06 -------- (83225)
07 -------- (80844)
08 ---------- (99743)
09 ------- (77542)
10 ------- (78943)
38.5% difference between likelihoods with this algorithm
1.05% difference between likelihoods with a real (computer) pRNG
$ python random_mental.py -m 10 -ns
WARNING: No seed being used!
WARNING: Non-odd mod space being used!
00 ---- (73205)
01 ------- (107809)
02 ---- (73080)
03 ------- (109286)
04 --- (59052)
05 ----- (75527)
06 ----- (83711)
07 ---- (71469)
08 ------ (92405)
09 ---------- (149373)
60.4% difference between likelihoods with this algorithm
1.36% difference between likelihoods with a real (computer) pRNG
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment