Представление графов в памяти компьютера (c++)

35
Представление графов в памяти компьютера (с примерами на C++) Максименкова Ольга Вениаминовна Старший преподаватель Департамента программной инженерии Факультета компьютерных наук (с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 1

Upload: olga-maksimenkova

Post on 18-Aug-2015

73 views

Category:

Education


2 download

TRANSCRIPT

Представление графов в памяти компьютера

(с примерами на C++)

Максименкова Ольга Вениаминовна

Старший преподаватель Департамента программной инженерии Факультета компьютерных наук

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 1

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 2

const int N = 3;const int M = 5;const int K = 7;int main() {

int multiArr[N][M][K] = { 1, 2, 3, 4, 5, 6, 7, 8 };for (int i = 0; i < N; i++)

for (int j = 0; j < M; j++)for (int k = 0; k < K; k++)

cout << multiArr[i][j][k] << " ";return 0;}

int arr[10]; // определён массив из 10 элементовfor (int i = 0;i < 10;i++)

cout << arr[i] << " ";return 0;

-858993460 -858993460 -858993460 -858993460 -858993460 -858993460 -858993460 -858993460 -858993460 -858993460

1 2 3 4 5 6 7 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Здесь есть инициализация!

А здесь нет!Если заменить arr[i] на (arr+i),

можно убедится, что адреса разные.

Но *(arr+i) даст такой же вывод.

Цели лекции

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 3

Рассмотреть

• Варианты представления графов в памяти компьютера

• Некоторые способы реализации этих представлений на

языке C++

Выявить

• Способы повышения быстродействия и экономии памяти

для частных случаев

Соглашения о терминологии

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 4

Мультиграф [multigraph] – граф, содержащий кратные рёбра.

Незнанов А.А., Кохов В.А. Алгоритмизация решения переборных задач анализа графов – М.: Издательский дом МЭИ,

2007. – 80 с.

Граф – неориентированный граф без петель и кратных рёбер.

Обозначение графа: G = (V, E), где идентификаторами (номерами) вершин и

ребер выступают числа натурального ряда:

V = {v1, v2, … , vp} = {0, 1, … , p1}, p = |V| – число вершин;

E = {e1, e2, … , eq} = {0, 1, … , q1}, q = |E| – число ребер.

Петля [loop] – ребро, инцидентное только одной вершине (соединяет вершину с

самой собой).

Кратными (или мультирёбрами) [multiple] называются рёбра, инцидентные

одной и той же паре вершин.

Две вершины смежны [adjacent], если они соединены ребром.

Два ребра смежны [adjacent], если они имеют общую вершину.

Базовые представленияИндексные представления графа/орграфа в памяти компьютера

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 5

Матрица смежности

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ

6

Матрица смежности графа – квадратная матрица A с числом строк и столбцов,

равным p. Элемент Ai j = 1, если существует дуга (i, j), в противном случае Ai j = 0. Для

мультиграфов вместо 1 заносится кратность мультиребра.

Объём памяти p2·a, где a – размер

элемента матрицы

int mtr[n][n] = { {0,1,0,0,1},{1,0,1,1,1},{0,1,0,1,0},{0,1,1,0,1},{1,1,0,1,0}

};

cout << n*n*sizeof(int);

0 1 0 0 1 1 0 1 1 1 0 1 0 1 0 0 1 1 0 1 1 1 0 1 0

12345

1 2 3 4 5

Для графов с весами на дугах, вместо 1 заносится вес ребра. Как перепишется

матрица смежности для примера выше?

5

1

2

34

1

7

23

4

5

6

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 7

5 2

34

4

7

12

3

5

6

9

8

0 1 0 0 1 1 0 2 0 1 0 2 0 2 0 0 0 2 0 2 1 1 0 2 0

12345

1 2 3 4 5

Мультиграф

int mtr[n][n] = { {0,1,0,0,1},{1,0,2,0,1},{0,2,0,2,0},{0,0,2,0,2},{1,1,0,2,0}

};

cout << n*n*sizeof(int);

Для всех видов графов веса вершин задают на главной диагонали

матрицы A или в виде дополнительного массива WV.

На чём можно сэкономить?

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 8

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

– над главной диагональю.

int main() {int** mtrSeg = new int*[n]; for (int i = 0; i < n; i++)

*(mtrSeg + i) = new int[n - i];

mtrSeg[0][0] = mtrSeg[0][3] = 1;mtrSeg[0][1] = mtrSeg[0][2] = 0;mtrSeg[1][0] = mtrSeg[1][1] = mtrSeg[1][2] = 1;mtrSeg[2][0] = 1; mtrSeg[2][1] = 0;mtrSeg[3][0] = 1;

for (int i = 0; i < n; i++) { // выводcout << "\n";for (int j = 0; j < n - i - 1; j++)

cout << *(*(mtrSeg + i)+j);}

0 1 0 0 1 1 0 1 1 1 0 1 0 1 0 0 1 1 0 1 1 1 0 1 0

12345

1 2 3 4 5

На чём можно сэкономить?

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 9

При отсутствии весов можно использовать битовую матрицу (каждый элемент

занимает один бит).

Brendan McKay and Adolfo Piperno. nauty and Traces (http://pallini.di.uniroma1.it)

Посмотреть вариант реализации подробно…

cout << "Memory size: " << sizeof(AdjMatr) + sizeof(rowSize*p);

Memory size: 8

FO и FI-представления

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 10

Для неориентированных графов определено только FO-представление,

реализуемое одним одномерным массивом.

5 2 5 0 1 3 4 5 0 4 2 0 3 2 5 0 1 2 4 0

Количество

вершин графа

(p)

Номера вершин,

смежных с

первой

Номера вершин,

смежных со

второй

Разделитель

(ноль)

Если нумерация вершин начинается с нуля, роль разделителя играет другой символ,

например -1.

Длина массива FO: 1 + 2·q + p, где q – количество рёбер, а p – количество

вершин графа.

5

1

2

34

1

7

23

4

5

6

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 11

#include <iostream>using namespace std;// число вершинconst int p = 5;// число рёберconst int q = 7;int main() {

int FO[1 + 2 * q + p] = { 5,2,5,0,1,3,4,5,0,4,2,0,3,2,5,0,1,2,4,0 };

cout << "\nNode: " << 1 << "\n\t";for (int i = 0, j = 1; i < 1 + 2 * q + p;i++) {

if (FO[i] == 0) {j++;cout << "\nNode: "<< j << "\n\t";continue;

}else cout << FO[i] << " ";

}return 0;

}

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 12

Варианты для орграфа:

1. для каждой вершины записываются номера вершин, в которые из этой вершины

исходят дуги (FO-представление),

2. Для каждой вершины записываются номера вершин, из которых исходят дуги в эту

вершину (FI-представление).

Длина массивов FO и FI равна 1 + q + p.

5

1

2

34

1

7

2

3

4

5

6

Задание на 5 минут: Напишите FO и FI

представления для этого орграфа. В качестве

разделителя используйте ноль.

Ответ:

FO 5 2 5 0 3 4 5 0 4 0 0 4 0

FI 5 0 1 0 2 0 2 5 3 0 1 2 0

На практических занятиях реализуйте данное представление на языке C++. Для

каждой вершины вычислите абсолютную разность сумм весов входящих и выходящих

рёбер. Результат вывести на экран.

MFO и MFI-представления

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 13

Для неориентированных графов определено только MFO-представление.

5

1

2

34

1

7

23

4

5

6

Номера вершин,

смежных с

первой

Номера вершин,

смежных со

второй

Длина массива ME равна 2 · q.

Длина массива MV равна p + 1

MFO =ME 2 5 1 3 4 5 4 2 3 2 5 1 2 4

MV 0 2 6 8 11 14 P 5

Индекс последнего

элемента ME плюс 1

Индекс первого

элемента ME, смежного

с первой вершиной

Индекс первого

элемента ME, смежного

со второй вершиной

Пример реализации

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 14

#include <iostream>using namespace std;// число вершинconst int p = 5;// число рёберconst int q = 7;int main() {

int ME[2 * q] = {2,5,1,3,4,5,4,2,3,2,5,1,2,4};int MV[p + 1] = { 0,2,6,8,11,14 };// цикл по вершинамfor (int i = 0; i < p; i++) {

cout << "Node " << i << ":\n\t";// перебор вершин, смежных с данной (соседей)for (int j = MV[i]; j < MV[i + 1];j++)

cout << ME[j] << " ";cout << "\n";

}return 0;

}

5

1

2

34

1

7

23

4

5

6

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 15

Длина массива ME равна q.

Длина массива MV равна p + 1

5

1

2

34

1

7

2

3

4

5

6

Варианты для орграфа:

1. FO-представление записывается в виде двух массивов (выходящие рёбра)

2. FI-представление записывается в виде двух массивов (входящие рёбра)

Задание на 5 минут: Напишите MFO представление для этого орграфа.

Ответ:

MFI =ME 1 2 2 3 5 1 2

MV 0 0 1 2 3 5 7 P 5

MFO =ME 2 5 3 4 5 4 4

MV 0 2 5 6 6 7 P 5

На практических занятиях реализуйте данное представление на языке C++.

На чём можно сэкономить?

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 16

В массивы FO и ME для каждой вершины записываются только те номера вершин,

которые не меньше (или не больше) номера этой вершины.

Это позволяет уменьшить размер массива FO c (1+2q+p) до (1+q+p), а массива

ME – с 2q до q.

Для неориентированных графов допустимы сокращённые FO- и MFO-

представления (BFO и BMFO)

BFO 5 2 5 0 3 4 5 0 4 0 5 0 0

5

1

2

34

1

7

23

4

5

6

FO 5 2 5 0 1 3 4 5 0 4 2 0 3 2 5 0 1 2 4 0

Сравните:

Матрица инциденций

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 17

Матрица инциденций графа – прямоугольная матрица В с числом строк p и

числом столбцов q. Каждый столбец соответствует одному из рёбер. Столбец,

соответствующий ребру e = {i, j} содержит 1 в i-й и j-й строке, в остальных строках

содержатся нули. Столбец, соответствующий петле, содержит единственную 1.

Для орграфа начало и конец дуги задаются разными числами (например, –1 –

начало, 1 – конец).

Для мультиграфа можно либо рассматривать каждое ребро в составе мультиребра

как отдельное (с записью в отдельный столбец), либо записывать значение

кратности ребра вместо 1.

Представление занимает объём памяти, равный pqa, где a – размер

элемента матрицы.

5

1

2

34

1

7

23

4

5

6

1 1 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1 1 1 1 0 1 0 0 0 1

12345

1 2 3 4 5 6 7

Примеры матриц инциденций

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 18

5

1

2

34

1

7

2

3

4

5

6

1 1 0 0 0 0 0 0 -1 1 1 0 1 0 0 0 0 -1 1 0 0 0 0 0 0 -1 -1 -1-1 0 -1 0 0 0 1

12345

1 2 3 4 5 6 7

5

1

2

34

4

7

12

3

5

6

9

8

1 1 0 0 0 0 0 0 0 1 0 1 1 1 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 0 1 1 0 0 0 0 1 1

12345

1 2 3 4 5 6 7 8 9

Мультиграф

Орграф

Массив рёбер

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 19

Массив рёбер графа – прямоугольная матрица C с двумя строками и числом

столбцов q.

Каждый столбец соответствует одному из рёбер.

В столбце, соответствующем ребру e = {i, j} первый элемент содержит i, а второй

– j.

5

1

2

34

1

7

23

4

5

6

1 2 2 2 3 4 5

2 3 4 5 4 5 1

q = 7

1 1 2 2 2 3 4

2 5 3 4 5 4 5

Отсортированный

C =

void setRib(rib& r, int a, int b) {r.beg = a;r.end = b;

}

struct rib {int beg;int end;

};

rib C[q];

Ссылочные представленияСписки смежности

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 20

Списки смежности

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 21

2

Указатель на граф

4 NULL

1 3

NU

LL

NULL

2 4 NULL

4

1 2 NULL3

1

24

12

3

4

5

3

Список LV содержит ссылки на

списки смежности отдельных

вершин LEi.

Каждый список смежности LEi– список

(обычно односвязный) номеров вершин,

смежных c вершиной i.

Организация списков смежности

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 22

// элемент списка смежностиstruct AdjList {

AdjList* next; // следующий элемент спискаint w; // номер вершины

};

// вершина графаstruct VerList {

AdjList* head; // указатель на начало списка смежностиVerList* next; // следущая вершина

};

Односвязные списки, построенные при помощи структур

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 23

int main() {VerList LVtmp;VerList* LVhead=&LVtmp; // указатель на начало списка// первая смежная с первой вершинаAdjList ELtmp1;ELtmp1.next = NULL;ELtmp1.w = 2;// вторая смежная с первой вершинаAdjList newEL;newEL.next = NULL;newEL.w = 4;// добавили вторую в списокELtmp1.next = &newEL;// связываем список смежности с первой вершинойLVtmp.head = &ELtmp1;LVtmp.next = NULL;cout << LVtmp.head->w << " ";cout << LVtmp.head->next->w;return 0;

}

Очень подробный пример очень нереального кода

2 4 NULL

Требуется декомпозиция!

Объектно-ориентированные представления

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 24

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 25

Объект-вершинаОбъект-ребро

Edges

Веса

Edges

Веса

Edges

Веса

Edges

Веса

1

2

3

4

SourceV1

DestV

SourceV5

DestV

SourceV4

DestV

SourceV3

DestV

SourceV2

DestV

Веса

Веса

Веса

Веса

Веса

124

12

3

4

5

3

Объектно-ориентированная библиотека для работы с графами

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 26

Boost Graph Library (BGL). Graph interface

(http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/graph_concepts.html)

Рекомендации по применению представлений

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 27

Применение матрицы смежности является оправданным, когда:

1) число вершин невелико (p 1000), а число рёбер велико (стандартная

оценка для обыкновенного графа: > p(p1)/4);

2) когда необходимо иметь быстрый доступ к произвольным рёбрам графа;

3) когда часто необходимо добавлять и удалять рёбра при неизменном числе

вершин.

Когда трансформация графа не требуется, хорошим компромиссом для графов

общего вида является MFO-представление и его варианты (списки смежности

и др.).

Матрица инциденций – самое невыгодное с алгоритмической

точки зрения представление графа.

Сложность базовых операций при различных представлениях графа

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 28

№ Представление

Асимптотическая сложность

операций

IsEdge AddEdge ForAllAdj

1 Матрица смежности 1 1 p

2 Матрица инциденций q p+alloc pq

3 Массив рёбер q alloc q

4 Отсортированный массив рёбер log(q) q+alloclog(q)+de

g(i)

5 FO-представление p+q p+q+alloc p+q

6 MFO-представлениеmax(deg(i),

deg(j))q+alloc deg(i)

7 Отсортированное MFO-представлениеmax(log(deg(i

)), log(deg(j)))q+alloc deg(i)

ПриложенияЗавершить показ

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 29

Вариант реализации матрицы смежности через битовую матрицу

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 30

#include <iostream>using namespace std;// число вершин графаconst unsigned int p = 5;// размер unsigned int в битахconst unsigned int ElSize = sizeof(unsigned int) << 3;// Двоичный логарифм от размера unsigned int в битахconst unsigned int LogElSize = log2(sizeof(unsigned int)) + 3;// длина строки битовой матрицы в unsigned intconst unsigned int rowSize = (p + (sizeof(unsigned int) << 3) - 1) >> LogElSize;// массив указателей на строки матрицы смежностиunsigned int* AdjMatRows[p];// указатель на саму битовую матрицуunsigned int* AdjMatr;

5

1

2

34

Выделение памяти под битовую матрицу

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 31

int main() {// выделяем память под матрицу смежностиAdjMatr = new unsigned int[rowSize*p];

// зануляем элементыfor (int i = 0; i < rowSize*p; AdjMatr[i] = 0,i++);

// связываем указатели на строки матрицы с участками памяти*AdjMatRows = AdjMatr;

for (int i = 1; i < p; i++) AdjMatRows[i] = AdjMatRows[i - 1]+ rowSize;

// здесь добавляем рёбра, вычисления, что угодно...delete AdjMatr;delete AdjMatRows;return 0;}

Добавления ребра… Куда поставить 1?

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 32

// функция добавления ребра между вершинами a и bvoid addEdge(int a, int b) { // номера вершин

// находим нужный uint в строке aunsigned int w = AdjMatRows[a][b >> LogElSize];// проверяем бит с номером остаток от деления b на число

бит в unsigned intunsigned int e = 1 << (b & (ElSize - 1));if (!(w & e)) {

// добавляем A[a,b]AdjMatRows[a][b >> LogElSize] = w | e;// добавляем A[b,a]w = AdjMatRows[b][a >> LogElSize];e = 1 << (a & (ElSize - 1));AdjMatRows[b][a >> LogElSize] = w | e;

}}

Функция печати битовой матрицы

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 33

// функция печати матрицы смежностиvoid printMat() {

for (int i = 0; i < p; i++) { // выводcout << "\n";for (int j = 0; j < rowSize; j++)

// битовое представление строкиfor (int k = 0; k < p; k++)

cout << ((AdjMatRows[i][j] >> k) & 1);}cout << "\n";

}

На практических занятиях:

1) запустите код и протестируйте его для матриц смежности графов с 32 и более

вершинами.

2) допишите функцию удаления ребра между вершинами a и b.

Окончание функции main()

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 34

addEdge(0, 1);addEdge(0, 4);addEdge(1, 2);addEdge(1, 3);addEdge(1, 4);addEdge(2, 3);addEdge(3, 4);cout << sizeof(AdjMatr) << " " << sizeof(rowSize*p);printMat();system("pause");delete AdjMatr;delete AdjMatRows;

Вернуться >>

(с) Максименкова О.В., НИУ ВШЭ, ФКН, ДПИ 35

Спасибо за внимание!

Максименкова Ольга Вениаминовна

Старший преподаватель Департамента программной инженерии, ФКН

E-mail: [email protected]

Blog: Stop To Scale (http://stoptoscale.blogspot.ru)