-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlcg.py
166 lines (150 loc) · 5.31 KB
/
lcg.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# Fettet, Louis
# Linear Congruential Generator
# 12/10/12
# "Anyone who considers arithmetical methods of producing random digits is, of
# of course, in a state of sin."
# - John von Neumann
# "Random numbers should not be generated with a method chosen at random."
# - Donald E. Knuth
# The following is my implementation of a linear congruential generator.
# My first implementation used the same numbers as VAX's MTH$RANDOM, and after
# testing these numbers I generated my own variables with some of the other
# functions below.
def lcg(start, end, last):
"""
Returns a pseudo-random number using the linear congruential recurrence
relation.
On the first iteration, it uses the seed pi, and on every following
iteration it can use the previously generated number as the next seed.
"""
if last is None:
x = 3.141592 # i think pi is a pretty cool irrational number
# ti makes circles and doesn't afraid of anything
else:
x = last
# This next part is really important. The values we choose for the
# variables makes the difference between creating the perfect generator
# or repeating the atrocities of the infamous... RANDU.
a = 3209867 # the "multiplier"; a-1 is divisible by all prime
# factors of m.
c = 12493 # the "increment"
m = 2**80 # the "modulus"; c and m are pairwise coprime.
# Finally, we plug in these variables into our LCG equation.
return start+((a*x+c)%m)%end
def noCycleTest():
"""
Tests to see if a number repeats within 65536 iterations.
Returns True if no cycle occurs; otherwise returns False.
"""
dic = {}
last = 3.141592 # We again use pi as our starting seed.
for i in range(65536):
last = lcg(0,1,last) # The previously generated number is used as
result = (last) # the next starting seed.
if result in dic:
return False
else:
dic[result] = 1
return True
def uniformityTest():
"""
Tests the uniformity of the generator on producing integers between 0 and 9.
Returns a dictionary with integers 0 through 9 along with the amount of
times each integer occurs.
"""
dic = {}
last = 3.141592
for i in range(65536):
last = lcg(0,10,last) # Generates a number between 0 and 10.
result = int(last) # Converts the number to an integer for testing.
if result in dic:
dic[result] += 1
else:
dic[result] = 1
return dic
def pokerTest():
"""
Tests the generator by having it produce 'random' card suits (hearts,
spades, clubs, and diamonds) over 65536 iterations.
It's a really horrible implementation.
Like really horrible.
I feel bad for anyone who has to read this.
"""
from collections import Counter
occur = []
last = 3.141592
for i in range(65536):
seq = {}
for e in range(5): # There are 5 cards in a hand.
last = lcg(0,4,last)
result = int(last)
if result == 0:
result = 'h' # hearts
if result == 1:
result = 'd' # diamonds
if result == 2:
result = 's' # spades
if result == 3:
result = 'c' # clubs
if result in seq:
seq[result] += 1
if result not in seq:
seq[result] = 1
occur.append(seq)
stringlist=[]
for i in occur: # This was definitely not the best way to
string = str(i) # implement this but I'm tired and stuff.
stringlist.append(string)
finaldic = {}
for i in stringlist:
if i in finaldic:
finaldic[i]+=1
else:
finaldic[i]=1
for sequence in finaldic:
print ('{0:35} {1:5}'.format(sequence,finaldic.get(sequence)))
# Below are some things I found, modified, and used in generating my initial
# variables for the LCG... theoretically you could use numbers generated from
# the LCG and plug them into these functions to return new parameters to put
# back into the generator. Not sure how this would affect the quality of the
# 'randomness', but I think it could definitely improve the security of the
# generator, making it more difficult to 'hack' if this were to be used in
# some kind of game or commercial software.
def factor(n):
if n == 1:
return [1]
i = 2
limit = n**0.5
while i <= limit:
if n % i == 0:
recur = factor(int(n/i))
recur.append(i)
return sorted(recur)
i += 1
return [n]
def factorPowers(n):
d = {}
for i in (factor(n)):
if i in d:
d[i] += 1
else:
d[i] = 1
return d
def isPrime(n):
if len(factor(n)) == 1:
return True
else: return False
def arePairwiseCoprime(s,t):
for i in factor(s):
if i in factor(t):
return False
return True
def GCD(s,t):
s = factor(s)
t = factor(t)
while len(s) > 0:
if max(s) in t:
return max(s)
else:
s.remove(max(s))
return 1