whats wrong with this while loop in Python? - python

Ok so I have spent hours trying to resolve this and I feel its some simple error but I cannot find a way to resolve this.
the section I am having issues with is the second half of the code. There seems to be an infinite loop somewhere among the 2 nested while loops. If anyone is able to help, this would be great, thanks in advance.
import sympy as sym
import random
A, B, C, D, E, F, G, H, I, J = sym.symbols('A, B, C, D, E, F, G, H, I, J')
picks_a_person = [A, B, C, D, E, F, G, H, I, J] #List of people picking a name from a hat
person_gets_picked = [A, B, C, D, E, F, G, H, I, J] # List of names drawn from a hat
def re_draws(p):
n = 0
count = 0
while n < 1000: #Repeats the test 1000 times for an accurate percentage
n += 1
random.shuffle(person_gets_picked) #Chooses a random order of the list of names drawn
for i in range(p):
if person_gets_picked[i] == picks_a_person[i]: #Checks for all 'p' elements of the lists are different
count = count + 1
print("count = " + str(count)) #Returns the number of times a re-draw was not required
import numpy as np
from collections import Counter
total = []
while len(total) < 1000:
order = []
picks_a_person = [A, B, C, D, E, F, G, H, I, J]
person_gets_picked = [A, B, C, D, E, F, G, H, I, J]
while len(order) < 10:
a = person_gets_picked[random.randint(0, (len(person_gets_picked)-1))]
if a != picks_a_person[0]:
order.append(a)
person_gets_picked.remove(a)
del picks_a_person[0]
total.append(order)
Counter(np.array(total)[:,1])

While there are a lot of odd things about your code, this is where it gets into an infinite loop:
picks_a_person = [A, B, C, D, E, F, G, H, I, J]
person_gets_picked = [A, B, C, D, E, F, G, H, I, J]
while len(order) < 10:
a = person_gets_picked[random.randint(0, (len(person_gets_picked)-1))]
if a != picks_a_person[0]:
order.append(a)
person_gets_picked.remove(a)
del picks_a_person[0]
total.append(order)
Let's do some rubber duck debugging - what happens when your random.randint(0, (len(person_gets_picked)-1)) returns a number larger than 0 nine times in a row (worst case scenario)? All person_gets_picked elements except A get removed and added to the order list (which is still under 10 elements to break away from the while loop).
At that point we have a state as picks_a_person = [A] and person_gets_picked = [A]. random.randint(0, (len(person_gets_picked)-1)) will, thus, always return 0, a will always be set to A and since picks_a_person[0] == A the condition if a != picks_a_person[0] will never be evaluated as True, hence the order will never get its 10th element and therefore you got yourself an infinite loop.
It doesn't even have to be nine positive numbers in a row for this to occur - all it needs to happen is for A to remain as one of the last two picks and for random to land on the other option.
So why don't you write your whole loop as:
persons = [A, B, C, D, E, F, G, H, I, J]
persons_num = len(persons)
total = [random.sample(persons, persons_num) for _ in range(1000)]
And you're done.

Related

How to reduce multiple sets of possible character mappings?

I am running a slide attack on a cipher to determine the cipher's key. I want to know if there is an existing mathematical/programmatic-based representation I could use within this attack. Below is a list of mapping relationships compiled by comparing various outputs and their respective inputs. It reads as follows, where '->' is used to represent a mapping from the lhs to the rhs:
If b->d... then z->i AND q->f.
Likewise, if z->i AND q->f... then b->d.
Alphabet: 'abcdefghijklmnopqrstuvwxyz_'
Example key: 'uwmsqbhkc_pgvtilnyfexjzarod' (i.e. a shuffled alphabet)
c1 c2 c3 c4
===============
a: t, g | o, t
b: z, q | x, q
c: w, t | c, _
d: i, h | i, f
e: w, y | t, e
f: u, e | e, d
g: w, _ | y, m
h: n, m | z, d
i: i, j | h, f
j: l, h | r, g
k: w, b | t, v
l: r, c | e, b
m: l, u | z, e
n: r, i | x, y
o: g, s | e, a
p: t, f | z, w
q: u, r | z, r
r: s, a | p, o
s: t, _ | c, k
t: n, t | y, c
u: a, o | e, h
v: y, l | g, x
w: x, z | o, n
x: z, n | u, i
y: k, z | f, u
z: t, x | a, o
_: o, s | g, k
Additionally, the character frequencies of c1 and c2 MUST match the character frequencies of c3 and c4, respectively. In pseudo code, len(c1[freq1]) == len(c3[freq1]) and len(c2[freq2]) == len(c4[freq2]). Based on this, I have generated the following frequency "table" (a dictionary[freq: set(chars)] per column) where you can visually confirm this behavior:
c1: {4: {w, t}, 2: {u, i, r, l, z, n}, 1: {a, s, k, x, o, g, y}}
c3: {4: {e, z}, 2: {c, x, t, o, g, y}, 1: {u, i, r, a, f, p, h}}
c2: {2: {_, s, z, t, h}, 1: {n, m, q, b, u, i, r, a, c, f, l, x, e, j, o, g, y}}
c4: {2: {d, f, k, e, o}, 1: {y, n, m, q, b, u, i, a, _, r, c, x, w, v, g, t, h}}
This frequency table is what gives me a start on cracking the cipher. In the case of sets with a single character, we automatically learn the associated character mapping. Otherwise, we need to cross reference to learn mappings. The two methods I used to accomplish this are set intersections and set differences. For example, note that both c1[4] and c2[2] contain the character t. If we take the intersection of their potential mappings (i.e. c3[4] and c4[2]), we are left with the set {e} and thus t->e. Finding mappings via set differences is similar, so I won't give an example. Mappings found here can often be used to derive additional mappings from the first table.
In terms of this program, I am trying to find the best way to reduce the set of possibilities using physically-derived pieces of information via the methods above. Is there an existing mathematical/programmatic-based representation of this process? What's the proper name of this process? Additionally, are there more methods for extracting information that I'm not seeing?
Thanks!
-yuno

Intersecting two sets, retaining all (up to) three parts efficiently

If you have two sets a and b and intersect them, there are three interesting parts (which may be empty): h(ead) elements of a not in b, i(ntersection) elements in both a and b, and t(ail) elements of b not in a.
For example: {1, 2, 3} & {2, 3, 4} -> h:{1}, i:{2, 3}, t:{4} (not actual Python code, clearly)
One very clean way to code that in Python:
h, i, t = a - b, a & b, b - a
I figure that this can be slightly more efficient though:
h, t = a - (i := a & b), b - i
Since it first computes the intersection and then subtracts only that from a and then b, which would help if i is small and a and b are large - although I suppose it depends on the implementation of the subtraction whether it's truly faster. It's not likely to be worse, as far as I can tell.
I was unable to find such an operator or function, but since I can imagine efficient implementations that would perform the three-way split of a and b into h, i, and t in fewer iterations, am I missing something like this, which may already exist?
from magical_set_stuff import hit
h, i, t = hit(a, b)
It's not in Python, and I haven't seen such a thing in a 3rd-party library either.
Here's a perhaps unexpected approach that's largely insensitive to which sets are bigger than others, and to how much overlap among inputs there may be. I dreamed it up when facing a related problem: suppose you had 3 input sets, and wanted to derive the 7 interesting sets of overlaps (in A only, B only, C only, both A and B, both A and C, both B and C, or in all 3). This version strips that down to the 2-input case. In general, assign a unique power of 2 to each input, and use those as bit flags:
def hit(a, b):
x2flags = defaultdict(int)
for x in a:
x2flags[x] = 1
for x in b:
x2flags[x] |= 2
result = [None, set(), set(), set()]
for x, flag in x2flags.items():
result[flag].add(x)
return result[1], result[3], result[2]
I won't accept my own answer unless nobody manages to beat my own solution or any of the good and concise Python ones.
But for anyone interested in some numbers:
from random import randint
from timeit import timeit
def grismar(a: set, b: set):
h, i, t = set(), set(), b.copy()
for x in a:
if x in t:
i.add(x)
t.remove(x)
else:
h.add(x)
return h, i, t
def good(a: set, b: set):
return a - b, a & b, b - a
def better(a: set, b: set):
h, t = a - (i := a & b), b - i
return h, i, t
def ok(a: set, b: set):
return a - (a & b), a & b, b - (a & b)
from collections import defaultdict
def tim(a, b):
x2flags = defaultdict(int)
for x in a:
x2flags[x] = 1
for x in b:
x2flags[x] |= 2
result = [None, set(), set(), set()]
for x, flag in x2flags.items():
result[flag].add(x)
return result[1], result[3], result[2]
def pychopath(a, b):
h, t = set(), b.copy()
h_add = h.add
t_remove = t.remove
i = {x for x in a
if x in t and not t_remove(x) or h_add(x)}
return h, i, t
def enke(a, b):
t = b - (i := a - (h := a - b))
return h, i, t
xs = set(randint(0, 10000) for _ in range(10000))
ys = set(randint(0, 10000) for _ in range(10000))
# validation
g = (f(xs, ys) for f in (grismar, good, better, ok, tim, enke))
l = set(tuple(tuple(sorted(s)) for s in t) for t in g)
assert len(l) == 1, 'functions are equivalent'
# warmup, not competing
timeit(lambda: grismar(xs, ys), number=500)
# competition
print('a - b, a & b, b - a ', timeit(lambda: good(xs, ys), number=10000))
print('a - (i := a & b), b - i ', timeit(lambda: better(xs, ys), number=10000))
print('a - (a & b), a & b, b - (a & b) ', timeit(lambda: ok(xs, ys), number=10000))
print('tim ', timeit(lambda: tim(xs, ys), number=10000))
print('grismar ', timeit(lambda: grismar(xs, ys), number=10000))
print('pychopath ', timeit(lambda: pychopath(xs, ys), number=10000))
print('b - (i := a - (h := a - b)) ', timeit(lambda: enke(xs, ys), number=10000))
Results:
a - b, a & b, b - a 5.6963334
a - (i := a & b), b - i 5.3934624
a - (a & b), a & b, b - (a & b) 9.7732018
tim 16.3080373
grismar 7.709292500000004
pychopath 6.76331460000074
b - (i := a - (h := a - b)) 5.197220600000001
So far, the optimisation proposed by #enke in the comments appears to win out:
t = b - (i := a - (h := a - b))
return h, i, t
Edit: added #Pychopath's results, which is indeed substantially faster than my own, although #enke's result is still the one to beat (and likely won't be with just Python). If #enke posts their own answer, I'd happily accept it as the answer.
Optimized version of yours, seems to be about 20% faster than yours in your benchmark:
def hit(a, b):
h, t = set(), b.copy()
h_add = h.add
t_remove = t.remove
i = {x for x in a
if x in t and not t_remove(x) or h_add(x)}
return h, i, t
And you might want to do this at the start, especially if the two sets can have significantly different sizes:
if len(a) > len(b):
return hit(b, a)[::-1]

How to fix tic-tac-toe two player bug

I am trying to make a tic-tac-toe game however when I try implement two players, the printed out result (current game state) is not as expected.
for example when I try to put the letter 'x' in box 1 it will add random letters/numbers in different spots. I have already tried this with only one player and it wrks perfectly.
import random
a = ' '
b = ' '
c = ' '
d = ' '
e = ' '
f = ' '
g = ' '
h = ' '
i = ' '
def gamestateboard(a, b, c, d, e, f, g, h, i):
x = [a, b, c]
y = [d, e, f]
z = [g, h, i]
print(x)
print(y)
print(z)
gamestateboard(a, b, c, d, e, f, g, h, i)
def checkwin():
if a == 0 and b == 0 and c == 0:
print('Winner')
if a == 0 and d == 0 and g == 0:
print('Winner')
if g == 0 and h == 0 and i == 0:
print('Winner')
if c == 0 and e == 0 and g == 0:
print('Winner')
if a == 0 and e == 0 and i == 0:
print('Winner')
if c == 0 and f == 0 and i == 0:
print('Winner')
for i in range(1000000):
inputs = str(input('Enter a position: '))
if i%2 == 0:
if inputs == '1':
a = 0
gamestateboard(a, b, c, d, e, f, g, h, i)
checkwin()
if inputs == '2':
b = 0
gamestateboard(a, b, c, d, e, f, g, h, i)
checkwin()
if inputs == '3':
c = 0
gamestateboard(a, b, c, d, e, f, g, h, i)
checkwin()
if inputs == '4':
d = 0
gamestateboard(a, b, c, d, e, f, g, h, i)
checkwin()
if inputs == '5':
e = 0
gamestateboard(a, b, c, d, e, f, g, h, i)
checkwin()
if inputs == '6':
f = 0
gamestateboard(a, b, c, d, e, f, g, h, i)
checkwin()
if inputs == '7':
g = 0
gamestateboard(a, b, c, d, e, f, g, h, i)
checkwin()
if inputs == '8':
h = 0
gamestateboard(a, b, c, d, e, f, g, h, i)
checkwin()
if inputs == '9':
i = 0
gamestateboard(a, b, c, d, e, f, g, h, i)
checkwin()
else:
if inputs == '1':
a = 'X'
gamestateboard(a, b, c, d, e, f, g, h, i)
checkwin()
if inputs == '2':
b = 'X'
gamestateboard(a, b, c, d, e, f, g, h, i)
checkwin()
if inputs == '3':
c = 'X'
gamestateboard(a, b, c, d, e, f, g, h, i)
checkwin()
if inputs == '4':
d = 'X'
gamestateboard(a, b, c, d, e, f, g, h, i)
checkwin()
if inputs == '5':
e = 'X'
gamestateboard(a, b, c, d, e, f, g, h, i)
checkwin()
if inputs == '6':
f = 'X'
gamestateboard(a, b, c, d, e, f, g, h, i)
checkwin()
if inputs == '7':
g = 'X'
gamestateboard(a, b, c, d, e, f, g, h, i)
checkwin()
if inputs == '8':
h = 'X'
gamestateboard(a, b, c, d, e, f, g, h, i)
checkwin()
if inputs == '9':
i = 'X'
gamestateboard(a, b, c, d, e, f, g, h, i)
checkwin()
You are using i as your loop variable but also the name of a variable you use to draw in your game world.
I recommend moving away from having these variables and using indexes.
gameworld =[ [' ',' ',' '] for i in range(3)]
def draw_world():
for i in range(3):
print(gameworld[i])
then you can ask for coordinates or divide and use the modulus to use a single value like you did before. 4 // 3 = 1 4 % 3 = 1 position 4 is at [1][1]
This means you don't need your giant if input == list.
edit: Oh yeah you probably also want to do a loop with something like
no_winner = True
while no_winner:
...
you can set no_winner to False if check_win returns true and then you game doens't go on forever
edit 2:
Almost forgot you have an indenting error with the else for i%2==0 though I'm guessing that may be just from when you copied and pasted into your question. You also never check to see if someone else has already taken a square so when your code does actually work a player can overwrite another player's move.

Equation in MATLAB and Python given different result

Currently I'm translating my MATLAB code to Python version,
function u = afterslip(A, B, C, D, E, X, Ta, Tb, Tc)
u = A*log(1+X/Ta) + B + C*log(1+X/Tb) - D*exp(-X/Tc) + E;
end
to:
from math import log,exp
def afterslip(A, B, C, D, E, Ta, Tb, Tc):
e = A*log(1+X/Ta) + B + C*log(1+X/Tb) - D*exp(-X/Tc) +
return e
But when I calculated using X=187, Ta=8, Tb=80, Tc=600, A=-27, B=-18, C=56, D=-71, and E=-18 both given different result. Anyone knows why this happened and how to fix it?
The second function misses argument X. See below.
def afterslip(A, B, C, D, E, X, Ta, Tb, Tc):
return A*log(1+X/Ta) + B + C*log(1+X/Tb) - D*exp(-X/Tc) + E

performing more complex calculations on groupby objects

I am currently looking to do some calculations on a large dataset of options where I want first to split the data according to the strike price and expiry, then perform a set of calculations shown below onto each subgroup. I have been able to separate the data using groupby to get the split I want, I also wrote the calculation i want to do which works when tested on a subgroup. The only problem I have is to combine the two together.
Here is the code I used to group my data:
grouped =df.groupby(['Expiry','Strike'])
I had a read online and they mentioned the use of the apply function but the examples only included simple functions such as summation or averages.
Here is the calculation that I would like to perform on each subgroup data, where x,y,z,u,R are columns that in each subset that is the same for all subgroups:
def p(d, S, B, c):
return d * S + B - c
def b_t(r, b_old, S, d, d_old, t):
return np.exp(r * t) * b_old + S * (d_old - d)
def e_t(d_old, S, c, r, t, b_old):
return d_old * S - c + np.exp(r * t) * b_old
P_results = []
B_results = []
E_results = []
for i,(d,S,c,t,r) in enumerate(zip(x,y,z,u,R)):
B = b_t(r, b_old, S, d, d_old, t)
P = p(d, S, B, c)
E = e_t(d_old, S, c, r, t, b_old)
print('i={},P={},B={},E={}'.format(i,P,B,E))
B_results.append(B)
P_results.append(P)
E_results.append(E)
b_old = B
d_old = d
I thought maybe if I could save each subset as a new variable dataframe then maybe it could work but I haven't been able to do that.
I hope this is clear and I think posting some data would help but I am not sure how best to upload it here.
Much appreciate your help!
UPDATE 1: Found a solution that works
grouped =df.groupby(['Expiry','Strike'])
lg = list(grouped)
P_results = []
l_results =[]
B_results = []
E_results = []
for l in range(len(lg)):
df2=lg[l][1]
d_old = df2.iloc[0, 4]
S_old = df2.iloc[0, 8]
c_old = df2.iloc[0, 10]
b_old = c_old - d_old * S_old
x = df2.iloc[1:, 4]
y = df2.iloc[1:, 8]
z = df2.iloc[1:, 10]
u = df2.iloc[1:, 9]
R = df2.iloc[1:, 7]
for i, (d, S, c, t, r) in enumerate(zip(x, y, z, u, R)):
B = b_t(r, b_old, S, d, d_old, t)
P = p(d, S, B, c)
E = e_t(d_old, S, c, r, t, b_old)
print('i={},P={},B={},E={}'.format(i, P, B, E))
l_results.append(l)
B_results.append(B)
P_results.append(P)
E_results.append(E)
b_old = B
d_old = d
BB = pd.DataFrame(np.column_stack([l_results, P_results,
E_results,B_results]),columns=['l','P','E','B'])
All I did was to transform grouped into a callable list and then call each of the sections out using a for loop then use another for loop to perform the calculations. It is not the prettiest output, I put l_results there to show which group the calculations were referring to but seems to be sufficient for now. If there is any better way please let me know!

Categories