Функция занимает много времени

В настоящее время я работаю над попыткой получить количество уникальных путей от узла 1 .. N максимальной длины для взвешенного ориентированного ациклического графа, я работал над получением максимальной длины, но я застрял на получении ЧИСЛА путей этого заданного максимальная длина...

Данные вводятся так:

91 120  # Number of nodes, number of edges

1 2 34

1 3 15

2 4 10

.... Поскольку Узел 1-> Узел 2 с весом 34,

I input my data using a diction so my dict looks like:
_distance = {}
_distance = {1: [(2, 34), (3, 15)], 2: [(4, 10)], 3: [(4, 17)], 4: [(5, 36), (6, 22)], 5: [(7, 8)],...ect

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

сначала я составляю список вершин

class Vertice:
    def __init__(self,name,weight=0,visted=False):
        self._n = name
        self._w = weight
        self._visited = visted
        self.pathTo

for i in range(numberOfNodes): # List of vertices (0-n-1)
  _V = Vertice(i) 
  _nodes.append(_V)

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

        for vert, neighbors in _distance.iteritems():
        _vert = _nodes[vert-1] # Current vertice array starts at 0, so n-1


        for x,y in neighbors:  # neighbores,y = weight of neighbors
            _v = _nodes[x-1]   # Node #1 will be will be array[0]

            if _v._visited == True:
                if _v._w > _vert._w+y:
                    _v._w = _v._w
                else:
                    _v._w = y + _vert._w

            else:

                _v._w = y + _vert._w
                _v._visited = True

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

max = _nodes[-1]._w

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

#  Start from first node in dictionary, distances is our dict{}
#  Target is the last node in the list of nodes, or the total number of nodes.
numLongestPaths(currentLocation=1,target=_numNodes,distances=_distance,maxlength=max)

def numLongestPaths(currentLocation,maxlength, target, sum=0, distances={}):


    _count = 0

    if currentLocation == target:
        if sum == maxlength:
                _count += 1

    else:
        for vert, weight in distances[currentLocation]:
            newSum = sum + weight
            currentLocation = vert
            _count += numLongestPaths(currentLocation,maxlength,target,newSum,distances)

    return _count

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

Это работает мгновенно для входов, таких как 8 узлов, и самый длинный путь - 20, находит 3 пути, и для входов, таких как 100 узлов, самая длинная длина 149 и только 1 уникальный путь этой длины, но когда я пытаюсь сделать набор данных с 91 узлом, таким как самый длинный путь 1338 и количество уникальных путей 32, функция занимает очень ДЛИННЕЕ, она работает, но очень медленно.

Может ли кто-нибудь дать мне несколько советов о том, что не так с моей функцией, из-за чего так долго нужно искать # путей длиной X от 1 до N? Я предполагаю, что у него экспоненциальное время работы, но я не уверен, как это исправить

Спасибо за помощь!

РЕДАКТИРОВАТЬ: Хорошо, я слишком много думал об этом и делал это неправильно, я реструктурировал свой подход, и теперь мой код выглядит следующим образом:

# BEGIN SEARCH.
    for vert, neighbors in _distance.iteritems():
        _vert = _nodes[vert-1] # Current vertice array starts at 0, so n-1


        for x,y in neighbors:  # neighbores

            _v = _nodes[x-1]   # Node #1 will be will be array[0]

            if _v._visited == True:
                if _v._w > _vert._w+y:
                    _v._w = _v._w
                elif _v._w == _vert._w+y:
                        _v.pathsTo += _vert.pathsTo
                else:
                    _v.pathsTo = _vert.pathsTo
                    _v._w = y + _vert._w

            else:

                _v._w = y + _vert._w
                _v.pathsTo = max(_vert.pathsTo, _v.pathsTo + 1)
                _v._visited = True

Я добавил переменную pathsTo в свой класс Vertice, и она будет содержать количество уникальных путей максимальной длины


person Liverlips    schedule 26.01.2018    source источник
comment
Размещение вашего вопроса на cs.stackexchange.com даст более интересные ответы.   -  person Barney    schedule 26.01.2018
comment
Перекрестно размещено: stackoverflow.com/q/48454870/781723, cs.stackexchange.com/q/87343/755. Пожалуйста, не размещайте один и тот же вопрос на нескольких сайтах. У каждого сообщества должен быть честный шанс ответить, не тратя время зря.   -  person D.W.    schedule 27.01.2018
comment
@Barney, у меня есть просьба. В будущем, если вы собираетесь предлагать другой сайт, не могли бы вы сообщить об этом постеру, чтобы он не публиковал перекрестные сообщения? Вы можете предложить им удалить вопрос здесь, прежде чем размещать его где-то еще, и напомнить им, что им нужно адаптировать вопрос к конкретному сайту. Это обеспечит лучший опыт для всех. Кроме того, я вижу, что вы оставили свой комментарий после того, как на этот вопрос уже был дан здесь один ответ. Я не думаю, что вам следует предлагать другой сайт после того, как здесь уже был дан ответ. (продолжение)   -  person D.W.    schedule 27.01.2018
comment
Наконец, пожалуйста, не предлагайте другой сайт, если вы хорошо не знаете его масштабы. Вопросы по кодированию на CS.SE не по теме, а вопросы о конкретных фрагментах кода там не по теме. Первое, что мы просим людей сделать, - это переписать свой вопрос, чтобы описать свой алгоритм (например, с помощью краткого псевдокода) вместо того, чтобы показывать код. Я не вижу, чтобы у вас был аккаунт на CS.SE. Возможно, лучше не предлагать сайты, на которых вы не активны, поскольку маловероятно, что вы будете знать, какие вопросы будут там хорошо приняты, если вы сами на них не активны. Спасибо за то, что вы слушали!   -  person D.W.    schedule 27.01.2018
comment
Извините за этот кросс-пост, я не знаю, как его удалить, поскольку разместил в качестве гостя на cs.exchange   -  person Liverlips    schedule 27.01.2018


Ответы (2)


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

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

person user2357112 supports Monica    schedule 26.01.2018
comment
спасибо за ответ, что, если сказать: Узел 7 указывает на Узел 8 и Узел 9, каждый из которых имеет вес 1, а затем Узел 8 и Узел 9 указывают на один и тот же Узел 10, каждый с одинаковым весом, если я перестану искать после узла 7- ›8-› 10 пути, не пропущу ли я путь 7- ›9-› 10, который также имеет такой же вес? так что мне нужно будет вычислять узел более одного раза? - person Liverlips; 26.01.2018
comment
@Liverlips: Продолжай думать об этом. Вам нужно научиться разобраться в этом самостоятельно; Я не хочу рассказывать вам все. - person user2357112 supports Monica; 26.01.2018
comment
привет @ user2357112 Я думал об этом весь день и вчера и прочитал еще несколько подходов к этому, я обновил свой пост своим новым кодом, который, кажется, отлично работает, но мой словарь еще не в топологическом порядке? так как сейчас он работает со всеми моими тестовыми примерами - person Liverlips; 27.01.2018

В дополнение к ответу @ user2357112 вот две дополнительные рекомендации

Язык

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

Структура данных

Узлы именуются в упорядоченном виде, поэтому вы можете значительно оптимизировать свой код, используя список вместо словаря. т.е.

_distance = [[] for i in range(_length)]
person AdminXVII    schedule 26.01.2018