Python: Conditional probabilities of winning a tiebreaker game - recursion

This question was asked again (Excel: Conditional probabilities of winning a tiebreaker game), but the OP wanted an Excel-based answer and the one s/he got was not really accurate.
I am trying to calculate the conditional probability of winning a tiebreaker match as described in http://strategicgames.com.au/PhD.pdf, page 21-22.
It requires coding two recursive formulas:
Formulas
(I cannot attach picture due to low rep)
This is what i've done so far:
def prob_tiebraker_game_A(Pa, Pb, a, b):
if a == 7 and b >= 0 and b <=5:
return 1
elif b == 7 and a >= 0 and a <=5:
return 0
elif a == 6 and b == 6:
return (Pa*(1-Pb))/(Pa*(1-Pb) + (1-Pa)*Pb)
elif (a+b) % 2 ==0:
return Pa*prob_tiebraker_game_B(Pa, Pb, a+1, b) + (1-Pa)*prob_tiebraker_game_B(Pa, Pb, a,b+1)
elif (a+b) % 2 !=0:
return Pa*prob_tiebraker_game_A(Pa, Pb, a+1, b) + (1-Pa)*prob_tiebraker_game_A(Pa, Pb, a,b+1)
def prob_tiebraker_game_B(Pa, Pb, a, b):
if b == 7 and a >= 0 and a <=5:
return 1
elif a == 7 and b >= 0 and b <=5:
return 0
elif a == 6 and b == 6:
return (Pb*(1-Pa))/(Pb*(1-Pa) + (1-Pb)*Pa)
elif (a+b) % 2 ==0:
return Pb*prob_tiebraker_game_A(Pa, Pb, a+1, b) + (1-Pb) * prob_tiebraker_game_A(Pa, Pb, a,b+1)
elif (a+b) % 2 !=0:
return Pb*prob_tiebraker_game_B(Pa, Pb, a+1, b) + (1-Pb) * prob_tiebraker_game_B(Pa, Pb, a,b+1)
For values of Pa = 0.62, Pb = 0.6, i should be getting these results:
Results
But i am getting the wrong numbers for any values of (a,b) except (0,0) and (6,6), (7,.) and (.,7) since these scores depend on the first 3 conditions in the functions which are trivial to evaluate.
I tried modifying the other conditions but with no success. Any help appreciated would be appreciated

You need only one recursion. Note (a+b) mod 2 identifies which player is serving right now. So probability of that player winning/not winning should be used accordingly.Here's the solution when player A is serving.
def prob_tiebraker_game(Pa, Pb, a, b):
if a == 7 and b >= 0 and b <=5:
return 1
elif b == 7 and a >= 0 and a <=5:
return 0
elif a == 6 and b == 6:
return (Pa*(1-Pb))/(Pa*(1-Pb) + (1-Pa)*Pb)
elif (a+b) % 2 != 0:
# if player A wins (probability Pa) a increases by 1, b remains same
# if player A doesn't win (probability 1-Pa) b increases by 1, a remains same
return Pa*prob_tiebraker_game(Pa, Pb, a+1, b) + (1-Pa)*prob_tiebraker_game(Pa, Pb, a, b+1)
elif (a+b) % 2 == 0:
# if player B wins (probability Pb) b increases by 1, a remains same
# if player B doesn't win (probability 1-Pb) a increases by 1, b remains same
return Pb*prob_tiebraker_game(Pa, Pb, a, b+1) + (1-Pb)*prob_tiebraker_game(Pa, Pb, a+1, b)
When player B is serving last two conditions will be swapped. You can write another function for that or combine both into one by adding an additional argument to function that indiciates whether player A served first or not.

Related

Goldbach graph using sagemath

I'm learning SageMath (uses Python 3)
and playing with the Goldbach conjecture.
I wrote this function (it works!):
def Goldbach(n):
if n % 2 != 0 or n <= 2:
show("No és parell")
else:
for i in srange(n):
if is_prime(i):
for j in srange(n):
if is_prime(j) and i + j == n:
a = [i, j]
show(a)
return
Now I'm trying (no idea) to do the following plot:
Denoting by r(2k) the number of Goldbach partitions of 2k,
the conjecture affirms that r(2k) > 0 if k > 1.
I have to do a graph of points (k, r(2k)), k > 2.
How could I do it?
First of all, let us get some better implementation in Sage
of the routine counting the number r(K) (for K > 2 some even integer) of the solutions for p + q = 2k, p, q prime numbers.
We count both solutions (p, q) and (q, p) when they differ.
def r(K):
if K not in ZZ or K <= 2 or K % 2:
return None
if K == 4:
return 1
count = 0
for p in primes(3, K):
for q in primes(3, K + 1 - p):
if p + q == K:
count += 1
return count
goldbach_points = [(K, r(K)) for K in range(4, 100,2)]
show(points(goldbach_points))
This gives:

integer programming: need help to formulate a constrain

I am trying to formulate a constrain for my math model. the constrain goal is:
if A = 1 and B = 1 then C <= D
otherwise (A or B or both are 0) there is no constraint.
A and B are binary variables. C and D are integer numbers.
so far I was able to come up with this equation:
M(A - 1) - (B - 1) + C <= D (M is a big big number)
this formulation does not hold when A = 1 and B = 0
You could do this in two steps, first introduce a variable X representing logical and of A and B.
X >= A + B - 1
X <= A
X <= B
Then use X to express the inequality:
C - M(1-X) <= D

How to approach this type of problem in permutation and combination?

Altitudes
Alice and Bob took a journey to the mountains. They have been climbing
up and down for N days and came home extremely tired.
Alice only remembers that they started their journey at an altitude of
H1 meters and they finished their wandering at an alitude of H2
meters. Bob only remembers that every day they changed their altitude
by A, B, or C meters. If their altitude on the ith day was x,
then their altitude on day i + 1 can be x + A, x + B, or x + C.
Now, Bob wonders in how many ways they could complete their journey.
Two journeys are considered different if and only if there exist a day
when the altitude that Alice and Bob covered that day during the first
journey differs from the altitude Alice and Bob covered that day during
the second journey.
Bob asks Alice to tell her the number of ways to complete the journey.
Bob needs your help to solve this problem.
Input format
The first and only line contains 6 integers N, H1, H2, A, B, C that
represents the number of days Alice and Bob have been wandering,
altitude on which they started their journey, altitude on which they
finished their journey, and three possible altitude changes,
respectively.
Output format
Print the answer modulo 10**9 + 7.
Constraints
1 <= N <= 10**5
-10**9 <= H1, H2 <= 10**9
-10**9 <= A, B, C <= 10**9
Sample Input
2 0 0 1 0 -1
Sample Output
3
Explanation
There are only 3 possible journeys-- (0, 0), (1, -1), (-1, 1).
Note
This problem comes originally from a hackerearth competition, now closed. The explanation for the sample input and output has been corrected.
Here is my solution in Python 3.
The question can be simplified from its 6 input parameters to only 4 parameters. There is no need for the beginning and ending altitudes--the difference of the two is enough. Also, we can change the daily altitude changes A, B, and C and get the same answer if we make a corresponding change to the total altitude change. For example, if we add 1 to each of A, B, and C, we could add N to the altitude change: 1 additional meter each day over N days means N additional meters total. We can "normalize" our daily altitude changes by sorting them so A is the smallest, then subtract A from each of the altitude changes and subtract N * A from the total altitude change. This means we now need to add a bunch of 0's and two other values (let's call them D and E). D is not larger than E.
We now have an easier problem: take N values, each of which is 0, D, or E, so they sum to a particular total (let's say H). This is the same at using up to N numbers equaling D or E, with the rest zeros.
We can use mathematics, in particular Bezout's identity, to see if this is possible. Some more mathematics can find all the ways of doing this. Once we know how many 0's, D's, and E's, we can use multinomial coefficients to find how many ways these values can be rearranged. Total all these up and we have the answer.
This code finds the total number of ways to complete the journey, and takes it modulo 10**9 + 7 only at the very end. This is possible since Python uses large integers. The largest result I found in my testing is for the input values 100000 0 100000 0 1 2 which results in a number with 47,710 digits before taking the modulus. This takes a little over 8 seconds on my machine.
This code is a little longer than necessary, since I made some of the routines more general than necessary for this problem. I did this so I can use them in other problems. I used many comments for clarity.
# Combinatorial routines -----------------------------------------------
def comb(n, k):
"""Compute the number of ways to choose k elements out of a pile of
n, ignoring the order of the elements. This is also called
combinations, or the binomial coefficient of n over k.
"""
if k < 0 or k > n:
return 0
result = 1
for i in range(min(k, n - k)):
result = result * (n - i) // (i + 1)
return result
def multcoeff(*args):
"""Return the multinomial coefficient
(n1 + n2 + ...)! / n1! / n2! / ..."""
if not args: # no parameters
return 1
# Find and store the index of the largest parameter so we can skip
# it (for efficiency)
skipndx = args.index(max(args))
newargs = args[:skipndx] + args[skipndx + 1:]
result = 1
num = args[skipndx] + 1 # a factor in the numerator
for n in newargs:
for den in range(1, n + 1): # a factor in the denominator
result = result * num // den
num += 1
return result
def new_multcoeff(prev_multcoeff, x, y, z, ag, bg):
"""Given a multinomial coefficient prev_multcoeff =
multcoeff(x-bg, y+ag, z+(bg-ag)), calculate multcoeff(x, y, z)).
NOTES: 1. This uses bg multiplications and bg divisions,
faster than doing multcoeff from scratch.
"""
result = prev_multcoeff
for d in range(1, ag + 1):
result *= y + d
for d in range(1, bg - ag + 1):
result *= z + d
for d in range(bg):
result //= x - d
return result
# Number theory routines -----------------------------------------------
def bezout(a, b):
"""For integers a and b, find an integral solution to
a*x + b*y = gcd(a, b).
RETURNS: (x, y, gcd)
NOTES: 1. This routine uses the convergents of the continued
fraction expansion of b / a, so it will be slightly
faster if a <= b, i.e. the parameters are sorted.
2. This routine ensures the gcd is nonnegative.
3. If a and/or b is zero, the corresponding x or y
will also be zero.
4. This routine is named after Bezout's identity, which
guarantees the existences of the solution x, y.
"""
if not a:
return (0, (b > 0) - (b < 0), abs(b)) # 2nd is sign(b)
p1, p = 0, 1 # numerators of the two previous convergents
q1, q = 1, 0 # denominators of the two previous convergents
negate_y = True # flag if negate y=q (True) or x=p (False)
quotient, remainder = divmod(b, a)
while remainder:
b, a = a, remainder
p, p1 = p * quotient + p1, p
q, q1 = q * quotient + q1, q
negate_y = not negate_y
quotient, remainder = divmod(b, a)
if a < 0:
p, q, a = -p, -q, -a # ensure the gcd is nonnegative
return (p, -q, a) if negate_y else (-p, q, a)
def byzantine_bball(a, b, s):
"""For nonnegative integers a, b, s, return information about
integer solutions x, y to a*x + b*y = s. This is
equivalent to finding a multiset containing only a and b that
sums to s. The name comes from getting a given basketball score
given scores for shots and free throws in a hypothetical game of
"byzantine basketball."
RETURNS: None if there is no solution, or an 8-tuple containing
x the smallest possible nonnegative integer value of
x.
y the value of y corresponding to the smallest
possible integral value of x. If this is negative,
there is no solution for nonnegative x, y.
g the greatest common divisor (gcd) of a, b.
u the found solution to a*u + b*v = g
v " "
ag a // g, or zero if g=0
bg b // g, or zero if g=0
sg s // g, or zero if g=0
NOTES: 1. If a and b are not both zero and one solution x, y is
returned, then all integer solutions are given by
x + t * bg, y - t * ag for any integer t.
2. This routine is slightly optimized for a <= b. In that
case, the solution returned also has the smallest sum
x + y among positive integer solutions.
"""
# Handle edge cases of zero parameter(s).
if 0 == a == b: # the only score possible from 0, 0 is 0
return (0, 0, 0, 0, 0, 0, 0, 0) if s == 0 else None
if a == 0:
sb = s // b
return (0, sb, b, 0, 1, 0, 1, sb) if s % b == 0 else None
if b == 0:
sa = s // a
return (sa, 0, a, 1, 0, 1, 0, sa) if s % a == 0 else None
# Find if the score is possible, ignoring the signs of x and y.
u, v, g = bezout(a, b)
if s % g:
return None # only multiples of the gcd are possible scores
# Find one way to get the score, ignoring the signs of x and y.
ag, bg, sg = a // g, b // g, s // g # we now have ag*u + bg*v = 1
x, y = sg * u, sg * v # we now have a*x + b*y = s
# Find the solution where x is nonnegative and as small as possible.
t = x // bg # Python rounds toward minus infinity--what we want
x, y = x - t * bg, y + t * ag
# Return the information
return (x, y, g, u, v, ag, bg, sg)
# Routines for this puzzle ---------------------------------------------
def altitude_reduced(n, h, d, e):
"""Return the number of distinct n-tuples containing only the
values 0, d, and e that sum to h. Assume that all these
numbers are integers and that 0 <= d <= e.
"""
# Handle some impossible special cases
if n < 0 or h < 0:
return 0
# Handle some other simple cases with zero values
if n == 0:
return 0 if h else 1
if 0 == d == e: # all step values are zero
return 0 if h else 1
if 0 == d or d == e: # e is the only non-zero step value
# If possible, return # of tuples with proper # of e's, the rest 0's
return 0 if h % e else comb(n, h // e)
# Handle the main case 0 < d < e
# --Try to get the solution with the fewest possible non-zero days:
# x d's and y e's and the rest zeros: all solutions are given by
# x + t * bg, y - t * ag
solutions_info = byzantine_bball(d, e, h)
if not solutions_info:
return 0 # no way at all to get h from d, e
x, y, _, _, _, ag, bg, _ = solutions_info
# --Loop over all solutions with nonnegative x, y, small enough x + y
result = 0
while y >= 0 and x + y <= n: # at most n non-zero days
# Find multcoeff(x, y, n - x - y), in a faster way
if result == 0: # 1st time through loop: no prev coeff available
amultcoeff = multcoeff(x, y, n - x - y)
else: # use previous multinomial coefficient
amultcoeff = new_multcoeff(amultcoeff, x, y, n - x - y, ag, bg)
result += amultcoeff
x, y = x + bg, y - ag # x+y increases by bg-ag >= 0
return result
def altitudes(input_str=None):
# Get the input
if input_str is None:
input_str = input('Numbers N H1 H2 A B C? ')
# input_str = '100000 0 100000 0 1 2' # replace with prev line for input
n, h1, h2, a, b, c = map(int, input_str.strip().split())
# Reduce the number of parameters by normalizing the values
h_diff = h2 - h1 # net altitude change
a, b, c = sorted((a, b, c)) # a is now the smallest
h, d, e = h_diff - n * a, b - a, c - a # reduce a to zero
# Solve the reduced problem
print(altitude_reduced(n, h, d, e) % (10**9 + 7))
if __name__ == '__main__':
altitudes()
Here are some of my test routines for the main problem. These are suitable for pytest.
# Testing, some with pytest ---------------------------------------------------
import itertools # for testing
import collections # for testing
def brute(n, h, d, e):
"""Do alt_reduced with brute force."""
return sum(1 for v in itertools.product({0, d, e}, repeat=n)
if sum(v) == h)
def brute_count(n, d, e):
"""Count achieved heights with brute force."""
if n < 0:
return collections.Counter()
return collections.Counter(
sum(v) for v in itertools.product({0, d, e}, repeat=n)
)
def test_impossible():
assert altitude_reduced(0, 6, 1, 2) == 0
assert altitude_reduced(-1, 6, 1, 2) == 0
assert altitude_reduced(3, -1, 1, 2) == 0
def test_simple():
assert altitude_reduced(1, 0, 0, 0) == 1
assert altitude_reduced(1, 1, 0, 0) == 0
assert altitude_reduced(1, -1, 0, 0) == 0
assert altitude_reduced(1, 1, 0, 1) == 1
assert altitude_reduced(1, 1, 1, 1) == 1
assert altitude_reduced(1, 2, 0, 1) == 0
assert altitude_reduced(1, 2, 1, 1) == 0
assert altitude_reduced(2, 4, 0, 3) == 0
assert altitude_reduced(2, 4, 3, 3) == 0
assert altitude_reduced(2, 4, 0, 2) == 1
assert altitude_reduced(2, 4, 2, 2) == 1
assert altitude_reduced(3, 4, 0, 2) == 3
assert altitude_reduced(3, 4, 2, 2) == 3
assert altitude_reduced(4, 4, 0, 2) == 6
assert altitude_reduced(4, 4, 2, 2) == 6
assert altitude_reduced(2, 6, 0, 2) == 0
assert altitude_reduced(2, 6, 2, 2) == 0
def test_main():
N = 12
maxcnt = 0
for n in range(-1, N):
for d in range(N): # must have 0 <= d
for e in range(d, N): # must have d <= e
counts = brute_count(n, d, e)
for h, cnt in counts.items():
if cnt == 25653:
print(n, h, d, e, cnt)
maxcnt = max(maxcnt, cnt)
assert cnt == altitude_reduced(n, h, d, e)
print(maxcnt) # got 25653 for N = 12, (n, h, d, e) = (11, 11, 1, 2) etc.

How could the following function be written using iteration instead of recursion?

How could the following function be written using iteration instead of recursion?
function mystery(b)
{
if b == 0 then
return 0
if (b / 2 == 0)
return mystery (b-1) + 3
else
return mystery (b-1) + 2
}
I assume that when you are doing b / 2 == 0 you are checking whether the number b is even or odd otherwise that is true only for case where b= 0,1.
Recursive function
def mystery(b):
if b == 0:
return 0
if b % 2 == 0:#check if b is even
return mystery(b-1)+3
else: return mystery(b-1)+ 2
Iterative function
def mystery_iter(b):
result= 0
while b > 0:
if b % 2 == 0:#check if b is even
result += 3
b= b-1
else:
result += 2
b= b-1
return result
At least two people suggest you reformulate the recursion as a loop. I instead suggest you first try to understand the mathematics of what the function is doing before considering unwinding the implicit recursive loop into an explicit iterative loop. Doing so in this case, you get a simpler answer:
def mystery(b):
if b == 0:
return 0
if b % 2 == 0:
return b // 2 * 5
return b // 2 * 5 + 2
This could be further reduced code-wise, possibly to a one-liner, if one desires. (Like #AkhileshPandey, I'm assuming that the division in (b / 2 == 0) was supposed to be a modulus operation (b % 2 == 0)) The above example is Python 3 as it wasn't clear what language the OP used, nor that the given code would run correctly in said language due to inconsistent use of then.

Is angle in between two angles

I have 3 angles a b c
a=315
b=20
c=45
ok so would like to know giving all three if b is in between a and c
i have the long way of doing this adding and subtracting that's seems to work. I would just like to get something smaller and maybe more efficient.
thanks
EDIT
Here is a picture what i am trying to say.
Ok I have angle L(currently 0) i add 45(or any angle) and subtract 45(or any angle) to get a and b (my view angle).
Now i need to know if the green dot is between a and b
(g> a || g > 0) && (g < b)
so in this picture only the top green dot will be true..
Sorry if I am not making my self clear my first language is not English
I had a similar problem. I got it. All the calculations are in degrees.
I needed to calculate id a gps location is inside a rectangle.
Or, I needed to see if an angle x is between angle check+r and angle check-r.
check-r<x<check+r.
If you need a<x<b, find the angle check in the middle of a and b and then the distance (r) of check from a or b.
The method normalize, changes the angles from -infinity...infinity to -180...180.
The method check, takes the arguments
x: the angle that we need to see if it is between the angles check-r and check+r.
check: the angle to check with.
r: the radius around angle check.
private static double normalize(double x) {
x = x % 360;
if (x>=180) {
return x-360;
}
if (x<-180) {
return x+360;
}
return x;
}
public static boolean check(double x, double check, double r) {
x = x - check;
x = normalize(x);
return x<r && x>-r;
}
1st off, every angle is between 2 other angles, what you're really asking is:
For given angles: a, b, and g, is g outside the reflex angle between a and b?
You can just go ahead and define a as the leftmost angle and b as the rightmost angle or you can solve for that, for example if either of these statements are true a is your leftmost angle:
a ≤ b ∧ b - a ≤ π
a > b ∧ a - b ≥ π
For simplicity let's say that your leftmost angle is l and your rightmost angle is r and you're trying to find if g is between them.
The problem here is the seem. There are essentially 3 positive cases that we're looking for:
l ≤ g ≤ r
l ≤ g ∧ r < l
g ≤ r ∧ r < l
If you're just defining a to be leftmost and b to be rightmost you're done here and your condition will look like:
a <= g && g <= b ||
a <= g && b < a ||
g <= b && b < a
If however you calculated the l and r you'll notice there is an optimization opportunity here in doing both processes at once. Your function will look like:
if(a <= b) {
if(b - a <= PI) {
return a <= g && g <= b;
} else {
return b <= g || g <= a;
}
} else {
if(a - b <= PI) {
return b <= g && g <= a;
} else {
return a <= g || g <= b;
}
}
Or if you need it you could expand into this nightmare condition:
a <= b ?
(b - a <= PI && a <= g && g <= b) || (b - a > PI && (b <= g || g <= a)) :
(a - b <= PI && b <= g && g <= a) || (a - b > PI && (a <= g || g <= b))
Note that all this math presumes that your input is in radians and in the range [0 : 2π].
Live Example
I personally had the same problem recently and found counterexamples for all the answers posted yet, so I will share my own approach.
Let a be the start angle and b the end angle and we are checking whether c is between them clockwise, that means when you go from a to b you must pass c. The approach of checking whether c is in the range from a to b gives you false positives when b is greater than a numerically. For example:
a=80°, b=320° and c=150°: a <= c <= b which would mean that c is between a and b.
But it isn't.
The approach that's working is to subtract 360 from b if it is greater than a and also subtract 360 from c if c is greater than a. Then check whether a <= c <= b.
In Java:
public static boolean isBetween(double c, double a, double b) {
if (b > a) b -= 360;
if (c > a) c -= 360;
return a <= c && c <= b;
}
This assumes that a, b and c are in range 0 to 360.
Some example:
isBetween(150, 80, 320) => false
isBetween(30, 80, 320) => true
isBetween(340, 80, 320) => true
isBetween(140, 0, 160) => true
isBetween(180, 0, 160) => false
There is an issue with the suggested solutions when handling negative angles (e.g. from=30 to=-29)
The suggested (kotlin) fix should be:
fun isBetween(from:Float,to:Float,check:Float,inclusive:Boolean = true):Boolean{
var a1 = to - from
a1 = (a1 + 180f).mod(360f) - 180f
if(a1<0f) a1+=360f
var a2 = check - from
a2 = (a2 + 180f).mod(360f) - 180f
if(a2<0f) a2+=360f
val between = if(inclusive) a2<=a1 else a2<a1 && a2>0f
println("$from->$to, $check, $between ($a1,$a2)")
return between }
Assuming a > c, you would actually use:
( b < a ) && ( b > c )
This is the same as checking if a value is between a lower and upper bound. Them being angles makes no difference, unless you want to take into account the fact that as you go round a circle, an angle of 405 is the same as an angle of 45. In which case you can just use a % 360 to get the angle betweeen 0 and 360.

Resources