Теория графов.Оргдеревья., Разбиение оргдеревьев. |
1. Заголовок темы должен быть информативным. В противном случае тема удаляется ...
2. Все тексты программ должны помещаться в теги [code=pas] ... [/code], либо быть опубликованы на нашем PasteBin в режиме вечного хранения.
3. Прежде чем задавать вопрос, см. "FAQ", если там не нашли ответа, воспользуйтесь ПОИСКОМ, возможно такую задачу уже решали!
4. Не предлагайте свои решения на других языках, кроме Паскаля (исключение - только с согласия модератора).
5. НЕ используйте форум для личного общения, все что не относится к обсуждению темы - на PM!
6. Одна тема - один вопрос (задача)
7. Проверяйте программы перед тем, как разместить их на форуме!!!
8. Спрашивайте и отвечайте четко и по существу!!!
Теория графов.Оргдеревья., Разбиение оргдеревьев. |
Fanat |
Сообщение
#1
|
Fanat Группа: Пользователи Сообщений: 261 Пол: Мужской Реальное имя: Сергей Репутация: 5 |
Как разбить ориентированное дерево на все неизоморфные поддеревья?..Входные данные-например,FO представление.
|
Michael_Rybak |
Сообщение
#2
|
Michael_Rybak Группа: Пользователи Сообщений: 1 046 Пол: Мужской Реальное имя: Michael_Rybak Репутация: 32 |
Идем от листов вверх к корню, и индексируем по ходу.
Лучше сразу на примере: Берем листья: F, G, H, I, K, L, M, N. Присваиваем каждому из них индекс 0. Между собой соответствующие поддеревья изоморфны, и других таких нет. Теперь рассматриваем узел, у которого все сыновья проиндексированы, а он - нет. Например E. Записываем индексы его сыновей: G(0), H(0); сортируем индексы: (0; 0). Смотрим, есть ли у нас такое поддерево среди рассмотренных? Нету. Значит, это - новое, даем ему индекс 1. Ведем такую табличку: 0: () 1: (0; 0) Берем следующий узел с проиндексированными сыновьями, например B. Записываем его детей: E(1), F(0); сортируем индексы: (0; 1). Такого еще не было, даем индекс 2: 0: () 1: (0; 0) 2: (0; 1) Дальше. Узел C. Дети: M(0), N(0). Сортируем индексы: (0; 0). Это у нас уже было. Это - индекс 1. Узел J. Дети: K(0), L(0). Сортируем индексы: (0; 0). Это у нас уже было. Это - индекс 1. Узел D. Дети: I(0), J(1). Сортируем индексы: (0; 1). Это у нас уже было. Это - индекс 2. Узел А. Дети: B(2), C(1), D(2). Сортируем индексы: (1; 2; 2). Такого еще не было, это будет индекс 3: 0: () 1: (0; 0) 2: (0; 1) 3: (1; 2; 2) Таким образом, в дереве есть 4 вида неизоморфных поддеревьев, и корням изоморных поддеревьев поставлены в соответствие одинаковые индексы. Чтобы делать это за n log n, можно строить префиксное дерево для списка полученных индексов (в котором, например, путь 1-2-2 оканчивается значением 3) |
Fanat |
Сообщение
#3
|
Fanat Группа: Пользователи Сообщений: 261 Пол: Мужской Реальное имя: Сергей Репутация: 5 |
Требуеться разбить на ВСЕ неизоморфные поддеревья.
Алгоритм: Нахождение и удаление висячих вершин. Проверка на изоморфность оргдеревьев. Только вот с реализациеё чето у меня не особо. Даже списковое представление сделать не получаеться. |
Michael_Rybak |
Сообщение
#4
|
Michael_Rybak Группа: Пользователи Сообщений: 1 046 Пол: Мужской Реальное имя: Michael_Rybak Репутация: 32 |
|
Fanat |
Сообщение
#5
|
Fanat Группа: Пользователи Сообщений: 261 Пол: Мужской Реальное имя: Сергей Репутация: 5 |
Например,рассмотрим такое огрдерево.
Дерево имеет 4 неизоморфных поддерева. Приведено их FO представление. Код program 1; uses crt; type ptr=^elem; elem=record num:integer; next:ptr; end; vector=array[1..100] of ptr; var an,k,ak:ptr; ch:integer; num,kol,nom:integer; f:text; s:char; i,n,j:integer; zap,zap1:vector; procedure del_list(var an:ptr); begin an:=nil; end; procedure add_begin(var an:ptr;num:integer); var k:ptr; begin new(k); k^.next:=an; k^.num:=num; an:=k; end; procedure add_end(var an:ptr;num:integer); var k:ptr; begin new(k); k^.next:=nil; k^.num:=num; an^.next:=k; end; procedure beg_list(var an:ptr;var k:ptr); begin k:=an; end; procedure end_list(var an:ptr;var k:ptr); begin k:=nil; end; procedure next(var k:ptr;var ch:integer); begin ch:=k^.next^.num; end; procedure take_beg(an:ptr;var ch:integer); begin ch:=an^.num; end; begin repeat clrscr; assign(f,'1.txt'); i:=1; reset(f); read(f,kol); for i:=1 to kol do begin del_list(zap[i]); add_begin(zap[i],i); beg_list(zap[i],an) end; i:=1; while not eof(f) do begin read(f,num); write(' ',num); if num=0 then i:=i+1 else add_end(zap[i],num); end; writeln; write(zap[1]^.num,' '); write(zap[1]^.next^.num,' '); writeln(zap[1]^.next^.next^.num); writeln; writeln('Xotite povtoprit? Y/N'); until readkey='n'; end. В этом коде создаем списковое представление дерева.Просьба подправить, так как оно создаётся неверно. Что-то со ссылками.Напиример в рассмотреном нами дереве результат будет 1 4 268 вместо должного 1 2 4. Сообщение отредактировано: Fanat - |
Michael_Rybak |
Сообщение
#6
|
Michael_Rybak Группа: Пользователи Сообщений: 1 046 Пол: Мужской Реальное имя: Michael_Rybak Репутация: 32 |
Приведи, пожалуйста, определения "FO представление" и "ориентированное дерево"
|
Fanat |
Сообщение
#7
|
Fanat Группа: Пользователи Сообщений: 261 Пол: Мужской Реальное имя: Сергей Репутация: 5 |
Ориентированное дерево — это ориентированный граф без циклов.(Так это понимает наш препод).
Массив FO формируется следующим образом: сначала записывается число вершин орграфа,затем номера вершин в любом порядке в которые заходят дуги из первой вершины,затем ставится разделитель 0.Далее записываются номера вершин, в которые заходят дуги из второй вершины,после чего ставиться разделитель и т.д. |
Michael_Rybak |
Сообщение
#8
|
Michael_Rybak Группа: Пользователи Сообщений: 1 046 Пол: Мужской Реальное имя: Michael_Rybak Репутация: 32 |
Я сейчас выхожу, подумаю над задачей в дороге. А ты пока напиши:
1) под поддеревом понимается любой подграф? 2) подробнее об предложенном преподом алгоритме. Что значит "удаление и сравнение". 3) тебе обязательно пользоваться именно списковым представлением? Если нет, я тебе напишу, как это намного проще реализовать |
Michael_Rybak |
Сообщение
#9
|
Michael_Rybak Группа: Пользователи Сообщений: 1 046 Пол: Мужской Реальное имя: Michael_Rybak Репутация: 32 |
И еще: "без циклов" = без ориентированных циклов? Или просто без циклов? Потому что если без ориентированных, от висячих вершин может и не оказаться
|
Fanat |
Сообщение
#10
|
Fanat Группа: Пользователи Сообщений: 261 Пол: Мужской Реальное имя: Сергей Репутация: 5 |
И еще: "без циклов" = без ориентированных циклов? Или просто без циклов? Потому что если без ориентированных, от висячих вершин может и не оказаться Просто без циклов. В понимании препода оргдерева это оргдерево в обычном понимании.Только вот из корня не обязательно достижимы все вершины.Да и корня в принципе мы не знаем.Поэтому можем подвесить граф за любую вершину. Под поддеревом понимаеться любой граф. Алгоритм: как представляю себе я, подвесим граф за 1 вершину. Найдём висячии. Рассмотрим всевозможные комбинации удаляя их и проверяя на изоморфность и неизоморфные запишем в файл. Файлы сортируем по кол-ву вершин. И так далее. В конце необходимо подвесить за следующую вершину. (Очень долго =( ) Списковым представлением пользоваться не обязательно. Просто входные данные могут быть очень большими (около 10000 вершин , а то и больше),а это дастаточно экономный вариант хранения графа. Не откажусь от любой помощи. |
Michael_Rybak |
Сообщение
#11
|
Michael_Rybak Группа: Пользователи Сообщений: 1 046 Пол: Мужской Реальное имя: Michael_Rybak Репутация: 32 |
Алгоритм: как представляю себе я, подвесим граф за 1 вершину. Найдём висячии. Рассмотрим всевозможные комбинации удаляя их и проверяя на изоморфность и неизоморфные запишем в файл. Файлы сортируем по кол-ву вершин. И так далее. В конце необходимо подвесить за следующую вершину. (Очень долго =( ) Я не понял, что ты имеешь ввиду под "Рассмотрим всевозможные комбинации удаляя их и проверяя на изоморфность и неизоморфные запишем в файл". Как именно мы это делаем? Сначала строим все возможные поддеревья, содержащие корень, а потом для каджой пары из них проверяем, не изоморфны ли они? Или как-то по ходу отсеиваем изоморфные? В общем вот что я тебе скажу. Лобовой алгоритм именно такой: подвесить за первую вершину, перебрать все возможные варианты последовательного отсечения части висячих вершин, получив таким образом все поддеревья, обязательно содержащие первую вершину. Потом подвесить за вторую вершину, и сделать то же самое. И так далее. Затем взять всю эту кучу поддеревьев, и попарно сравнивать их, находя изоморфные. Теперь, как это можно ускорить: 1) после того, как мы уже подвешивали за первую вершину, нам больше нет смысла рассматривать опять те поддеревья (но подвешенные за другую вершину), которым она принадлежит. Поэтому мы ее удаляем со всеми инцидентными ребрами. Подвешиваем за вторую, удаляем ее тоже, и т.д. 2) существенное ускорение можно получить неким подобием хеширования. Для каждого поддерева можно попридумывать несколько числовых характеристик, не зависящих от нумерации - количество вершин с нечетной степенью, количество ребер, соединяющих вершины одинаковой степени и т.п. И проверять на изоморфность только те пары поддеревьев, у которых все эти характеристики совпадают 3) можно воспользоваться приемом, который я тебе описал в самом первом посте. Подвесив за какую-нибудь вершину, будем вот так индексировать (систему индексирования нужно немного подправить, чтобы она учитывала направление ребра). Тогда можно будет значительную часть изоморфных поддеревьев отсечь на ходу. НО. Я очень сомневаюсь, что имеет смысл особо париться. Пункты 1 и 2, конечно, надо сделать, а вот пункт третий - с ним мороки побольше, а в худшем случае он все равно не поможет. Дело в том, что для некоторых входных данных количество неизоморфных поддеревьев будет порядка O(2^n), и никакие оптимизации тут не спасут при n = 10000, и даже при n = 50 Цитата Я так понял, что Списковым представлением пользоваться не обязательно. Просто входные данные могут быть очень большими (около 10000 вершин , а то и больше),а это дастаточно экономный вариант хранения графа. Еще меньше памяти занимает обычный список ребер. Вот: type TEdge = record x, y: longint end; Проходишь слева направо по FO представлению, и заполняешь edges в отсортированном порядке парами (откуда-куда). Отсортированном по "откуда", т.е. сначала идут все ребра, исходящие из 1, потом - из 2 и т.д (фактически, просто выбрасываешь нолики из FO). По ходу заполняешь first и last; first[i] - начало подсписка для вершины i, т.е. такое наименьшее t, что edges[t].x = i; а last[i] - конец подсписка для вершины i, т.е такое наименьшее w>t, что edges[w].x <> i. Теперь, чтобы пробежаться по вершинам, достижимым из i, надо пробежаться от edges[first[i]] до edges[last[i]-1]. Очень удобно и экономно. Поскольку здесь нам не помешают и обратные связи (от сына к отцу), ребра можно продублировать (a->b порождает b->a), отсортировать список, и уже потом пройтись по нему, заполняя first и last. Цитата Не откажусь от любой помощи. Пробуй, приходи, будем советоваться. Сообщение отредактировано: Michael_Rybak - |
Fanat |
Сообщение
#12
|
Fanat Группа: Пользователи Сообщений: 261 Пол: Мужской Реальное имя: Сергей Репутация: 5 |
Можешь на примере показать что мы храним в массивах first i last?..Почему просто не хранить список рёбер в двух массивах?..
|
Michael_Rybak |
Сообщение
#13
|
Michael_Rybak Группа: Пользователи Сообщений: 1 046 Пол: Мужской Реальное имя: Michael_Rybak Репутация: 32 |
Можешь на примере показать что мы храним в массивах first i last?.. Для твоего примера: FO представление: 4240020 Массив edges: {(1, 2), (1, 4), (3, 2)} Массив first: {1, -1, 3, -1} Массив last: {3, -1, 4, -1} first[1] = 1, last[1] = 3; это значит, что ребра, исходящие из вершины 1, начинаются в edges[1], и кончаются в edges[3] (не включительно) first[2] = -1, last[2] = -1; это значит, что ребер, исходящих из вершины 2, нет first[3] = 3, last[3] = 4; это значит, что ребра, исходящие из вершины 3, начинаются в edges[3], и кончаются в edges[4] (не включительно) first[4] = -1, last[4] = -1; это значит, что ребер, исходящих из вершины 4, нет Фактически, first и last - это указатели на начало и конец подсписка ребер для данной вершины. Теперь, чтобы пробежаться по ребрам, исходящим из вершины i, пишем: Writeln('Список вершин, достижимых по ребру из ', i, ':'); Цитата Почему просто не хранить список рёбер в двух массивах?.. Что значит список ребер в двух массивах? Как это? Хранить можно как угодно; я всегда пользуюсь именно таким представлением, которое описал. Убедился, что так - удобнее всего, по крайней мере для меня. |
Маня |
Сообщение
#14
|
|||
Группа: Пользователи Сообщений: 9 Пол: Женский Репутация: 1 |
Такая вот задача:
требуется определить изоморфны ли два ориентированных дерева или нет. Входные данные: я задаю два уже ориентированных дерева в FO-представлении. Помогите,пожалуйста,(сравнить) определить алгоритм изоморфизма этих графов!
-------------------- Потому что, потому что
Всех нужнее и дороже, Всех доверчивей и строже В этом мире доброта В этом мире ДОБРОТА |
|||
Michael_Rybak |
Сообщение
#15
|
Michael_Rybak Группа: Пользователи Сообщений: 1 046 Пол: Мужской Реальное имя: Michael_Rybak Репутация: 32 |
Прочти мой первый пост здесь.
Алгоритм, который я там описываю, очень похож на то, что тебе нужно. Сначала придумай, как его переделать для проверки двух ациклических *неориентированных* графов на изоморфность. Потом придумай, как сравнивать ориентированные деревья. |
Fanat |
Сообщение
#16
|
Fanat Группа: Пользователи Сообщений: 261 Пол: Мужской Реальное имя: Сергей Репутация: 5 |
Сколько думал, а сделать ничё не могу.....
|
Michael_Rybak |
Сообщение
#17
|
Michael_Rybak Группа: Пользователи Сообщений: 1 046 Пол: Мужской Реальное имя: Michael_Rybak Репутация: 32 |
Давай шаг за шагом.
1. Напиши программу, которая читает FO представление, и сохраняет информацию о ребрах так, чтобы можно было удобно бегать по ребрам, исходящим из данной вершины. Программа должна содержать процедуру PrintEdges(i: longint), печатающую ребра, входящие в вершину i, и ребра, исходящие из нее. Итак, на вход программа получает FO представление, на выходе - что-то типа такого: Код Вершина 1 Исходящие ребра: 1-2 1-3 1-6 Входящие ребра: 4-1 Вершина 2 Исходящие ребра: 2-3 Входящие ребра: 1-2 Вершина 3 Исходящие ребра: 3-5 3-6 Входящие ребра: 1-3 2-3 ... 2. Теперь напиши программу, которая выдаст список *всех* возможных поддеревьев данного графа. Делаем это так. Упорядочим все ребра (пронумеруем). Сначала построим список всех поддеревьев, содержащих 0 ребер. Т.е. просто берем все вершины, каждую из них обзываем поддеревом. Теперь построим все поддеревья, использующие только первое ребро. Для этого надо попытаться к каждому из его концов прикрепить одно из уже построеных поддеревьев. Теперь построим все поддеревья, использующие только первых два ребра (но не обязательно оба). Для этого надо попытаться к каждому из концов второго ребра прикрепить одно из уже построеных поддеревьев. Теперь построим все поддеревья, использующие только первых три ребра (но не обязательно каждое). Для этого надо попытаться к каждому из концов третьего ребра прикрепить одно из уже построеных поддеревьев. И так далее. Давай на твоем примере. FO = 4240020 Пронумеруем ребра (как угодно) Ребро 1: 1->2 Ребро 2: 3->2 Ребро 3: 1->4 Поддеревья будем описывать так: [(список_вершин), (список_ребер)] Поддеревья, содержащие 0 ребер: {[(1), ()], [(2), ()], [(3), ()], [(4), ()]} Теперь попробуем использовать ребро 1, т.е. ребро 1->2. Нам надо из уже построенного списка поддеревьев {[(1), ()], [(2), ()], [(3), ()], [(4), ()]} присоединить что-то к 1, а что-то к 2. В данном случае единственный результат - это [(1, 2), (1->2)]. Получили список подграфов, в котором используется только первое ребро: {[(1), ()], [(2), ()], [(3), ()], [(4), ()], [(1, 2), (1->2)]} Следующее ребро: 3->2. Пробуем присоединить. К тройке можно присоединить только [(3), ()], а к двойке - либо [(2), ()], либо [(1, 2), (1->2)]. Получаем еще два новых поддерева: [(2, 3), (3->2)] и [(1, 2, 3), (1->2, 3->2)]. Общий список: {[(1), ()], [(2), ()], [(3), ()], [(4), ()], [(1, 2), (1->2)], [(2, 3), (3->2)], [(1, 2, 3), (1->2, 3->2)]} И последнее ребро: 1->4. К четверке можно присоединить только [(4), ()], а к единице уже кучу всего: [(1), ()], [(1, 2), (1->2)], [(1, 2, 3), (1->2, 3->2)]. Получаем новых 3 поддерева. Общий список выглядит так: {[(1), ()], [(2), ()], [(3), ()], [(4), ()], [(1, 2), (1->2)], [(2, 3), (3->2)], [(1, 2, 3), (1->2, 3->2)], [(1, 4), (1->4)], [(1, 2, 4), (1->2, 1->4)], [(1, 2, 3, 4), (1->2, 3->2, 1->4)]} Вот мы и построили все связные подграфы. В общем случае, пытаясь использовать новое ребро x->y, мы бежим по списку уже построенных поддеревьев, и выбираем содержащие вершину x; пусть нашлось n вариантов. Потом бежим еще раз, и выбираем содержащие вершину y; пусть нашлось m вариантов. Тогда для каждого из n вариантов пробуем каждый из m вариантов, и получаем nm вариантов использования нового ребра. Что не понятно, спрашивай. |
Маня |
Сообщение
#18
|
Группа: Пользователи Сообщений: 9 Пол: Женский Репутация: 1 |
Послушайте! А если я сделаю так?
Как всё это на паскаде организовать??? Задача: требуется определить изоморфны ли два ориентированных дерева или нет Входные данные : FO-представление графа Выходные данные: либо ориентированные деревья изоморфны/не изоморфны либо граф не является ориентированным (неправильно заданы входные данные) Алгоритм: Будем обозначать направленное ребро через единичку, находящуюся на пересечении двух вершин в матрице смежности. Причём если стрелка ребра направлена только в одну сторону, то ставим единичку на пересечении вершины, откуда она отходит, с вершиной, куда она входит. Т.к. по определению дерево, это, грубо сказать, связный граф без циклов, то будем воспринимать двунаправленное ребро, за простое ребро. Пример. Матрица смежности: 0100000 1001000 0001000 0010100 0001010 0000101 0000000 1. С помощью матрицы смежности делаем проверку на правильность ввода, т.е. проверяем: является ли граф связным не имеет ли он циклов. 3. Очевидно, что деревья с разным количеством вершин не могут быть изоморфны, следовательно, делаем проверку (создаём процедуру) на одинаковое количество вершин в двух входных ориентированных графах. 4.Фактически, графы будут изоморфны тогда и только тогда, когда из матрицы смежности одного графа можно получить матрицу смежности другого графа перестановкой (перенумерацией) вершин. Т.е. для нашего примера: 1234567 1 0100000 2 1001000 3 0001000 4 0010100 5 0001010 6 0000101 7 0000000 Получим 1435672 1 0000001 4 0011000 3 0100000 5 0100100 6 0001010 7 0000000 2 1100000 Т.е. два ориентированных дерева G1 и G2 изоморфны G1 G2 Причём G2 получен путём перестановок вершин в матрице смежности. Таким образом, если мы имеем два графа (с одинаковым количеством вершин), заданных матрицами инцидентности. Достаточно перебрать все возможные перестановки вершин второго графа (разумеется, при каждой перестановке соответствующим способом меняя матрицу) - если ни в одном случае матрицы не совпадут - значит графы неизоморфны. Оптимизация: Поскольку сравнивать придётся матрицы n*n, (где n-количество вершин), а всего перестановок n!, то разумно будет сократить количество тех перестановок, которые имеют смысл. Возьмём граф и разобьем его вершины на группы по парам (In,Out), где In - количество входящих рёбер,т.е. количество единиц в соответствующем столбце, а Out – количество исходящих рёбер, т.е. количество единиц в соответствующей строке. Для указанного примера, имеем: 1 (1,1) 2 (1,2) 3 (1,1) 4 (3,2) 5 (2,2) 6 (1,2) 7 (1,1) Получаем 4 группы вершин: {1,3,7}{2,6}{4}{5} Так вот, вместо 7! = 1*2*3*4*5*6*7 = 5040 перестановок достаточно просто отсортировать вершины в обоих графах по соответствующим парам (например, сначала по In, потом по Out) и разумных перестановок будет только 3!*2!*1!*1!=1*1*1*2*3*1*2=12(т.е. произведение факториалов мощностей групп вершин) -------------------- Потому что, потому что
Всех нужнее и дороже, Всех доверчивей и строже В этом мире доброта В этом мире ДОБРОТА |
Michael_Rybak |
Сообщение
#19
|
Michael_Rybak Группа: Пользователи Сообщений: 1 046 Пол: Мужской Реальное имя: Michael_Rybak Репутация: 32 |
Эвристики тут ни к чему. Сравнить два графа можно *всегда* очень быстро, за полиномиальное время. Никаких n!. Читай мой пост и задавай вопросы.
|
Маня |
Сообщение
#20
|
Группа: Пользователи Сообщений: 9 Пол: Женский Репутация: 1 |
Там все связано с разбиением деревьев на поддеревья...а мне это не нужно!
-------------------- Потому что, потому что
Всех нужнее и дороже, Всех доверчивей и строже В этом мире доброта В этом мире ДОБРОТА |
Текстовая версия | 22.12.2024 18:35 |