I was doing the google foobar challenge but ran out of time on the following challenge i am trying to see what i did wrong.
Challenge
As
a passed solution:
from fractions import Fraction
def solution(p):
n = len(p)
if n >= 2:
r0_n = -2 * (p[n - 1] + reduce(
lambda a, b: a + b, [0] + [(-1)**i * 2 * p[i]
for i in range(n - 2, 0, -1)]) + (-1)**(n-1)*p[0])
r0_d = 1 + ((n+1) % 2)*2
if r0_n < r0_d:
return [-1, -1]
r = ['NAN'] * n
r[0] = float(r0_n) / float(r0_d)
for i in range(1, n):
r[i] = p[i] - p[i - 1] - r[i - 1]
if r[i] < 1:
return [-1, -1]
r0 = Fraction(r0_n, r0_d)
r0.limit_denominator()
return [r0.numerator, r0.denominator]
return [-1, -1]
some tests:
if __name__ == '__main__':
print solution([4, 30, 50]), [12, 1]
print solution([4, 17, 50]), [-1, -1]
print solution([1, 51]), [100, 3]
print solution([1, 31]), [20, 1]
print solution([1, 31, 51, 71]), [20, 1]
print solution([1, 31, 51, 71, 91]), [20, 1]
print solution([4, 9, 17, 31, 40]), [4, 1]
output:
[12, 1] [12, 1]
[-1, -1] [-1, -1]
[100, 3] [100, 3]
[20, 1] [20, 1]
[20, 1] [20, 1]
[20, 1] [20, 1]
[4, 1] [4, 1]
some thinking
# equaltion | i
# ---------------------|---
# / r0 + r1 == p1 - p0 [0]
# | r1 + r2 == p2 - p1 [1]
# | r2 + r3 == p3 - p2 [2]
# | r3 + r4 == p4 - p3 [3]
# | r4 + r5 == p5 - p4 [4]
# \ r5 == r0/2 [5]
#
#
# / r0 + r1 + 0 + 0 + 0 + 0 = p1 - p0
# | 0 + r1 + r2 + 0 + 0 + 0 = p2 - p1
# | 0 + 0 + r2 + r3 + 0 + 0 = p3 - p2
# | 0 + 0 + 0 + r3 + r4 + 0 = p4 - p3
# | 0 + 0 + 0 + 0 + r4 + r5 = p4 - p4
# \ r0 + 0 + 0 + 0 + 0 - 2*r5 = 0
#
# / 1 1 0 0 0 0 \ / r0 \ / p1 - p0 \
# | 0 1 1 0 0 0 | | r1 | | p2 - p1 |
# | 0 0 1 1 0 0 | | r2 | | p3 - p2 |
# | 0 0 0 1 1 0 | * | r3 | = | p4 - p3 |
# | 0 0 0 0 1 1 | | r4 | | p5 - p4 |
# \ 1 0 0 0 0 -2 / \ r5 / \ 0 /
If you're interested in a perfect working solution, this is what I wrote: https://gist.github.com/1lann/be45311db1bd8cbbe6650b0a3e9d1977
It constructs a system of equations where it solves the values for every radius of every gear. Here's how it computes the solution for 4 pegs for example.
The system of equations would be:
2x + a = peg[1] - peg[0]
a + b = peg[2] - peg[1]
b + x = peg[3] - peg[2]
My program constructs a matrix to represent this:
[
[2, 1, 0],
[0, 1, 1],
[1, 0, 1]
]
It then computes the inverse of the matrix, and then applies it to the distances between the pegs in order to find the radius of every gear. If you're wondering how the maths work, you can look at: https://www.mathsisfun.com/algebra/systems-linear-equations-matrices.html
Each gear is then verified to have a radius >= 1, and finally the value of x*2 is returned. In order to support fractions (any rational number), all numbers are of a Fraction type.
I did hard code some edge cases, such as when the number of pegs = 2.
I got their invitation last night and I worked on this problem for several hours. I created my solution of this problem.
from fractions import Fraction
from sympy import symbols, solve
def create_equations(_pegs, _isEven, smbls, r0):
eq = None
temp = symbols('temp')
if _isEven:
for i in range(len(smbls)):
if i == 0:
eq = -r0 - r0 / 2 - smbls[i] + temp
if 0 < i < len(smbls):
eq = eq.subs(temp, (-1) ** (i + 1) * 2 * smbls[i] + temp)
if i == len(smbls) - 1:
eq = eq.subs(temp, - smbls[i])
else:
for i in range(len(smbls)):
if i == 0:
eq = -r0 + r0 / 2 - smbls[i] + temp
if 0 < i < len(smbls):
eq = eq.subs(temp, (-1) ** (i + 1) * 2 * smbls[i] + temp)
if i == len(smbls) - 1:
eq = eq.subs(temp, smbls[i])
return eq
def create_symbols(len_pegs):
smbls = []
for i in range(len_pegs):
smbls.append(symbols("P" + str(i)))
r0 = symbols("r0")
return smbls, r0
def answer(pegs):
# first check
len_pegs = len(pegs)
if (not pegs) or len_pegs == 1:
return [-1, -1]
isEven = True if (len_pegs % 2 == 0) else False
# create list of symbols used in equation based on list length
smbls, r0 = create_symbols(len_pegs)
# the function returns an equation based on the following equation
# for even list length : (0 = r0 + rn -Pn + 2Pn-1 - 2Pn-2 + 2Pn-3 ... - P1 + P0)
# for odd list length : (0 = r0 - rn -Pn + 2Pn-1 - 2Pn-2 + 2Pn-3 ... + P1 - P0)
# where rn = r0/2
equation = create_equations(pegs, isEven, smbls, r0)
# substituting values of variables in the equation
for i in range(len_pegs):
equation = equation.subs(smbls[i], pegs[i])
# solving the equation and simplifying float values to simple fraction
radius_g1 = float(solve(equation)[0])
radius_g1 = Fraction(radius_g1).limit_denominator()
# finally we check if radius of any gear is less than one as required
radius_cur = radius_g1
for i in range(0, len_pegs-1):
dist_c = pegs[i + 1] - pegs[i]
radius_next = dist_c - radius_cur
if radius_cur < 1 or radius_next < 1:
return [-1, -1]
else:
radius_cur = radius_next
# return the end results [numerator, denominator]
return [radius_g1.numerator, radius_g1.denominator]
if __name__ == "__main__":
# try some examples
print(answer([4, 30, 50, 56]))
print(answer([4, 17, 50]))