Должен ли я перебирать ориентированный граф, используя итеративный поиск в глубину (IDDFS)?

Пример: у меня есть 20 человек в качестве объекта, и каждый человек знает 0-n других. Направление ссылки имеет значение! Человек А может знать Б, но Б может не знать А. Это ориентированный граф.

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

Так что в худшем случае все связаны со всеми, все друг друга знают. Это не реальный вариант использования, но я хочу написать тест для этого, чтобы учиться и играть. В производственной среде количество объектов будет ограничено примерно 20, но способы, которыми эти объекты связаны друг с другом, не ограничены.

Это иллюстрирует проблему в упрощенном виде: graphспасибо за источник

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

Представим, что человек А знает Б, который знает С и знает А. Результат может быть таким:

A знает B знает C знает A (хорошо, но мы не хотим заканчиваться бесконечным циклом, поэтому мы остановимся здесь) A знает C знает A A знает T знает R знает V

Это было бы глупо и должно быть устранено: А знает, Б знает, С знает, А знает, С знает, А знает, Т знает, Р знает, В...

У меня есть пара сумасшедших идей, как решить эту проблему. Но...

Вопрос) Должен ли я делать это с итеративным поиском в глубину (IDDFS)?


Джон был так любезен, что указал на DFS в Википедии

Я застрял в этой части статьи: wikipedia

поиск в глубину, начиная с A, предполагая, что левые ребра в показанном графе выбираются перед правыми ребрами, и предполагая, что поиск запоминает ранее посещенные узлы и не будет их повторять (поскольку это небольшой граф), посетит узлы в следующем порядке: A, B, D, F, E, C, G. Ребра, пройденные в этом поиске, образуют дерево Тремо, структуру с важными приложениями в теории графов.

конкретно это примечание:

"(поскольку это небольшой график)"

Хорошо, а что, если это огромный график?


person Proud Member    schedule 26.04.2011    source источник
comment
Если это огромный граф, это займет много времени, так как он будет исследовать крайний слева направо в дереве.   -  person deceleratedcaviar    schedule 26.04.2011
comment
В реальном случае это будет небольшой граф, в худшем случае может состоять из 20 узлов. Но я просто не хочу никаких ограничений в отношении разрешенных соединений.   -  person Proud Member    schedule 26.04.2011
comment
Заставьте это работать, затем сделайте это быстро.   -  person YXD    schedule 26.04.2011
comment
Да мне и сейчас не до скорости. Я устрою вечеринку с пончиками, когда это сработает.   -  person Proud Member    schedule 26.04.2011


Ответы (5)


Ваша структура данных действительно является графом.

Я ненавижу давать такой голый ответ, но вопрос настолько прост, что Graph Traversal в Википедии более чем адекватно. Объясняются два основных подхода, а также псевдокод.

person Jon    schedule 26.04.2011
comment
Вот что говорит об этом Википедия: Обход дерева — это частный случай обхода графа. В отличие от обхода дерева, при обычном обходе графа каждый узел, возможно, придется посетить более одного раза, а корневой узел, который соединяется со всеми другими узлами, может не существовать. ‹‹‹ Таким образом, в статье даже не затрагивается проблема обхода графа, когда узел приходится посещать несколько раз. - person Proud Member; 26.04.2011
comment
@BugAlert: Какое отношение к этому имеет обход дерева? Надеюсь, вы не нажали эту ссылку. Стратегии обхода графа определенно решают проблему обхода... графа. - person Jon; 26.04.2011
comment
На этой странице около 4 ссылок (не принимая во внимание хром интерфейса). Первый касается теории графов в целом. Второй про обход дерева. Третий о DFS, четвертый о BFS. Почему бы вам просто не дать правильную ссылку, когда вы ее там увидите :-)) - person Proud Member; 26.04.2011
comment
@BugAlert: Эта страница: en.wikipedia.org/wiki/Depth-first_search ( на который ссылается ссылка Graph Traversal) говорит само за себя, включая псевдокод. Я думаю, вам следует сначала ознакомиться с небольшой терминологией (граф, узел, вершина, ребро), и все будет в порядке. - person Jon; 26.04.2011

Изменить: я должен упомянуть, что название автора и вопрос изменились так сильно, что некоторая информация в этом ответе может быть не на 100% актуальной.

Как уже упоминал Джон, это действительно график. На самом деле ориентированный граф.

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

Списки смежности будут занимать больше места, но могут быть лучше в больших сетях, так как позволяют минимизировать вычисления на несвязанных узлах; тогда как матрицы смежности немного более удобны, но хранят данные для каждого ребра, независимо от того, существует ли оно (подключено) или нет.

Основная проблема, которую я обнаружил при использовании списков смежности, заключалась не в их теоретическом пространстве, а в C++, я сохранял каждый связанный узел как указатель в векторе внутри каждого узла; это могло выйти из-под контроля, как только сеть стала больше, и было очень неудобно визуализировать, а также управлять новыми узлами и удалять узлы. По сравнению с матрицами смежности, которые имеют единую ссылку для всех узлов (могут храниться в одном векторе узлов) и могут быть легко изменены.


Если ваш вопрос действительно касается обхода, то если ваш граф реализован как матрица смежности, как вектор векторов, обход прост. См. ниже псевдокод:

Чтобы прочитать (для каждого нейрона) все нейроны, к которым подключен аксон нейрона (т.е. выходы нейрона)

for (size_t i = 0; i < n; ++i) { // adjacency matrix is n * n
    Neuron& neuron = nodes[i];
    for (size_t j = 0; i < n; ++i) {
        Axon_connection& connection = edges[j][i];
        if (connection.exists()) {
            ...
        }
    }
}

Чтобы прочитать все (для каждого нейрона) нейроны, к которым подключены дендриты нейрона (т.е. входы нейрона)

for (size_t i = 0; i < n; ++i) { // adjacency matrix is n * n
    Neuron& neuron = nodes[i];
    for (size_t j = 0; i < n; ++i) {
        Dendrite& dendrite = edges[j][i];
        if (dendrite.exists()) {
            ...
        }
    }
}

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

person deceleratedcaviar    schedule 26.04.2011
comment
Я должен отметить, что я хочу использовать это на смартфоне :-), так что память имеет решающее значение. - person Proud Member; 26.04.2011
comment
Я обновил свой ответ, это должно быть очень удобно для памяти, если вы используете матрицы смежности. В моей собственной реализации я использую (n*24)+24 байта только для векторов, а затем n^2 байта для матрицы смежности. Предполагая, что вы храните стоимость соединения в виде числа с плавающей запятой на каждом ребре, это будет n^2 * sizeof(float). На смартфоне, предполагая, что вы используете минимальный объем памяти для структур данных, вы можете предположить: (20^2 * 4) + (20^2 * 1) = (400 * 4) + 400 = 2000 байт ‹ 2 КБ. - person deceleratedcaviar; 26.04.2011
comment
Выглядит хорошо, но я не использую матрицы смежности. Это намного превышает возможности моего мозга прямо сейчас. Каждый объект Person имеет массив, который связывает все другие известные объекты Person (и так далее). - person Proud Member; 26.04.2011
comment
По сути, это список смежности, и, без сомнения, обход становится полуторной задачей. Может быть, покажите нам, что у вас есть, и мы сможем указать вам правильное направление. - person deceleratedcaviar; 26.04.2011
comment
Цель-C. Код распространяется на другие мои вопросы, заданные ранее. Можете ли вы объяснить часть с управляющим массивом всех узлов? Это для отслеживания посещаемого состояния? - person Proud Member; 26.04.2011
comment
Нет, я не имею в виду DFS или BFS в своем ответе. Я не хотел запутать вас этим утверждением о массиве узлов и поэтому удалил его из своего комментария. - person deceleratedcaviar; 26.04.2011

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

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

Чтобы ответить на ваш вопрос, вы можете выполнить поиск в глубину и поиск в ширину на произвольном графе, как вы это делали ранее на дереве - вам просто нужно отслеживать, какие узлы вы посетили. Ищите это в любом коде/псевдокоде, с которым вы сталкиваетесь. Вам не нужно отслеживать посещенные заметки на дереве (как в других ваших вопросах), поскольку дерево - это особый экземпляр графа (связный ациклический граф), который не может быть «дико взаимосвязан».

person YXD    schedule 26.04.2011
comment
Я буду обновлять свой вопрос, когда пройду через это. Итак, когда я вспомню, какой узел был посещен, как мне справиться с узлами, которые можно посещать много раз из-за множества входящих и исходящих соединений из петель? - person Proud Member; 26.04.2011
comment
При первом посещении узла отметьте его как посещенный. Затем вы посещаете только детей/соседей, которых еще не посещали. - person YXD; 26.04.2011
comment
Представьте, что у вас есть группа друзей, и у каждого друга есть несколько друзей в адресной книге. Если вы хотите распространить какие-то новости в группе, вы должны начать с того, что обзвоните всех, кто есть в вашей адресной книге, и скажете: «Вот какие-то новости, расскажите всем своим друзьям». Когда каждый человек затем звонил своим друзьям, получатель говорил, что я уже слышал новости и передал их, спасибо. и не будет передавать сообщение во второй раз. - person YXD; 26.04.2011
comment
Это хорошее объяснение. Интересно, почему в этих статьях нельзя так писать. - person Proud Member; 26.04.2011

В ответ на ваш первоначальный вопрос, это определенно теоретически возможно решить. Однако, если вы ищете кратчайший путь, то это подозрительно похоже на задачу коммивояжера, которая NP-hard.

В любом случае существует множество различных алгоритмов обхода графа (DFS, IDDFS, BFS и т. д.), которые могут быть полезны.

person Flash    schedule 26.04.2011
comment
Кратчайший путь легко найти с помощью простого поиска в ширину. Однако вы правы в том, что указанная проблема приведет к огромному пространству поиска. - person btilly; 26.04.2011
comment
Я внедрил DFS и протестировал его с таким графиком. Он застревает с петлями. Так как циклические соединения не создаются, это нормально. Но иначе вылетает. - person Proud Member; 26.04.2011
comment
Если вы отметите узлы как посещенные, вы можете быть уверены, что посещаете только те узлы, которые раньше не видели. - person Flash; 26.04.2011
comment
Конечно, но я должен пройти все возможные пути. Узел можно посетить несколько раз. Я думаю о железных дорогах и стрелочных переводах. Придется построить физическую модель, чтобы поиграть с ней. - person Proud Member; 26.04.2011

Один из способов (и не обязательно лучший) сделать это — изменить граф.

Например, скажем, что граф изначально кодирует A-->B-->C. Если ребро A-->C не существует, добавьте ребро A-->C.

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

person David Weiser    schedule 26.04.2011
comment
Зачем мне добавлять ребро A--›C, если оно уже есть? Кроме того, у этих объектов Person уже есть массив, указывающий на каждого другого человека, которого они знают. - person Proud Member; 26.04.2011
comment
Извините за это, опечатка :) Насколько я понимаю, для пути A-->B-->C, A не сразу знает C, но должен сделать вывод, что он знает C. Подход, который я дал, просто говорит, как только вы сделаете вывод, что вы знаете кого-то, сделайте это знание явным на графике. - person David Weiser; 26.04.2011
comment
Теперь это имеет смысл. Спасибо! - person Proud Member; 26.04.2011