Гамильтоновы графы — различия между версиями
SergeyBud (обсуждение | вклад) (→Основные определения) |
(→Алгоритм нахождения гамильтового цикла) |
||
| Строка 90: | Строка 90: | ||
Приведём два алгоритма поиска гамильтонова цикла. | Приведём два алгоритма поиска гамильтонова цикла. | ||
| − | bool check_hamiltonian(graph g, bool[] used, int vert, int count, int[] next): | + | '''bool''' check_hamiltonian(graph g, '''bool'''[] used, '''int''' vert, '''int''' count, '''int'''[] next): |
| − | if (count == g.vertices) | + | '''if''' (count == g.vertices) |
next[vert] = 0 | next[vert] = 0 | ||
| − | return (vert; 0) in g.edges | + | '''return''' (vert; 0) '''in''' g.edges |
| − | for | + | '''for''' i = 0 '''to''' g.vertices |
| − | if (!used[i] && (vert; i) in g.edges) | + | '''if''' (!used[i] && (vert; i) in g.edges) |
used[i] = true | used[i] = true | ||
next[vert] = i | next[vert] = i | ||
| − | if (check_hamiltonian(g, used, i, count + 1, next)) | + | '''if''' (check_hamiltonian(g, used, i, count + 1, next)) |
| − | return true | + | '''return''' true |
used[i] = false | used[i] = false | ||
| − | return false | + | '''return''' false |
* used — отметки о посещении | * used — отметки о посещении | ||
| Строка 111: | Строка 111: | ||
Приведём алгоритм, основанный на динамическом программировании, который работает значительно быстрее. Алгоритм основан на следующей идее: будем для каждой пары из подмножества вершин и вершины считать, существует ли гамильтонов путь для этого подмножества вершин, заканчивающихся в выделенной вершине. Суммарно таких состояний будет <tex>O(n2^n)</tex>, для обсчёта каждого из них требуется <tex>O(n)</tex> времени, то есть, суммарно алгоритм работает за <tex>O(n^22^n)</tex> времени. Псевдокод, реализующий этот алгоритм, приведён ниже: | Приведём алгоритм, основанный на динамическом программировании, который работает значительно быстрее. Алгоритм основан на следующей идее: будем для каждой пары из подмножества вершин и вершины считать, существует ли гамильтонов путь для этого подмножества вершин, заканчивающихся в выделенной вершине. Суммарно таких состояний будет <tex>O(n2^n)</tex>, для обсчёта каждого из них требуется <tex>O(n)</tex> времени, то есть, суммарно алгоритм работает за <tex>O(n^22^n)</tex> времени. Псевдокод, реализующий этот алгоритм, приведён ниже: | ||
| − | bool[][] get_dp_table(graph g): | + | '''bool'''[][] get_dp_table(graph g): |
| − | int n = g.vertices | + | '''int''' n = g.vertices |
| − | bool[][] result = new int[1 << n][n] | + | '''bool'''[][] result = new int[1 << n][n] |
| − | for | + | '''for''' i = 0 '''to''' n |
| − | result[1 << i][i] = (0; i) in g.edges; | + | '''result'''[1 << i][i] = (0; i) '''in''' g.edges; |
| − | for | + | '''for''' i = 1 '''to''' 1 << n |
| − | if (count(i) == 1) | + | '''if''' (count(i) == 1) |
| − | continue | + | '''continue''' |
| − | for | + | '''for''' j = 0 '''to''' n |
| − | if ((1 << j) & i != 0) | + | '''if''' ((1 << j) & i != 0) |
| − | for | + | '''for''' k = 0 '''to''' n |
| − | if (k != j && (1 << k) & i != 0) | + | '''if''' (k != j && (1 << k) & i != 0) |
| − | result[i][j] = result[(1 << j) ^ i][k] && (k; j) in g.edges | + | result[i][j] = result[(1 << j) ^ i][k] && (k; j) '''in''' g.edges |
| − | return result | + | '''return''' result |
В приведённом выше коде считаем, что n меньше количества бит в числовом типе данных, для операций над множествами используются побитовые логические операции в синтаксисе языка C. Функция count считает количество единичных бит в числе (она проста в реализации, но не относится к алгоритма, поэтому не приводится). Граф гамильтонов тогда, когда dp[(1 << n) - 1][i] && (i; 0) <tex>\in</tex> g.edges для некоторого i. | В приведённом выше коде считаем, что n меньше количества бит в числовом типе данных, для операций над множествами используются побитовые логические операции в синтаксисе языка C. Функция count считает количество единичных бит в числе (она проста в реализации, но не относится к алгоритма, поэтому не приводится). Граф гамильтонов тогда, когда dp[(1 << n) - 1][i] && (i; 0) <tex>\in</tex> g.edges для некоторого i. | ||
Версия 03:03, 30 декабря 2015
Содержание
Основные определения
| Определение: |
| Гамильтоновым путём (англ. Hamiltonian path) называется простой путь, приходящий через каждую вершину графа ровно один раз. |
| Определение: |
| Гамильтоновым циклом (англ. Hamiltonian cycle) называют замкнутый гамильтонов путь. |
| Определение: |
| Граф называется полугамильтоновым (англ. Semihamiltonian graph), если он содержит гамильтонов путь. |
| Определение: |
| Граф называется гамильтоновым (англ. Hamiltonian graph), если он содержит гамильтонов цикл. |
Очевидно, что любой гамильтонов граф также и полугамильтонов.
Достаточные условия гамильтоновости графа
Теорема Дирака
| Теорема: |
Если и для любой вершины неориентированного графа , то - гамильтонов граф. |
Теорема Оре
| Теорема: |
Если и для любых двух различных несмежных вершин и неориентированного графа , то - гамильтонов граф. |
Теорема Поша
| Теорема (Поша): |
Пусть граф имеет вершин и выполнены следующие два условия:
|
Теорема Редеи-Камиона
| Теорема: |
Любой сильносвязный турнир - гамильтонов. |
Теорема Гуйя-Ури
| Теорема (Ghouila-Houri): |
Пусть G - сильносвязный ориентированный граф. G - гамильтонов. |
Теорема Хватала
| Теорема (Хватал): |
Пусть:
Тогда если верна импликация: |
Алгоритм нахождения гамильтового цикла
Приведём два алгоритма поиска гамильтонова цикла.
bool check_hamiltonian(graph g, bool[] used, int vert, int count, int[] next):
if (count == g.vertices)
next[vert] = 0
return (vert; 0) in g.edges
for i = 0 to g.vertices
if (!used[i] && (vert; i) in g.edges)
used[i] = true
next[vert] = i
if (check_hamiltonian(g, used, i, count + 1, next))
return true
used[i] = false
return false
- used — отметки о посещении
- vert — текущая вершина
- count — количество посещённых вершин
Приведённая процедура работает следующим образом: перебираются всё рёбра из текущей вершины в ещё не посещённые. Чтобы проверить граф на гамильтоновость, необходимо запустить процедуру из вершины с номером 0 и параметром count = 1. Если процедура возвращает true, то в массиве next будет храниться следующая вершина на гамильтоновом цикле. Этот алгоритм в худшем случае перебирает путей, что даёт сложность работы .
Приведём алгоритм, основанный на динамическом программировании, который работает значительно быстрее. Алгоритм основан на следующей идее: будем для каждой пары из подмножества вершин и вершины считать, существует ли гамильтонов путь для этого подмножества вершин, заканчивающихся в выделенной вершине. Суммарно таких состояний будет , для обсчёта каждого из них требуется времени, то есть, суммарно алгоритм работает за времени. Псевдокод, реализующий этот алгоритм, приведён ниже:
bool[][] get_dp_table(graph g):
int n = g.vertices
bool[][] result = new int[1 << n][n]
for i = 0 to n
result[1 << i][i] = (0; i) in g.edges;
for i = 1 to 1 << n
if (count(i) == 1)
continue
for j = 0 to n
if ((1 << j) & i != 0)
for k = 0 to n
if (k != j && (1 << k) & i != 0)
result[i][j] = result[(1 << j) ^ i][k] && (k; j) in g.edges
return result
В приведённом выше коде считаем, что n меньше количества бит в числовом типе данных, для операций над множествами используются побитовые логические операции в синтаксисе языка C. Функция count считает количество единичных бит в числе (она проста в реализации, но не относится к алгоритма, поэтому не приводится). Граф гамильтонов тогда, когда dp[(1 << n) - 1][i] && (i; 0) g.edges для некоторого i.
Источники
- Харари Ф. Теория графов: Пер. с англ. / Предисл. В. П. Козырева; Под ред. Г.П.Гаврилова. Изд. 4-е. — М.: Книжный дом "ЛИБРОКОМ", 2009. — 60 с.
- Седжвик Р. Фундаментальные алгоритмы на C++. Алгоритмы на графах. — СПб: ООО «ДиаСофтЮП», 2002.
- Гамильтонов граф