我在一次求职面试中遇到了这个问题,我找不到解决方案的正确方法,所以我在这里发布了这个问题:
有一种机器人可以通过以下两种方式在坐标平面上移动:
鉴于机器人的当前位置为(x,y),如果方向如下所示,则机器人可以移动等于x和y的和:
(x,y) -> (x+y, y)
(x,y) -> (x, x+y)
现在给定一个初始点(x1,y1)和一个目的地点(x2,y2),您需要编写一个程序来检查机器人是否可以通过任意移动到达目的地。
注意:x1,y1,x2,y2> 0
说明:
假设机器人的起始点为(2,3),目标为(7,5)
在这种情况下,结果为是,因为机器人可以采用以下路径:
(2,3)->(2,2 + 3)=>(2,5)
(2,5)->(2 + 5,5)=>(7,5)
假设机器人的起始点为(2,3),目标为(4,5)
在这种情况下的结果为“否”,因为无论机器人采用哪种路径都无法达到(4,5)
答案 0 :(得分:13)
一种方法是递归地探索所有可能的动作,直到达到目标为止。
要考虑的事情是,机器人可以无限期地移动(永远不会到达目标),因此您需要一个端盖,以便功能得以完成。幸运的是,位置始终在x
和y
轴上增加,因此,当x坐标或y坐标大于目标时,您可以放弃探索该路径。
类似这样:
def can_reach_target(pos, target):
if pos == target:
return True
if pos[0] > target[0] or pos[1] > target[1]:
return False
return can_reach_target((pos[0], sum(pos)), target) or \
can_reach_target((sum(pos), pos[1]), target)
它有效:
>>> can_reach_target((2,3),(7,5))
True
>>> can_reach_target((2,3),(4,5))
False
一个局限性是,这不适用于负坐标-不确定这是否是必需条件,请让我知道是否满足要求,然后我将调整答案。
另一方面,如果不允许使用负坐标,那么我们也可以将其作为Dave suggests处理。这是非常有效的,因为认识到机器人只有一种方式到达每个坐标。
该方法依赖于能够确定我们步进的方式:增加x坐标或y坐标。我们可以通过选择两者中较大的一个来确定最后更改的坐标。以下证明可以保证是这种情况。
状态更改的可能性是:
1. (a, b) => (a+b, b) a x-coordinate change
和
2. (a, b) => (a, a+b) a y-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))
可以正常工作:
>>> 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 :(得分:11)
后退。我假设起始坐标为正。假设您想知道(a,b)的起点是否与(x,y)的终点兼容。从(x,y)退后一步,您处于(x-y,y)或(x,y-x)。如果x> y选择前者,否则选择后者。
答案 2 :(得分:10)
我同意戴夫的观点,即倒退是一种有效的方法。如果只有正坐标是合法的,则每个坐标最多具有一个有效父级。这样一来,您就可以向后工作,而不会组合爆炸。
这是一个示例实现:
def get_path(source, destination):
path = [destination]
c,d = destination
while True:
if (c,d) == source:
return list(reversed(path))
if c > d:
c -= d
else:
d -= c
path.append((c,d))
if c < source[0] or d < source[1]:
return None
print(get_path((1,1), (1,1)))
print(get_path((2,3), (7,5)))
print(get_path((2,3), (4,5)))
print(get_path((1,1), (6761, 1966)))
print(get_path((4795, 1966), (6761, 1966)))
结果:
[(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)解有用:
答案 3 :(得分:3)
为此,递归功能应该可以正常工作。您甚至还有很多可能性。
def find_if_possible(x,y,x_obj,y_obj,max_depth):
if(max_depth < 0):
return 0
elif(x == x_obj and y == y_obj):
return 1
elif(x>x_obj or y>y_obj):
return 0
else:
return(sum(find_if_possible(x+y,y,x_obj,y_obj,max_depth-1),find_if_possible(x,y+x,x_obj,y_obj,max_depth-1))