Может ли робот достичь точки (x, y)? - PullRequest
0 голосов
/ 27 августа 2018

Я столкнулся с этим вопросом в одном из собеседований, и я не могу найти правильный алгоритм решения, поэтому я публикую этот вопрос здесь:

Есть робот, который может двигаться на координатной плоскости двумя способами:

Учитывая, что текущая позиция робота равна (x, y), робот может двигаться, равный сумме x & y, в любом случае, если директона такова:

(x,y)  ->  (x+y, y)
(x,y)  ->  (x, x+y)

Теперь, учитывая начальную точку (x1, y1) и точку назначения (x2, y2), вам нужно написать программу, чтобы проверить, сможет ли робот когда-либо достичь пункта назначения, совершив любое количество ходов.

Примечание: x1, y1, x2, y2> 0

Пояснение:

  1. Предположим, что начальная точка робота (2,3) и десинтация (7,5)

    Результат в этом случае - да, поскольку робот может пойти по этому пути:

    (2,3) -> (2, 2 + 3) => (2, 5)

    (2,5) -> (2 + 5, 5) => (7,5)

  2. Предположим, что начальная точка робота (2,3) и десинтация (4,5)

    Результат в этом случае - Нет, так как независимо от того, по какому пути робот пойдет, он не сможет достичь (4,5)

Ответы [ 4 ]

0 голосов
/ 27 августа 2018

Я согласен с Дейвом, что возвращение назад - это эффективный подход. Если допустимы только положительные координаты, то каждая координата имеет не более одного действительного родителя. Это позволяет вам работать в обратном направлении без комбинаторного взрыва.

Вот пример реализации:

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):

  • (a, b) достижимо из (1,1) тогда и только тогда, когда a и b взаимно просты.
  • Если a и b имеют общий фактор, то у всех потомков (a, b) также есть этот общий фактор. Эквивалентно, если существует путь от (a, b) до (c, d), то существует также путь от (n * a, n * b) до (n * c, n * d) для любого положительного целое число n.
  • если a и b взаимно просты и не являются (1,1), то существует бесконечно много взаимно простых координат, которые недоступны из (a, b). Выбирая (a, b) в качестве отправной точки, вы фактически ограничиваете себя какой-то одной ветвью дерева, образованного (1,1). Вы никогда не сможете достичь ни одной из родственных ветвей (a, b), где находится бесконечно много координат.
0 голосов
/ 27 августа 2018

Рекурсивная функция должна хорошо работать для этого. Вы даже получили количество возможностей.

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))
0 голосов
/ 27 августа 2018

Вернитесь назад. Я предполагаю, что начальные координаты положительные. Скажем, вы хотите знать, совместима ли начальная точка (a, b) с конечной точкой (x, y). На шаг назад от (x, y) вы были либо (x-y, y), либо (x, y-x). Если x> y, выберите первое, в противном случае выберите второе.

0 голосов
/ 27 августа 2018

Наивный грубый подход

Одним из способов будет рекурсивное исследование каждого возможного хода, пока вы не достигнете цели.

Следует учесть, что робот может продолжать двигаться бесконечно (никогда не достигая цели), поэтому вам нужен конечный случай, чтобы функция завершилась. К счастью, положение всегда увеличивается по оси 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

Ограничение состоит в том, что это не работает для отрицательных координат - не уверен, является ли это требованием, просто дайте мне знать, если это так, и я адаптирую ответ.


Bactracking

С другой стороны, если отрицательные координаты недопустимы, мы также можем подойти к этому, как Дейв предлагает . Это гораздо эффективнее, поскольку осознание того, что робот может добраться до каждой координаты одним-единственным способом.

Метод основан на том, что мы можем определить, каким путем мы шагнули: либо увеличить 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-координата, вычтите y-координату из x-координаты, в противном случае вычтите x-координату из y-координаты.

То есть для координаты (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

Итак, вы можете видеть, что бэктрекер почти в три раза быстрее в обоих случаях.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...