Алгоритм поиска наиболее эффективных ходов для достижения заданной точки
предположим, что у меня есть набор точек в элементе n-мерном пространстве. Используя 3 измерения, например:
A : [1,2,3]
B : [4,5,6]
C : [7,8,9]
у меня также есть набор векторов, которые описывают возможные движения в этом пространстве:
V1 : [+1,0,-1]
V2 : [+2,0,0]
теперь, учитывая точку dest, мне нужно найти отправную точку p и набор векторов движется это приведет меня к dest наиболее эффективным образом. Эффективность определяется как "наименьшее количество ходов", не обязательно" наименьшее линейное расстояние": допустимо выбрать p что дальше dest чем другие кандидаты, если набор ходов таков, что вы можете попасть туда за меньшее количество ходов. Векторы в движется должен быть строгим подмножеством доступных векторов; вы не можете используйте один и тот же вектор несколько раз, если он не появляется более одного раза во входном наборе.
мой вход содержит ~100 начальных точек и, возможно, ~10 векторов, а мое количество измерений - ~20. Начальные точки и доступные векторы будут фиксированы на время жизни приложения, но я буду находить пути для многих, многих разных dest очков. Я хочу оптимизировать скорость, а не память. Допустимо, чтобы алгоритм потерпел неудачу (чтобы не найти возможных путей к dest).
обновление с принятым решением
Я принял решение, очень похожее на то, которое отмечено ниже как "принято". Я перебираю все точки и векторы и строю список всех достижимых точек с маршрутами для их достижения. Я преобразую этот список в хэш dest, п+векторов>, выбор кратчайшего набора векторов для каждой точки назначения. (Существует также немного оптимизации для размера хэша, который не является здесь уместно.) Последующие dest поиск происходит в постоянное время.
6 ответов
на самом деле, учитывая, что у вас около 10 векторов, вы можете, для данного dest точка, рассчитывать только 1024 "цели" из подмножества векторов -- e.g каждое доступное пространство с информацией о том, какой набор ходов туда попадает. Это может быть "медленный" или "быстрый" в зависимости от контекста (это будет абсурдно быстро, если реализовано на параллельном вычислительном устройстве, таком как GPU).
имея все наборы, которые туда попадают, вы можете рассчитать пути a намного быстрее, то вы можете выбрать точку, из которой добраться до dest в наименьшем количестве ходов, выбирая из подмножества тех, которые являются вашим запросом или далее.
(спасибо Strilanc)
Я считаю, что вы сможете сделать обобщенное применение алгоритма поиска пути A* (он же звезда). Нет причин, почему это нельзя сделать в N-М пространстве. Это наиболее оптимальный путь, если можно описать стоимость каждого хода.
таким образом, вы хотите найти подмножество вашего набора векторов, чтобы подмножество суммировалось с заданным значением. В 1 измерении это называется проблема подмножество сумме, и является NP-полной.
к счастью, у вас есть только ~10 векторов, поэтому ваш размер проблемы на самом деле довольно мал и сговорчив. Начните с того, что просто попробуйте все комбинации 2^10 для каждой начальной точки и выберите лучшую. Затем работайте оттуда, ища простые оптимизации.
легкие оптимизация, которая может работать:
- приоритет поиска подмножеств, включая векторы, направленные в правильном направлении.
- встреча-в-середине. Используйте хэш-таблицу для хранения всех точек, доступных с помощью подмножеств первой половины Вашего набора ходов, и посмотрите, можете ли вы поразить любую из них, используя вторую половину вашего набора ходов, начиная с конца.
- вернуться назад. У вас есть только одна конечная точка, поэтому хэшируйте все достижимые начальные точки оттуда, а затем проверьте все возможные начальная точка.
- параллелизм
учитывая, что у вас есть начальные точки и фиксированный набор векторов, вы можете рассчитать список всех достижимых пунктов назначения, а затем просто найти заданный пункт назначения?
Как утверждает Корнель, у вас есть не более 2^10 = 1024 достижимых пункта назначения. Таким образом, вы можете просто генерировать все достижимые назначения за 2^N раз (где N-количество векторов) простым рекурсивным поколением. Это будет достаточно быстро, конечно. Тем не менее, предположим, что вы хотели растянуть его.
вы можете оптимизировать его до времени O(2^(N/2+1)), используя решение meet-in-the-middle. Вы разделяете набор векторов на два подмножества и генерируете все достижимые назначения для каждого подмножество независимо. Затем выполните итерацию по одному набору доступных мест назначения и для каждого местоположения найдите разницу между ним и целевым назначением. Если вектор находится в другом наборе абонентов, у вас есть решение: объединить два и готово. Трудность здесь в эффективном запросе, если у вас есть необходимый вектор в другом наборе: это можно сделать за O(1) время с помощью хэш-таблицы.
Это O(2^(N/2)) Время на подмножество, умноженное на два подмножества дают O(2^(N/2+1)). Чтобы присоединиться к двум, Это O(2^(N/2)) Время. Таким образом, это дает нам O(2^(N/2+1)) Время в целом.
- начните с самого начала.
- ничего
- получить расстояние до места назначения
- проверьте все возможные ходы и выберите тот, который приближает вас к месту назначения за один ход.
- end do
это может колебаться вокруг пункта назначения,но это приблизит вас.