Algorithm 机器人能到达点(x,y)吗?
我在一次面试中遇到了这个问题&我无法找到正确的解决方法,因此,我将这个问题发布在这里: 有一个机器人可以通过以下两种方式在坐标平面上移动: 假设机器人当前位置为(x,y),机器人可以在以下任一方向移动等于x&y之和:Algorithm 机器人能到达点(x,y)吗?,algorithm,coordinates,Algorithm,Coordinates,我在一次面试中遇到了这个问题&我无法找到正确的解决方法,因此,我将这个问题发布在这里: 有一个机器人可以通过以下两种方式在坐标平面上移动: 假设机器人当前位置为(x,y),机器人可以在以下任一方向移动等于x&y之和: (x,y) -> (x+y, y) (x,y) -> (x, x+y) 现在,给定一个初始点(x1,y1)和一个目标点(x2,y2),您需要编写一个程序来检查机器人是否能够通过任意次数的移动到达目标 注:x1、y1、x2、y2>0 说明: 假设机器人的初始点为
(x,y) -> (x+y, y)
(x,y) -> (x, x+y)
现在,给定一个初始点(x1,y1)和一个目标点(x2,y2),您需要编写一个程序来检查机器人是否能够通过任意次数的移动到达目标
注:x1、y1、x2、y2>0
说明:
x
和y
轴上的位置总是在增加,因此当x坐标或y坐标大于目标时,您可以放弃探索该路径
比如:
def能否达到目标(位置,目标):
如果pos==目标:
返回真值
如果位置[0]>目标[0]或位置[1]>目标[1]:
返回错误
返回可以到达目标((位置[0],总和(位置)),目标)或\
能否达到目标((合计(位置),位置[1]),目标)
它的工作原理是:
>>能否达到目标((2,3)、(7,5))
真的
>>>能否达到目标((2,3)、(4,5))
假的
一个限制是,这不适用于负坐标-不确定这是否是一个要求,请告诉我是否是,我会调整答案
Bactracking 另一方面,如果不允许负坐标,那么我们也可以将其视为。这是更有效的,因为意识到有一个,而且只有一个机器人的方式获得每个坐标 该方法依赖于能够确定我们的步骤:增加x坐标或y坐标。我们可以通过选择两个坐标中较大的一个来确定上次更改的坐标。下面的证据保证了这一点 状态更改的可能性包括:
1. (a, b) => (a+b, b) a x-coordinate change
以及
在案例(1)中,x坐标现在更大,因为:
a > 0
a + b > b (add b to both sides)
同样地,由于b
也是>0
,我们可以推断a+b
是>a
现在我们可以从目标开始问:是哪个坐标引导我们来到这里的?答案很简单。如果x坐标大于y坐标,则从x坐标减去y坐标,否则从y坐标减去x坐标 也就是说,对于坐标,
(x,y)
,如果x>y
,那么我们来自(x-y,y)
,否则(x,y-x)
第一个代码现在可以调整为:
def can_reach_target(pos, target):
if pos == target:
return True
if target[0] < pos[0] or target[1] < pos[1]:
return False
x, y = target
return can_reach_target(pos, (x-y,y) if x > y else (x,y-x))
时间安排
因此,您可以看到,在这两种情况下,回溯程序的速度几乎是原来的三倍。递归函数应该可以很好地实现这一点。你甚至得到了很多可能性
def find_如果可能(x,y,x_obj,y_obj,max_depth):
如果(最大深度<0):
返回0
elif(x==x_obj和y==y_obj):
返回1
elif(x>x\U obj或y>y\U obj):
返回0
其他:
返回(求和(如果可能的话查找(x+y,y,x\u obj,y\u obj,max\u depth-1),如果可能的话查找(x,y+x,x\u obj,y\u obj,max\u depth-1))
向后走。我假设起始坐标为正。假设你想知道(a,b)的起点是否与(x,y)的终点兼容。从(x,y)后退一步,你要么在(x-y,y)或者(x,y-x).如果x>y选择前者,否则选择后者。我同意Dave的观点,向后走是一种有效的方法。如果只有正坐标是合法的,那么每个坐标最多有一个有效的父坐标。这样,你就可以向后走,而不会出现组合爆炸
下面是一个示例实现:
def get_路径(源、目标):
路径=[目的地]
c、 d=目的地
尽管如此:
如果(c,d)=源:
返回列表(反向(路径))
如果c>d:
c-=d
其他:
d-=c
路径追加((c,d))
如果c<源[0]或d<源[1]:
一无所获
打印(获取路径((1,1)、(1,1)))
打印(获取路径((2,3)、(7,5)))
打印(获取路径((2,3)、(4,5)))
打印(获取路径((1,1)、(67611966)))
打印(获取路径((47951966),(67611966)))
结果:
[(1, 1)]
[(2, 3), (2, 5), (7, 5)]
None
[(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (6, 5), (11, 5), (16, 5), (21, 5), (26, 5), (31, 5), (36, 5), (41, 5), (46, 5), (46, 51), (46, 97), (143, 97), (143, 240), (383, 240), (623, 240), (863, 240), (863, 1103), (863, 1966), (2829, 1966), (4795, 1966), (6761, 1966)]
[(4795, 1966), (6761, 1966)]
附录:我在此过程中所做的一些观察可能有助于找到O(1)解决方案:
- (a,b)可从(1,1)到达当且仅当a和b是互质
- 如果a和b有一个公共因子,那么(a,b)的所有子级也有该公共因子。等价地,如果存在从(a,b)到(c,d)的路径,那么对于任何正整数n,也存在从(n*a,n*b)到(n*c,n*d)的路径
- 如果a和b是互质而不是(1,1),那么有无限多的互质坐标无法从(a,b)中获得。通过选择(a,b)作为起点,你实际上将自己限制在(1,1)形成的树的某个子分支上。你永远无法到达(a,b)的任何兄弟分支,其中有无限多个坐标
>>> can_reach_target((2,3),(7,5))
True
>>> can_reach_target((2,3),(4,5))
False
>>> timeit.timeit('brute_force((2,3),(62,3))',globals=locals(),number=10**5)
3.41243960801512
>>> timeit.timeit('backtracker((2,3),(62,3))',globals=locals(),number=10**5)
1.4046142909792252
>>> timeit.timeit('brute_force((2,3),(602,3))',globals=locals(),number=10**4)
3.518286211998202
>>> timeit.timeit('backtracker((2,3),(602,3))',globals=locals(),number=10**4)
1.4182081500184722
[(1, 1)]
[(2, 3), (2, 5), (7, 5)]
None
[(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (6, 5), (11, 5), (16, 5), (21, 5), (26, 5), (31, 5), (36, 5), (41, 5), (46, 5), (46, 51), (46, 97), (143, 97), (143, 240), (383, 240), (623, 240), (863, 240), (863, 1103), (863, 1966), (2829, 1966), (4795, 1966), (6761, 1966)]
[(4795, 1966), (6761, 1966)]