我正在做谷歌foobar挑战,但在以下挑战上没时间我试图看看我做错了什么。
挑战
作为Lambda指挥官的私人助理,您已经完成了配置LAMBCHOP世界末日设备轴向齿轮的任务。它应该非常简单 - 只需添加齿轮即可创建合适的旋转比率。但问题是,由于LAMBCHOP的布局以及支撑它的梁和管道的复杂系统,支撑齿轮的销钉固定到位。
LAMBCHOP的工程师已经为您列出了识别各种支撑梁上钉子组的位置的列表。您需要在每个挂钉上放置一个齿轮(否则齿轮会与未占用的挂钉碰撞)。工程师拥有大量不同尺寸的齿轮,因此您可以选择任意尺寸的齿轮,从半径为1。您的目标是建立一个系统,其中最后一个齿轮以第一档的速率(每分钟转数或转速)的两倍旋转,无论方向如何。每个齿轮(最后一个齿轮除外)接触并转动下一个挂钉的齿轮。
给定一个名为pegs的不同正整数列表,表示每个peg沿支撑梁的位置,写一个函数答案(pegs),如果有解,则返回两个正整数的列表a和b表示为了实现上述目标,第一档半径的分子和分母以其最简单的形式,使得半径= a / b。比率a / b应大于或等于1.并非所有支持配置都必须能够创建正确的旋转比率,因此如果任务不可能,则函数answer(pegs)应返回列表[-1, -1]。
例如,如果钉位于[4,30,50],则第一个齿轮的半径可以为12,第二个齿轮的半径可以为14,最后一个齿轮的半径为6。因此,最后一个齿轮的旋转速度是第一个齿轮的两倍。在这种情况下,钉子将是[4,30,50]并且答案(钉子)应该返回[12,1]。
列表挂钩将按升序排序,并包含至少2个且不超过20个不同的正整数,所有正整数均在1和10000之间。
测试用例
Inputs:
(int list) pegs = [4, 30, 50]
Output:
(int list) [12, 1]
Inputs:
(int list) pegs = [4, 17, 50]
Output:
(int list) [-1, -1]
我目前的解决方案如下
def answer(pegs):
n = len(pegs)
g = range(n)
k = pegs[1] - pegs[0]
for i in range(0,k,2):
g[0] = i
for j in range(1,n):
g[j] = (pegs[j] - pegs[j-1]) - g[j-1]
if any(b < 1 for b in g):
continue
if 1.0*g[0]/g[-1] == 2.0:
return [g[0],1]
return [-1, -1]
我只能通过6个测试用例我现在已经没时间了,但我很好奇什么是正确的解决方案
答案 0 :(得分:8)
这是python 2.7中的工作代码,所有测试用例都由Google传递。这是我在抓论一段时间之后提出的最佳解决方案:
from fractions import Fraction
def answer(pegs):
arrLength = len(pegs)
if ((not pegs) or arrLength == 1):
return [-1,-1]
even = True if (arrLength % 2 == 0) else False
sum = (- pegs[0] + pegs[arrLength - 1]) if even else (- pegs[0] - pegs[arrLength -1])
if (arrLength > 2):
for index in xrange(1, arrLength-1):
sum += 2 * (-1)**(index+1) * pegs[index]
FirstGearRadius = Fraction(2 * (float(sum)/3 if even else sum)).limit_denominator()
#now that we have the radius of the first gear, we should again check the input array of pegs to verify that
#the pegs radius' is atleast 1.
currentRadius = FirstGearRadius
for index in xrange(0, arrLength-2):
CenterDistance = pegs[index+1] - pegs[index]
NextRadius = CenterDistance - currentRadius
if (currentRadius < 1 or NextRadius < 1):
return [-1,-1]
else:
currentRadius = NextRadius
return [FirstGearRadius.numerator, FirstGearRadius.denominator]
请参阅此图片,了解我如何提出此代码:
答案 1 :(得分:4)
我认为您的解决方案是沿着正确的方向行进,但不允许小数半径。
请注意,我们可以象征性地考虑您的算法,设置g[0]=x
,然后根据g[j]
计算所有x
值。事实证明,每个g[j]
是x
的线性函数(渐变1或-1)。
因此,您会发现g[-1] = a+mx
其中m为+1或-1,a为整数。
对于存在的解决方案,您需要解决方程式:
g[0]/g[-1] = 2
x/(a+mx) = 2
x=2(a+mx)
x(1-2m)=2a
x=2a/(1-2m)
所以这给出了一个候选值x(作为分数),然后你可以重新检查它以确保没有中间半径变为负数。
答案 2 :(得分:3)
如果您对完美的工作解决方案感兴趣,这就是我写的:https://gist.github.com/1lann/be45311db1bd8cbbe6650b0a3e9d1977
它构造了一个方程组,它解决了每个齿轮每个半径的值。以下是它如何计算4个桩的解决方案。
方程组将是:
2x + a = peg[1] - peg[0]
a + b = peg[2] - peg[1]
b + x = peg[3] - peg[2]
我的程序构造一个矩阵来表示:
[
[2, 1, 0],
[0, 1, 1],
[1, 0, 1]
]
然后计算矩阵的倒数,然后将其应用于桩钉之间的距离,以便找到每个齿轮的半径。如果您想知道数学如何工作,您可以查看:https://www.mathsisfun.com/algebra/systems-linear-equations-matrices.html
然后验证每个齿轮的半径> = 1,最后返回x * 2的值。为了支持分数(任何有理数),所有数字都是分数类型。
我对一些边缘情况进行了硬编码,例如当pegs的数量= 2时。
答案 3 :(得分:2)
单次通过方法
表示p_0,p_1,...,p_n
钉的位置。
让我通过以下递归关系定义序列a_k
a_0 = 0
a_k = a_{k-1}+(-1)^{k+1}(p_k-p_{k-1})
如果您计算a_n
并简化,您会发现这与p_k
的交替总和与其他答案中看到的相同,在大多数答案中,除第一个和第二个词语外,大多数词语的系数为2
。最后一个。
我们将在下面看到为什么可以方便地查看此数字序列。
如果我们用r_0,r_1,...,r_n
表示齿轮的半径,则它们满足方程式
r_k = (-1)^k(r_0-a_k)
此外,半径不小于1
的条件等于不等式
r_0 >= a_0 + 1
r_1 <= a_1 - 1
r_2 >= a_2 + 1
r_3 <= a_3 - 1
...
这意味着可以将半径不等式的全部序列简化为一对不等式
max(a_k+1, for k even) <= r_0 <= min(a_k - 1, for k odd)
最后,速度加倍的条件是
(1+2(-1)^{n+1}) r_0 = 2a_n(-1)^{n+1}
因此,计算序列a_n
可以让我们同时获得答案和一遍中的半径限制。
用Python编写的代码看起来像as follows。随时进行进一步改进。
define solution(pegs):
n = len(pegs)
if n<2:
return (-1,-1)
an = 0 # This contains, at each step the value of the sequence a_k
pm_one = -1 # The alternating sign in the formulas above.
# This and the next will get the bounds for the radii.
max_even_a = -float("inf")
min_odd_a = float("inf")
for i in range(n-1):
an -= pm_one*(pegs[i+1]-pegs[i])
pm_one *=-1
if not i&1:
min_odd_a = min(min_odd_a, an)
else:
max_even_a = max(max_even_a, an)
# In the formulas above the numerator has a (-1)^{n+1} factor.
# Here the sign has been cancelled with the sign of the denominator.
numerator = 2*an
denominator = abs(1+2*pm_one)
# The inequalities in the explanation are here written as integers.
# Note that here denominator is positive. So, passing it to the other side
# doesn't change the sign of the inequality.
# Of course, the inequalities have here the negated sign and an OR
# because we are detecting when they fail.
if numerator < denominator*(max_even_a+1) \
or numerator > denominator*(min_odd_a-1):
return (-1,-1)
# Sometimes the denominator is 3. If it can be cancelled we do so.
if pm_one == 1 and numerator%3 == 0:
numerator //=3
denominator = 1
return (numerator, denominator)
答案 4 :(得分:2)
我在 2020 年 1 月遇到了这个问题。完成后,我想看看其他人是否按照我的方式进行了操作,但看起来不像。
销钉的位置以及第一个半径表示最后一个齿轮的半径是多少。第一个齿轮半径和最后一个齿轮半径之间存在线性关系,以及第一个齿轮半径大小的边界:
这是我编写的函数,它将接收销钉位置和起始齿轮半径并计算隐含的最后一个齿轮半径:
def implied_last_radius(pegs, start_radius):
diffs = [start_radius] + [x - y for x, y in zip(pegs[1:], pegs[:-1])]
for i in range(1, len(diffs)):
diffs[i] = diffs[i] - diffs[i - 1]
return diffs[-1]
问题指出所有齿轮的半径必须>= 1,最后一个齿轮必须是第一个齿轮大小的一半。这对半径为 2 的第一个齿轮设置了下限(即,第一个齿轮半径小于 2 的任何有效结果将导致最后一个齿轮半径小于 1,这是不允许的)。这也为第一个齿轮尺寸设置了上限,因为第二个挂钩中的齿轮也必须具有最小半径 1。因此:
first_radius_diff = pegs[1] - pegs[0]
first_radius_range = (2, first_radius_diff - 1)
我们可以使用上述函数计算每个第一个半径的隐含半径。对于挂钩 [4, 30, 50] 答案是:
First Radii = (2, 25)
Implied Last Radii = (-4, 19)
即如果第一个半径为2,则最后一个半径必须为-4(无效),如果第一个半径为25,则最后一个半径为19。线性关系可以建模为y = mx + b
:
m = (implied_radii[1] - implied_radii[0]) / (first_radius_range[1] - first_radius_range[0])
b = implied_radii[0] - m * first_radius_range[0]
现在我们有了第一个和最后一个齿轮半径大小之间的关系。当最后一个半径是值的一半时,我们简单地计算第一个半径:
first_radius = b / (0.5 - m)
最后,我们检查该值是否在允许的第一齿轮半径范围内,但重要的是它不需要任何半径小于 1 的销钉上的齿轮:
def solvable(pegs, start_radius, radius_range):
diffs = [start_radius] + [x - y for x, y in zip(pegs[1:], pegs[:-1])]
for i in range(1, len(diffs)):
diffs[i] = diffs[i] - diffs[i - 1]
return radius_range[0] <= start_radius <= radius_range[1] and all([diff >= 1 for diff in diffs])
最后一个让我感到棘手的部分是转换为简化的小数形式。这很容易通过将所有数值映射到 fractions.Fraction
类来解决。如果第一齿轮半径通过可解测试,则返回分子和分母,否则返回 [-1, -1]。
答案 5 :(得分:0)
from fractions import Fraction
def answer(a):
l = len(a)
if(not a or l == 1): return [-1,-1]
s = (a[l-1] - a[0]) if (l % 2 == 0) else (-a[l-1]-a[0]);
if(l > 2):
for i in range(1, l-1): s+= 2 * (-1)**(i+1) * a[i]
v = Fraction(2*(float(s)/3 if (l%2==0) else float(s))).limit_denominator();
c = v;
for i in range(0, l-2):
d = a[i+1] - a[i]
n = d - c
if(c < 1 or n < 1): return [-1,-1]
else: c = n
return [v.numerator, v.denominator];
答案 6 :(得分:0)
通过的解决方案:
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]
一些测试
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]
输出:
[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]
一些思考
# 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 /
答案 7 :(得分:0)
在评估了本问答中的大多数解决方案之后,我计算了结果。
通过链接和实现时间评估解决方案(因为随着时间的推移人们发现越来越快的解决方案)。除ThisWind解决方案外,所有解决方案均已通过验证
Number of test iterations: 10000
solutionValDo : 7.3772001
solutionThisWind : 1.1203797
solutionNotDijkstra : 0.3143226
salutionLamichhane : 6.6052445
solutioncbarraford : 26.4765467
solution1lann : 147.5525556
solutionDayz : 6.619154
答案 8 :(得分:0)
昨晚我收到了他们的邀请,我在这个问题上工作了几个小时。我创建了这个问题的解决方案。
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]))
答案 9 :(得分:0)
在此处添加我的递归解决方案。
def solutionNader(pegs):
"""
returns the radius of cog i
ri = Xi -/+ r0
also sets the ans for the fractional requirement.
"""
def helper(X,i):
if i == len(pegs)-1: # last cog, we need to make sure its half of r0
X = 2*(pegs[i]-pegs[i-1]-X)
if i%2:
ans[0],ans[1] = [X,3] if (X)%3 else [X/3,1]
return X/6.0
else:
ans[0],ans[1] = [-X,1]
return -X/2
#recursively calculate the radius of next cog
r_next = helper(pegs[i]-pegs[i-1]-X,i+1) if i>0 else helper(0,1)
#radius of r = gap bitween the pegs - r of next cog
r = pegs[i+1]-pegs[i]-r_next
if r < 1:
ans[0],ans[1] = [-1,-1]
raise Exception('Invalid Cog')
return r
try:
ans = [-1,-1]
helper(0,0)
finally:
return ans
感谢@Ashitakalax 的性能测试
测试迭代次数:10000
答案 10 :(得分:0)
这是我想出的解决方案,我在 2021 年 3 月 30 日做到了,并且通过了 verify solution.py
和 submit solution.py
。
解决方案 (pegs.py
)
import numpy as np
from fractions import Fraction
def solution(pegs):
# constraint I: gear[i] + gear[i+1] == distance[i]
# constraint II: gear[0] == 2 * gear[-1]
# constraint III: gear[0] >= 1
#
# I ensures that the gears fit into the spaces of the pegs
# II ensures that the total gear ration is 2:1
# III ensures that the gears have at least a radius of 1
pegs = np.array(pegs)
n = len(pegs)
gear_1 = [-1, -1]
distances = np.zeros(n - 1)
system_equations = np.zeros([n, n + 1])
# calculate the distances
for i in range(n - 1):
distances[i] = pegs[i + 1] - pegs[i]
# The first equation is about constraint II
system_equations[0, 0] = 1
system_equations[0, -2] = -2
# The other equations are about constraint I
for i in range(n - 1):
system_equations[i + 1, i] = 1
system_equations[i + 1, i + 1] = 1
system_equations[i + 1, -1] = distances[i]
# check for solvability
rank_a = np.linalg.matrix_rank(system_equations[:, 0:n])
rank_ac = np.linalg.matrix_rank(system_equations)
# System of linear equation has no solution (not full rank)
if rank_a != rank_ac:
return gear_1
# The system of linear equations has indefinite solutions (not expected)
if rank_ac < n:
return gear_1
# Solve the system of linear equations
gears = np.linalg.inv(system_equations[:, 0:n]).dot(system_equations[:, n])
# Check validity of the solution
if is_no_solution(distances, gears):
return gear_1
# convert gear 1 to integer fraction
frac_gear_1 = Fraction(gears[0]).limit_denominator()
gear_1[0] = int(frac_gear_1.numerator)
gear_1[1] = int(frac_gear_1.denominator)
# return the solution
return gear_1
# Check if the solution is valid
def is_no_solution(distances, gears):
# Check constraint I
for i in range(len(distances)):
if gears[i] + gears[i + 1] - distances[i] > 0.01:
return True
# Check constraint II
if gears[0] != gears[-1] * 2:
return True
# Check constraint III
for gear in gears:
if gear < 1:
return True
# no constraint has been violated thus the solution is valid
return False
测试(test_pegs.py
)
from unittest import TestCase
from pegs import solution
class Test(TestCase):
def test_solution(self):
# provided test-cases
self.assertEqual([12, 1], solution([4, 30, 50]))
self.assertEqual([-1, -1], solution([4, 17, 50]))
# my own test-cases
self.assertEqual([10, 3], solution([5, 10]))
# test-cases from Stack Overflow @thiswind
self.assertEqual([100, 3], solution([1, 51]))
self.assertEqual([20, 1], solution([1, 31]))
self.assertEqual([20, 1], solution([1, 31, 51, 71]))
self.assertEqual([20, 1], solution([1, 31, 51, 71, 91]))
self.assertEqual([4, 1], solution([4, 9, 17, 31, 40]))
答案 11 :(得分:-1)
我的python代码使用相当基本的操作来完成任务,而不会强行执行它。但是我很懒,没有真正评论,所以您必须自己弄清楚。它通过了foobar解决方案,因此肯定可以工作。
def answer(pegs):
distances = [pegs[x+1]-pegs[x] for x in range(len(pegs)-1)]
x = 0
for i in distances: #gets d_(n)-d_(n-1)+d_(n-2)...+-d_(1)
x = i - x
#this tests if firstGearRadius is positive or negative
if len(distances)%2 == 0: #if it's positive
solution = [x*-2,1]
elif len(distances)%2 == 1: #if it's negative
if x*2 % 3 == 0: #if the numerator is divisible by 3
solution = [x*2/3,1]
else:
solution = [x*2,3]
#finds sizes of the gears
gearSizes = [float(solution[0])/float(solution[1])]
x = gearSizes[0]
for i in distances:
x = i - x
gearSizes.append(x)
if any([True for x in gearSizes if x<=1]): #gears must be at least 1 unit radius
return [-1,-1]
else:
return solution