Last active
March 30, 2018 19:06
-
-
Save tusing/3de5682785a34fbda67785a4a3256050 to your computer and use it in GitHub Desktop.
Mental-math random number
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ 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