|
Serwis Edukacyjny w I-LO w Tarnowie
Materiały dla uczniów liceum |
Wyjście Spis treści Wstecz Dalej
Autor artykułu: mgr Jerzy Wałaszek |
©2026 mgr Jerzy Wałaszek
|
W danym grafie nieskierowanym należy znaleźć wszystkie kliki maksymalne.

Klika maksymalna (ang. maximal clique) jest
taką kliką grafu, że nie można do niej już dodać żadnego nowego wierzchołka z grafu.
Klika największa (ang. maximum clique) jest największym podgrafem pełnym.
Problem znajdowania największej kliki jest
problemem trudnym obliczeniowo.
Do znajdowania maksymalnych klik w grafie nieskierowanym można zastosować
prosty algorytm rekurencyjny Brona-Kerboscha. Algorytm otrzymuje na wejściu
trzy zbiory
P : zbiór wierzchołków, które są kandydatami do rozważenia.
R : zbiór wierzchołków, które są częściowym wynikiem znajdowania kliki.
X : zbiór wierzchołków pominiętych.
Przy pierwszym wywołaniu algorytmu zbiory R i X są puste, a zbiór P zawiera wszystkie wierzchołki grafu. Zasada działania algorytmu jest następująca:
Najpierw sprawdzamy w algorytmie, czy zbiory
Jeśli zbiór P nie jest pusty, to wybieramy z niego kolejne wierzchołki v. Dla każdego z tych wierzchołków tworzymy następujące zbiory tymczasowe:
Wywołujemy rekurencyjnie algorytm ze zbiorami
Następnie ze zbioru P usuwamy
wierzchołek v
K01: Jeśli (P jest pusty)(X jest pusty), ; znaleziona klika maksymalna to Pisz R i zakończ K02: Dla każdego wierzchołka v w P: wykonuj kroki K03…K10 K03: Zeruj zbiór N K04: Dla każdego sąsiada u wierzchołka v: ; tworzymy zbiór sąsiadów wykonuj: N ← N + u K05: R' ← R+v ; w R' tworzymy zbiór R z dodanym wierzchołkiem v K06: P' ← iloczyn zbiorów P i N ; z P' usuwamy wierzchołki, które nie są sąsiadami v K07: X' ← iloczyn zbiorów X i N ; z X' usuwamy wierzchołki, które nie są sąsiadami v K08: BronKerbosch(n,graf,R',P',X') ; wywołanie rekurencyjne K09: P ← P - v ; ze zbioru P usuwamy wierzchołek v K10: R ← R + v ; do zbioru R dodajemy wierzchołek v K11: Zakończ
|
Uwaga: Zanim uruchomisz program, przeczytaj wstęp do tego artykułu, w którym wyjaśniamy funkcje tych programów oraz sposób korzystania z nich. |
| Program odczytuje
definicję
grafu nieskierowanego i wyszukuje w nim wszystkie kliki maksymalne
za pomocą algorytmu Brona-Kerboscha. Zbiory zostały zaimplementowane
w tablicy logicznej. Program dodatkowo wypisuje liczbę wywołań rekurencyjnych algorytmu Brona-Kerboscha dla celów porównawczych. |
![]() |
8 19 0 1 0 2 0 5 0 6 1 2 1 3 1 5 1 6 2 4 2 5 2 6 2 7 3 4 3 6 3 7 4 6 5 6 5 7 6 7 |
Pascal// Algorytm Brona-Kerboscha
// Data: 10.07.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------
program cliques;
// Definicja elementu
// listy sąsiedztwa
type
PSLel = ^SLel;
SLel = record
// Następny
// element listy
next : PSLel;
// Wierzchołek docelowy
v : integer;
end;
// Definicja struktury zbioru
SSet = record
vmin : integer;
nmax : integer;
T : array of boolean;
end;
// Zmienne globalne
var
// Liczba wierzchołków
// i krawędzi grafu
n, m : integer;
// Tablica list
// sąsiedztwa
graf : array of PSLel;
// Zbiór sąsiadów
SN : SSet;
// Licznik wywołań
// rekurencyjnych
rcnt : integer;
// Obsługa zbiorów
// ---------------
// Procedura dodaje
// element x do zbioru A
//----------------------
procedure s_add(var A : SSet;
x : integer);
var
i : integer;
begin
// Obliczamy indeks
i := x - A.vmin;
// Ustawiamy element
A.T[i] := true;
end;
// Procedura usuwa
// element x ze zbioru
//--------------------
procedure s_remove(var A : SSet;
x : integer);
var
i : integer;
begin
// Obliczamy indeks
i := x - A.vmin;
// Zerujemy element
A.T[i] := false;
end;
// Procedura usuwa
// wszystkie elementy
// ze zbioru
//-------------------
procedure s_clear(var A : SSet);
var
i : integer;
begin
// Zerujemy tablicę
for i := 0 to A.nmax do
A.T[i] := false;
end;
// Procedura tworzy zbiór
//-----------------------
procedure s_create(var A : SSet;
vmin, vmax : integer);
begin
// Wypełniamy
// strukturę danymi
A.vmin := vmin;
// Indeks ostatniego
// elementu tablicy
A.nmax := vmax - vmin;
// Tworzymy dynamicznie
// tablicę logiczną
SetLength(A.T, A.nmax + 1);
// Tworzymy zbiór pusty
s_clear(A);
end;
// Procedura wyznacza
// część wspólną zbiorów A i B
//----------------------------
procedure s_intersection
(var A, B, C :SSet);
var
i : integer;
begin
for i := 0 to A.nmax do
C.T[i] := A.T[i] and B.T[i];
end;
// Procedura kopiuje
// zbiór A do zbioru B
//--------------------
procedure s_copy(var A, B : SSet);
var
i : integer;
begin
for i := 0 to A.nmax do
B.T[i] := A.T[i];
end;
// Procedura ustawia
// zbiór pełny
procedure s_full(var A : SSet);
var
i : integer;
begin
for i := 0 to A.nmax do
A.T[i] := true;
end;
// Funkcja sprawdza,
// czy zbiór A jest pusty
// true - jest
// false - nie jest
//-----------------------
function s_empty(var A : SSet)
: boolean;
var
i : integer;
m : boolean;
begin
// Pobieramy bity do m
m := A.T[0];
for i := 1 to A.nmax do
// Sumujemy logicznie bity
m := m or A.T[i];
s_empty := (m = false);
end;
// Funkcja bada,
// czy element x
// należy do zbioru A
// true - tak, należy
// false - nie, nie należy
//------------------------
function s_isin(var A : SSet;
x : integer)
: boolean;
var
i : integer;
begin
// Obliczamy indeks
i := x - A.vmin;
s_isin := A.T[i]
end;
// Procedura wyświetla
// elementy zbioru A
//--------------------
procedure s_print(var A : SSet);
var
i : integer;
begin
for i := 0 to A.nmax do
if A.T[i] then
write(i + A.vmin,' ');
end;
// Procedura wyszukuje
// kliki maksymalne
//--------------------
procedure BronKerbosch
(var SR, SP, SX : SSet);
var
RP, PP, XP : SSet;
p : PSLel;
v : integer;
begin
// Zwiększamy licznik
// wywołań rekurencyjnych
inc(rcnt);
// Jeśli zbiory SP i SX są puste,
if s_empty(SP) and
s_empty(SX) then
// to SR zawiera wierzchołki
// kliki maksymalnej
begin
write('MAXIMAL CLIQUE: ');
s_print(SR);
writeln;
end
else
begin
// Tworzymy puste
// zbiory pomocnicze
s_create(RP, 0, n-1);
s_create(PP, 0, n-1);
s_create(XP, 0, n-1);
// Przechodzimy przez
// kolejne wierzchołki
// grafu
for v := 0 to n - 1 do
// Jeśli wierzchołek
// v jest w zbiorze SP, to
if s_isin(SP, v) then
// go przetwarzamy
begin
// Najpierw zerujemy
// zbiór sąsiadów
s_clear(SN);
// Przeglądamy listę
// sąsiedztwa wierzchołka v
p := graf[v];
while p <> nil do
begin
// Każdego sąsiada
// umieszczamy w SN
s_add(SN, p^.v);
p := p^.next;
end;
// W RP tworzymy zbiór SR
// z dodanym wierzchołkiem v
s_copy(SR, RP);
s_add(RP, v);
// W PP tworzymy
// iloczyn SP i SN
s_intersection(SP, SN, PP);
// W XP tworzymy
// iloczyn SX i SN
s_intersection(SX, SN, XP);
// Wywołanie rekurencyjne
BronKerbosch(RP, PP, XP);
// Z SP usuwamy
// wierzchołek v
s_remove(SP, v);
// i dodajemy go do SX
s_add(SX, v);
end;
// Usuwamy zbiory pomocnicze
SetLength(RP.T, 0);
SetLength(PP.T, 0);
SetLength(XP.T, 0);
end;
end;
// **********************
// *** PROGRAM GŁÓWNY ***
// **********************
var
SR, SP, SX :SSet;
i, u, v : integer;
p, r : PSLel;
begin
// Odczytujemy liczbę
// wierzchołków i krawędzi
read(n, m);
// Tworzymy puste zbiory
s_create(SR, 0, n-1);
s_create(SP, 0, n-1);
s_create(SX, 0, n-1);
s_create(SN, 0, n-1);
// Tworzymy tablicę
// list sąsiedztwa
SetLength(graf, n);
// Zerujemy listy sąsiedztwa
for i := 0 to n - 1 do
graf[i] := nil;
for i := 1 to m do
begin
// Wierzchołki krawędzi
read(u, v);
// Tworzymy element listy
new(p);
p^.v := u;
// Element dołączamy
// do listy sąsiadów v
p^.next := graf[v];
graf[v] := p;
// To samo dla krawędzi
// w drugą stronę
new(p);
p^.v := v;
// Element dołączamy
// do listy sąsiadów u
p^.next := graf[u];
graf[u] := p;
end;
// W zbiorze SP ustawiamy
// wszystkie wierzchołki
s_full(SP);
writeln;
// Zerujemy licznik
// wywołań rekurencyjnych
rcnt := 0;
// Szukamy rekurencyjnie
// klik maksymalnych
BronKerbosch(SR, SP, SX);
writeln;
writeln('RECURSIVE CALLS = ',rcnt);
writeln;
// Usuwamy zbiory
SetLength(SR.T, 0);
SetLength(SP.T, 0);
SetLength(SX.T, 0);
SetLength(SN.T, 0);
// Usuwamy tablicę
// list sąsiedztwa
for v := 0 to n-1 do
begin
p := graf[v];
while p <> nil do
begin
r := p;
p := p^.next;
dispose(r);
end;
end;
SetLength(graf, 0);
end.
|
C++// Algorytm Brona-Kerboscha
// Data: 10.07.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------
#include <iostream>
#include <iomanip>
using namespace std;
// Definicja elementu
// listy sąsiedztwa
struct SLel
{
// Następny element listy
SLel *next;
// Wierzchołek docelowy
int v;
};
// Definicja struktury zbioru
struct Set
{
int vmin, nmax;
bool *T;
};
// Zmienne globalne
//-----------------
// Liczba wierzchołków
// i krawędzi grafu
int n, m;
// Tablica list sąsiedztwa
SLel ** graf;
// Zbiór sąsiadów
Set SN;
// Licznik wywołań
// rekurencyjnych
int rcnt = 0;
// Obsługa zbiorów
//----------------
// Procedura dodaje
// element x do zbioru A
//----------------------
void s_add(Set & A, int x)
{
// Obliczamy indeks
int i = x - A.vmin;
// Ustawiamy bit na 1
A.T[i] = true;
}
// Procedura usuwa
// element x ze zbioru
//--------------------
void s_remove(Set & A, int x)
{
// Obliczamy indeks
int i = x - A.vmin;
// Ustawiamy bit na 0
A.T[i] = false;
}
// Procedura usuwa wszystkie
// elementy ze zbioru
//--------------------------
void s_clear(Set & A)
{
// Zerujemy tablicę
for(int i = 0; i <= A.nmax; i++)
A.T[i] = false;
}
// Procedura tworzy zbiór
//-----------------------
void s_create(Set & A,
int vmin,
int vmax)
{
// Wypełniamy strukturę danymi
A.vmin = vmin;
// Indeks ostatniego
// elementu tablicy
A.nmax = vmax - vmin;
// Tworzymy dynamicznie
// tablicę logiczną
A.T = new bool [A.nmax+1];
// Tworzymy zbiór pusty
s_clear(A);
}
// Procedura wyznacza część
// wspólną zbiorów A i B
//-------------------------
void s_intersection(Set & A,
Set & B,
Set & C)
{
for(int i = 0; i <= A.nmax; i++)
C.T[i] = A.T[i] && B.T[i];
}
// Procedura kopiuje
// zbiór A do zbioru B
//--------------------
void s_copy(Set & A, Set & B)
{
for(int i = 0; i <= A.nmax; i++)
B.T[i] = A.T[i];
}
// Procedura ustawia
// zbiór pełny
//-------------------
void s_full(Set & A)
{
for(int i = 0; i <= A.nmax; i++)
A.T[i] = true;
}
// Funkcja sprawdza,
// czy zbiór A jest pusty
// true - tak, jest pusty
// false - nie, nie jest pusty
//----------------------------
bool s_empty(Set A)
{
// Pobieramy bity do m
bool m = A.T[0];
// Sumujemy logicznie bity
for(int i = 1; i <= A.nmax; i++)
m = m || A.T[i];
return !m;
}
// Funkcja bada, czy element
// x należy do zbioru A
// true - tak, należy
// false - nie, nie należy
//--------------------------
bool s_isin(Set & A, int x)
{
// Obliczamy indeks
int i = x - A.vmin;
return A.T[i];
}
// Procedura wyświetla
// elementy zbioru A
//--------------------
void s_print(Set A)
{
int i;
for(i = 0; i <= A.nmax; i++)
if(A.T[i])
cout << i + A.vmin << " ";
}
// Procedura wyszukuje
// kliki maksymalne
//--------------------
void BronKerbosch(Set & SR,
Set & SP,
Set & SX)
{
Set RP, PP, XP;
SLel * p;
int v;
// Zwiększamy licznik
// wywołań rekurencyjnych
rcnt++;
// Jeśli zbiory SP
// i SX są puste,
if(s_empty(SP) && s_empty(SX))
// to SR zawiera wierzchołki
// kliki maksymalnej
{
cout << "MAXIMAL CLIQUE: ";
s_print(SR);
cout << endl;
}
else
{
// Tworzymy puste
// zbiory pomocnicze
s_create(RP, 0, n-1);
s_create(PP, 0, n-1);
s_create(XP, 0, n-1);
// Przechodzimy przez
// kolejne wierzchołki grafu
for(v = 0; v < n; v++)
// Jeśli wierzchołek v
// jest w zbiorze SP, to
if(s_isin(SP, v))
// go przetwarzamy
{
// Najpierw zerujemy
// zbiór sąsiadów
s_clear(SN);
// Przeglądamy listę
// sąsiedztwa wierzchołka v
for(p = graf[v];p;p = p->next)
// Każdego sąsiada
// umieszczamy w SN
s_add(SN, p->v);
// W RP tworzymy zbiór SR
// z dodanym wierzchołkiem v
s_copy(SR, RP);
s_add(RP, v);
// W PP tworzymy
// iloczyn SP i SN
s_intersection(SP, SN, PP);
// W XP tworzymy
// iloczyn SX i SN
s_intersection(SX, SN, XP);
// Wywołanie rekurencyjne
BronKerbosch (RP, PP, XP);
// Z SP usuwamy
// wierzchołek v
s_remove(SP, v);
// i dodajemy go do SX
s_add(SX, v);
}
// Usuwamy zbiory pomocnicze
delete [] RP.T;
delete [] PP.T;
delete [] XP.T;
}
}
// **********************
// *** PROGRAM GŁÓWNY ***
// **********************
int main()
{
Set SR, SP, SX;
int i, u, v;
SLel *p, *r;
// Odczytujemy liczbę
// wierzchołków i krawędzi
cin >> n >> m;
// Tworzymy puste zbiory
s_create(SR, 0, n-1);
s_create(SP, 0, n-1);
s_create(SX, 0, n-1);
s_create(SN, 0, n-1);
// Tworzymy tablicę
// list sąsiedztwa
graf = new SLel * [n];
// Zerujemy listy sąsiedztwa
for(i = 0; i < n; i++)
graf[i] = nullptr;
for(i = 0; i < m; i++)
{
// Wierzchołki krawędzi
cin >> u >> v;
// Tworzymy element listy
p = new SLel;
p->v = u;
// Element dołączamy
// do listy sąsiadów v
p->next = graf[v];
graf[v] = p;
p = new SLel;
p->v = v;
// Element dołączamy
// do listy sąsiadów u
p->next = graf[u];
graf[u] = p;
}
// W zbiorze SP ustawiamy
// wszystkie wierzchołki
s_full(SP);
cout << endl;
// Szukamy rekurencyjnie
// klik maksymalnych
BronKerbosch(SR, SP, SX);
cout << endl
<< "RECURSIVE CALLS = "
<< rcnt << endl << endl;
// Usuwamy zbiory
delete [] SR.T;
delete [] SP.T;
delete [] SX.T;
delete [] SN.T;
// Usuwamy tablicę
// list sąsiedztwa
for(v = 0; v < n; v++)
{
p = graf[v];
while(p)
{
r = p;
p = p->next;
delete r;
}
}
delete [] graf;
return 0;
}
|
Basic' Algorytm Brona-Kerboscha
' Data: 10.07.2014
' (C)2014 mgr Jerzy Wałaszek
'---------------------------
' Definicja elementu
' listy sąsiedztwa
Type SLel
' Następny element listy
next As SLel Ptr
' Wierzchołek docelowy
v As Integer
End Type
' Definicja struktury zbioru
Type Set
vmin As Integer
nmax As Integer
T As Boolean Ptr
End Type
' Zmienne globalne
'-----------------
' Liczba wierzchołków
' i krawędzi grafu
Dim Shared As Integer n, m
' Tablica list sąsiedztwa
Dim Shared As SLel Ptr Ptr graf
' Zbiór sąsiadów
Dim Shared As Set SN
' Licznik wywołań rekurencyjnych
Dim Shared As Integer rcnt = 0
' Obsługa zbiorów
'----------------
' Procedura dodaje
' element x do zbioru A
'----------------------
Sub s_add(ByRef A As Set, _
ByVal x As Integer)
Dim As Integer i
' Obliczamy indeks
i = x - A.vmin
' Ustawiamy bit na 1
A.T[i] = True
End Sub
' Procedura usuwa
' element x ze zbioru
'--------------------
Sub s_remove(ByRef A As Set, _
ByVal x As Integer)
Dim As Integer i
' Obliczamy indeks
i = x - A.vmin
' Ustawiamy bit na 0
A.T[i] = False
End Sub
' Procedura usuwa
' wszystkie elementy
' ze zbioru
'-------------------
Sub s_clear(ByRef A As Set)
Dim As Integer i
' Zerujemy tablicę
For i = 0 To A.nmax
A.T[i] = False
Next
End Sub
' Procedura tworzy zbiór
'-----------------------
Sub s_create(ByRef A As Set, _
ByVal vmin As Integer, _
ByVal vmax As Integer)
' Wypełniamy strukturę danymi
A.vmin = vmin
' Indeks ostatniego
' elementu tablicy
A.nmax = vmax - vmin
' Tworzymy dynamicznie
' tablicę mapy bitowej
A.T = New Boolean [A.nmax + 1]
' Tworzymy zbiór pusty
s_clear(A)
End Sub
' Procedura wyznacza
' część wspólną
' zbiorów A i B
'-------------------
Sub s_intersection(ByRef A As Set, _
ByRef B As Set, _
ByRef C As Set)
Dim As Integer i
For i = 0 To A.nmax
C.T[i] = A.T[i] And B.T[i]
Next
End Sub
' Procedura kopiuje
' zbiór A do zbioru B
'--------------------
Sub s_copy(ByRef A As Set, _
ByRef B As Set)
Dim As Integer i
For i = 0 To A.nmax
B.T[i] = A.T[i]
Next
End Sub
' Procedura ustawia
' zbiór pełny
'------------------
Sub s_full(ByRef A As Set)
Dim As Integer i
For i = 0 To A.nmax
A.T[i] = True
Next
End Sub
' Funkcja sprawdza,
' czy zbiór A jest pusty
' true - tak, jest pusty
' false - nie, nie jest pusty
'----------------------------
Function s_empty(ByRef A As Set) _
As Boolean
Dim As Integer i
' Pobieramy bity do m
Dim As Boolean m = A.T[0]
For i = 1 To A.nmax
' Sumujemy logicznie bity
m = m Or A.T[i]
Next
Return Not m
End Function
' Funkcja bada,
' czy element x
' należy do zbioru A
' true - tak, należy
' false - nie, nie należy
'------------------------
Function s_isin(ByRef A As Set, _
ByVal x As Integer) _
As Boolean
' Obliczamy indeks
Dim As Integer i = x - A.vmin
Return A.T[i]
End Function
' Procedura wyświetla
' elementy zbioru A
'--------------------
Sub s_print(ByRef A As Set)
Dim As Integer i
For i = 0 To A.nmax
If A.T[i] Then _
Print Using "& ";i + A.vmin;
Next
End Sub
' Procedura wyszukuje
' kliki maksymalne
'--------------------
Sub BronKerbosch(ByRef SR As Set, _
ByRef SP As Set, _
ByRef SX As Set)
Dim As Set RP, PP, XP
Dim As SLel Ptr p
Dim As Integer v
' Zwiększamy licznik
' wywołań rekurencyjnych
rcnt += 1
' Jeśli zbiory
' SP i SX są puste,
' to SR zawiera
' wierzchołki
' kliki maksymalnej
If s_empty(SP) AndAlso _
s_empty(SX) Then
Print "MAXIMAL CLIQUE: ";
s_print(SR)
Print
Else
' Tworzymy puste
' zbiory pomocnicze
s_create(RP, 0, n-1)
s_create(PP, 0, n-1)
s_create(XP, 0, n-1)
' Przechodzimy przez
' kolejne wierzchołki
' grafu
For v = 0 To n - 1
' Jeśli wierzchołek v
' jest w zbiorze SP, to
' go przetwarzamy
If s_isin(SP, v) Then
' Najpierw zerujemy
' zbiór sąsiadów
s_clear(SN)
' Przeglądamy listę
' sąsiedztwa wierzchołka v
p = graf[v]
While p
' Każdego sąsiada
' umieszczamy w SN
s_add(SN, p->v)
p = p->next
Wend
' W RP tworzymy
' zbiór SR z dodanym
' wierzchołkiem v
s_copy(SR, RP)
s_add(RP, v)
' W PP tworzymy
' iloczyn SP i SN
s_intersection(SP, SN, PP)
' W XP tworzymy
' iloczyn SX i SN
s_intersection(SX, SN, XP)
' Wywołanie rekurencyjne
BronKerbosch(RP, PP, XP)
' Z SP usuwamy
' wierzchołek v
s_remove(SP, v)
' i dodajemy go do SX
s_add(SX, v)
End If
Next
' Usuwamy
' zbiory pomocnicze
Delete [] RP.T
Delete [] PP.T
Delete [] XP.T
End If
End Sub
' **********************
' *** PROGRAM GŁÓWNY ***
' **********************
Dim As Set SR, SP, SX
Dim As integer i, u, v
Dim As SLel Ptr p, r
Open Cons For Input As #1
' Odczytujemy liczbę
' wierzchołków i krawędzi
Input #1, n, m
' Tworzymy puste zbiory
s_create(SR, 0, n-1)
s_create(SP, 0, n-1)
s_create(SX, 0, n-1)
s_create(SN, 0, n-1)
' Tworzymy tablicę
' list sąsiedztwa
graf = New SLel Ptr [n]
For i = 0 To n - 1
' Zerujemy listy
' sąsiedztwa
graf[i] = 0
Next
For i = 1 To m
' Wierzchołki krawędzi
Input #1, u, v
' Tworzymy
' element listy
p = New SLel
p->v = u
' Element dołączamy
' do listy sąsiadów v
p->next = graf[v]
graf[v] = p
' To samo dla krawędzi
' w drugą stronę
p = New SLel
p->v = v
' Element dołączamy
' do listy sąsiadów u
p->next = graf[u]
graf[u] = p
Next
Close #1
' W zbiorze SP
' ustawiamy
' wszystkie wierzchołki
s_full(SP)
Print
' Szukamy rekurencyjnie
' klik maksymalnych
BronKerbosch(SR, SP, SX)
Print
Print "RECURSIVE CALLS =";rcnt
Print
' Usuwamy zbiory
Delete [] SR.T
Delete [] SP.T
Delete [] SX.T
Delete [] SN.T
' Usuwamy tablicę
' list sąsiedztwa
For v = 0 To n - 1
p = graf[v]
While p
r = p
p = p->next
Delete r
Wend
Next
Delete [] graf
End
|
Python
(dodatek)# Algorytm Brona-Kerboscha
# Data: 21.03.2025
# (C)2014 mgr Jerzy Wałaszek
#---------------------------
# Definicja elementu
# listy sąsiedztwa
class SLel:
def __init__(self):
# Następny element listy
self.next = None
# Wierzchołek docelowy
self.v = 0
# Definicja struktury zbioru
class Set:
def __init__(self):
self.vmin = 0
self.nmax = 0
self.t = []
# Obsługa zbiorów
#----------------
# Procedura dodaje
# element x do zbioru A
def s_add(a,x):
# Obliczamy indeks
i = x - a.vmin
# Ustawiamy komórkę
a.t[i] = True
# Procedura usuwa
# element x ze zbioru
def s_remove(a,x):
# Obliczamy indeks
i = x - a.vmin
# Zerujemy komórkę
a.t[i] = False
# Procedura usuwa
# wszystkie elementy
# ze zbioru
def s_clear(a):
# Zerujemy wszystkie komórki
a.t = [False] * (a.nmax + 1)
# Procedura tworzy zbiór
def s_create(a,vmin,vmax):
# Wypełniamy strukturę danymi
a.vmin = vmin
# Indeks ostatniego
# elementu tablicy
a.nmax = vmax - vmin
# Tworzymy dynamicznie
# tablicę mapy
a.t = [False] * (a.nmax + 1)
# Procedura wyznacza
# część wspólną
# zbiorów A i B
def s_intersection(a,b,c):
for i in range(a.nmax+1):
c.t[i] = a.t[i] and b.t[i]
# Procedura kopiuje
# zbiór A do zbioru B
def s_copy(a,b):
b.t = a.t[:]
# Procedura ustawia
# zbiór pełny
def s_full(a):
a.t = [True] * (a.nmax + 1)
# Funkcja sprawdza,
# czy zbiór A jest pusty
# true - tak, jest pusty
# false - nie, nie jest pusty
def s_empty(a):
# sumujemy komórki
m = a.t[0]
for i in range(1,a.nmax+1):
m = m or a.t[i]
return not m
# Funkcja bada,
# czy element x
# należy do zbioru A
# true - tak, należy
# false - nie, nie należy
def s_isin(a,x):
# Obliczamy indeks
i = x - a.vmin
return a.t[i]
# Procedura wyświetla
# elementy zbioru A
def s_print(a):
for i in range(a.nmax+1):
if a.t[i]:
print(i + a.vmin,end=" ")
# Procedura wyszukuje
# kliki maksymalne
def bron_kerbosch(sr,sp,sx):
global n,sn,graf,rcnt
rp = Set()
pp = Set()
xp = Set()
# Zwiększamy licznik
# wywołań rekurencyjnych
rcnt += 1
# Jeśli zbiory
# SP i SX są puste,
# to SR zawiera
# wierzchołki
# kliki maksymalnej
if s_empty(sp) and s_empty(sx):
print("MAXIMAL CLIQUE: ",end="")
s_print(sr)
print()
else:
# Tworzymy puste
# zbiory pomocnicze
s_create(rp, 0, n-1)
s_create(pp, 0, n-1)
s_create(xp, 0, n-1)
# Przechodzimy przez
# kolejne wierzchołki
# grafu
for v in range(n):
# Jeśli wierzchołek v
# jest w zbiorze SP, to
# go przetwarzamy
if s_isin(sp,v):
# Najpierw zerujemy
# zbiór sąsiadów
s_clear(sn)
# Przeglądamy listę
# sąsiedztwa
# wierzchołka v
p = graf[v]
while p:
# Każdego sąsiada
# umieszczamy w SN
s_add(sn, p.v)
p = p.next
# W RP tworzymy
# zbiór SR z dodanym
# wierzchołkiem v
s_copy(sr, rp)
s_add(rp, v)
# W PP tworzymy
# iloczyn SP i SN
s_intersection(sp,sn,pp)
# W XP tworzymy
# iloczyn SX i SN
s_intersection(sx,sn,xp)
# Wywołanie rekurencyjne
bron_kerbosch(rp,pp,xp)
# Z SP usuwamy
# wierzchołek v
s_remove(sp, v)
# i dodajemy go do SX
s_add(sx, v)
# Usuwamy
# zbiory pomocnicze
del rp,pp,xp
# **********************
# *** PROGRAM GŁÓWNY ***
# **********************
sr = Set()
sp = Set()
sx = Set()
sn = Set()
# Odczytujemy liczbę
# wierzchołków i krawędzi
x = input().split()
n = int(x[0])
m = int(x[1])
# Tworzymy puste zbiory
s_create(sr, 0, n-1)
s_create(sp, 0, n-1)
s_create(sx, 0, n-1)
s_create(sn, 0, n-1)
# Tworzymy tablicę
# list sąsiedztwa
graf = [None] * n
for i in range(m):
# Wierzchołki krawędzi
x = input().split()
u = int(x[0])
v = int(x[1])
# Tworzymy
# element listy
p = SLel()
p.v = u
# Element dołączamy
# do listy sąsiadów v
p.next = graf[v]
graf[v] = p
# To samo dla krawędzi
# w drugą stronę
p = SLel()
p.v = v
# Element dołączamy
# do listy sąsiadów u
p.next = graf[u]
graf[u] = p
# W zbiorze SP
# ustawiamy
# wszystkie wierzchołki
s_full(sp)
print()
# Szukamy rekurencyjnie
# klik maksymalnych\
rcnt = 0
bron_kerbosch(sr, sp, sx)
print()
print("RECURSIVE CALLS =",rcnt)
print()
# Usuwamy zbiory
del sr,sp,sx,sn
# Usuwamy tablicę
# list sąsiedztwa
for v in range(n):
while graf[v]:
graf[v] = graf[v].next
del graf
|
| Wynik: | |
8 19 0 1 0 2 0 5 0 6 1 2 1 3 1 5 1 6 2 4 2 5 2 6 2 7 3 4 3 6 3 7 4 6 5 6 5 7 6 7 MAXIMAL CLIQUE: 0 1 2 5 6 MAXIMAL CLIQUE: 1 3 6 MAXIMAL CLIQUE: 2 4 6 MAXIMAL CLIQUE: 2 5 6 7 MAXIMAL CLIQUE: 3 4 6 MAXIMAL CLIQUE: 3 6 7 RECURSIVE CALLS = 52 |
![]() |
Algorytm Brona-Kerboscha da się ulepszyć, tak aby wykonywał mniej wywołań
rekurencyjnych. W tym celu przed wejściem do pętli rozwijającej zbiór P tworzymy
sumę zbiorów
K01: Jeśli (P jest pusty)(X jest pusty), to pisz R ; znaleziona klika maksymalna i zakończ K02: P" ← suma zbiorów P i X ; będziemy szukać piwota ; w sumie zbiorów P i X K03: ncmax ← 0 ; zerujemy globalny licznik sąsiadów K04: Dla każdego wierzchołka u w P" : wykonuj kroki K05…K07 ; przeglądamy graf, ; biorąc pod uwagę tylko wierzchołki w P K05: nc ← 0 ; zerujemy bieżący licznik sąsiadów K06: Dla każdego sąsiada w wierzchołka u: wykonuj: Jeśli w należy do P, ; jeśli sąsiad jest w P, to nc ← nc + 1 ; to zwiększamy bieżący licznik sąsiadów K07: Jeśli nc ≥ ncmax, ; jeśli wierzchołek u ma nie mniej ; sąsiadów niż jest w ncmax, to to: v ← u ; zapamiętujemy ten wierzchołek i ncmax ← nc ; i zapamiętujemy liczbę jego sąsiadów K08: P" ← P K09: Dla każdego sąsiada u wierzchołka v: wykonuj: P" ← P"\u ; tworzymy zbiór P bez sąsiadów piwota K10: Dla każdego wierzchołka v w P": wykonuj kroki K11…K18 K11: Zeruj zbiór N K12: Dla każdego sąsiada u wierzchołka v: wykonuj: N ← N+u ; tworzymy zbiór sąsiadów K13: R' ← R+v ; w R' tworzymy zbiór R z dodanym wierzchołkiem v K14: P' ← iloczyn zbiorów P i N ; z P' usuwamy wierzchołki, ; które nie są sąsiadami v K15: X' ← iloczyn zbiorów X i N ; z X' usuwamy wierzchołki, ; które nie są sąsiadami v K16: BronKerbosch(n,graf,R',P',X') ; wywołanie rekurencyjne K17: P ← P \ v ; ze zbioru P usuwamy wierzchołek v K18: R ← R + v ; do zbioru R dodajemy wierzchołek v K19: Zakończ
|
Uwaga: Zanim uruchomisz program, przeczytaj wstęp do tego artykułu, w którym wyjaśniamy funkcje tych programów oraz sposób korzystania z nich. |
| Program odczytuje
definicję
grafu nieskierowanego i wyszukuje w nim wszystkie kliki maksymalne
za pomocą algorytmu Brona-Kerboscha. Zbiory zostały zaimplementowane
w tablicy logicznej. Program dodatkowo wypisuje liczbę wywołań rekurencyjnych algorytmu Brona-Kerboscha dla celów porównawczych. |
![]() |
8 19 0 1 0 2 0 5 0 6 1 2 1 3 1 5 1 6 2 4 2 5 2 6 2 7 3 4 3 6 3 7 4 6 5 6 5 7 6 7 |
Pascal// Algorytm Brona-Kerboscha
// z piwotem
// Data: 15.07.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------
program cliques;
// Definicja elementu
// listy sąsiedztwa
type
PSLel = ^SLel;
SLel = record
// Następny element listy
next : PSLel;
// Wierzchołek docelowy
v : integer;
end;
// Definicja struktury zbioru
SSet = record
vmin : integer;
nmax : integer;
T : array of boolean;
end;
// Zmienne globalne
var
// Liczba wierzchołków
// i krawędzi grafu
n, m : integer;
// Tablica list sąsiedztwa
graf : array of PSLel;
// Zbiór sąsiadów
SN : SSet;
// Licznik wywołań
// rekurencyjnych
rcnt : integer;
// Obsługa zbiorów
//----------------
// Procedura dodaje
// element x do zbioru A
//----------------------
procedure s_add(var A : SSet;
x : integer);
var
i : integer;
begin
// Obliczamy indeks
i := x - A.vmin;
// Ustawiamy element
A.T[i] := true;
end;
// Procedura usuwa
// element x ze zbioru
//--------------------
procedure s_remove(var A : SSet;
x : integer);
var
i : integer;
begin
// Obliczamy indeks
i := x - A.vmin;
// Zerujemy element
A.T[i] := false;
end;
// Procedura usuwa
// wszystkie elementy
// ze zbioru
//-------------------
procedure s_clear(var A : SSet);
var
i : integer;
begin
// Zerujemy tablicę
for i := 0 to A.nmax do
A.T[i] := false;
end;
// Procedura tworzy zbiór
//-----------------------
procedure s_create(var A : SSet;
vmin, vmax : integer);
begin
// Wypełniamy
// strukturę danymi
A.vmin := vmin;
// Indeks ostatniego
// elementu tablicy
A.nmax := vmax - vmin;
// Tworzymy dynamicznie
// tablicę logiczną
SetLength(A.T, A.nmax + 1);
// Tworzymy zbiór pusty
s_clear(A);
end;
// Procedura wyznacza
// sumę zbiorów A i B
//-------------------
procedure s_union
(var A, B, C : SSet);
var
i : integer;
begin
for i := 0 to A.nmax do
C.T[i] := A.T[i] or B.T[i];
end;
// Procedura wyznacza
// część wspólną zbiorów A i B
//----------------------------
procedure s_intersection
(var A, B, C :SSet);
var
i : integer;
begin
for i := 0 to A.nmax do
C.T[i] := A.T[i] and B.T[i];
end;
// Procedura kopiuje
// zbiór A do zbioru B
//--------------------
procedure s_copy(var A, B : SSet);
var
i : integer;
begin
for i := 0 to A.nmax do
B.T[i] := A.T[i];
end;
// Procedura ustawia
// zbiór pełny
procedure s_full(var A : SSet);
var
i : integer;
begin
for i := 0 to A.nmax do
A.T[i] := true;
end;
// Funkcja sprawdza,
// czy zbiór A jest pusty
// true - jest
// false - nie jest
//-----------------------
function s_empty(var A : SSet)
: boolean;
var
i : integer;
m : boolean;
begin
// Pobieramy bity do m
m := A.T[0];
for i := 1 to A.nmax do
// Sumujemy logicznie bity
m := m or A.T[i];
s_empty := (m = false);
end;
// Funkcja bada,
// czy element x
// należy do zbioru A
// true - tak, należy
// false - nie, nie należy
//------------------------
function s_isin(var A : SSet;
x : integer)
: boolean;
var
i : integer;
begin
// Obliczamy indeks
i := x - A.vmin;
s_isin := A.T[i]
end;
// Procedura wyświetla
// elementy zbioru A
//--------------------
procedure s_print(var A : SSet);
var
i : integer;
begin
for i := 0 to A.nmax do
if A.T[i] then
write(i + A.vmin,' ');
end;
// Procedura wyszukuje
// kliki maksymalne
//--------------------
procedure BronKerbosch
(var SR, SP, SX : SSet);
var
RP, PP, XP, PPP : SSet;
p : PSLel;
v, u, ncmax, nc : integer;
begin
// Zwiększamy licznik
// wywołań rekurencyjnych
inc(rcnt);
// Jeśli zbiory SP i SX
// są puste, to SR zawiera
// wierzchołki kliki
// maksymalnej
if s_empty(SP) and
s_empty(SX) then
begin
write ('MAXIMAL CLIQUE: ');
s_print(SR);
writeln;
end
else
begin
// Tworzymy zbiór
// pomocniczy PPP
s_create(PPP, 0, n-1);
// W PPP tworzymy sumę
// zbiorów SP i SX
s_union(SP, SX, PPP);
// Zerujemy licznik
// sąsiadów
ncmax := 0;
// Przechodzimy przez
// kolejne wierzchołki
// grafu
for u := 0 to n-1 do
// Jeśli wierzchołek
// jest w PPP, to
// przetwarzamy go
if s_isin(PPP, u) then
begin
// Zerujemy bieżący
// licznik sąsiadów
nc := 0;
// Badamy sąsiadów
// wierzchołka v
p := graf[u];
while p <> nil do
begin
// Jeśli sąsiad w SP,
// zwiększamy nc
if s_isin(SP, p^.v) then
inc(nc);
// Następny sąsiad
p := p^.next;
end;
// Jeśli liczba sąsiadów w P
// jest większa od ncmax,
if nc >= ncmax then
begin
// to zapamiętujemy
// wierzchołek
v := u;
// oraz jego liczbę
// sąsiadów w P
ncmax := nc;
end;
end;
// W PPP umieszczamy zbiór SP
s_copy(SP, PPP);
p := graf[v];
// Przeglądamy listę
// sąsiadów piwota
while p <> nil do
begin
// Z PPP usuwamy każdego
// sąsiada piwota
s_remove(PPP, p^.v);
// Następny sąsiad
p := p^.next;
end;
// Tworzymy puste
// zbiory pomocnicze
s_create(RP, 0, n-1);
s_create(PP, 0, n-1);
s_create(XP, 0, n-1);
// Przechodzimy przez
// kolejne wierzchołki grafu
for v := 0 to n-1 do
// Jeśli wierzchołek v
// jest w zbiorze PPP,
// to go przetwarzamy
if s_isin(PPP, v) then
begin
// Najpierw zerujemy
// zbiór sąsiadów
s_clear(SN);
// Przeglądamy listę
// sąsiedztwa wierzchołka v
p := graf[v];
while p <> nil do
begin
// Każdego sąsiada
// umieszczamy w SN
s_add(SN, p^.v);
p := p^.next;
end;
// W RP tworzymy zbiór SR
// z dodanym wierzchołkiem v
s_copy(SR, RP);
s_add(RP, v);
// W PP tworzymy
// iloczyn SP i SN
s_intersection(SP, SN, PP);
// W XP tworzymy
// iloczyn SX i SN
s_intersection(SX, SN, XP);
// Wywołanie rekurencyjne
BronKerbosch(RP, PP, XP);
// Z SP usuwamy
// wierzchołek v
s_remove(SP, v);
// i dodajemy go do SX
s_add(SX, v);
end;
// Usuwamy zbiory pomocnicze
SetLength(RP.T, 0);
SetLength(PP.T, 0);
SetLength(XP.T, 0);
SetLength(PPP.T, 0);
end;
end;
// **********************
// *** PROGRAM GŁÓWNY ***
// **********************
var
SR, SP, SX : SSet;
i, u, v : integer;
p, r : PSLel;
begin
// Odczytujemy liczbę
// wierzchołków i krawędzi
read(n, m);
// Tworzymy puste zbiory
s_create(SR, 0, n-1);
s_create(SP, 0, n-1);
s_create(SX, 0, n-1);
s_create(SN, 0, n-1);
// Tworzymy tablicę
// list sąsiedztwa
SetLength(graf, n);
// Zerujemy listy sąsiedztwa
for i := 0 to n-1 do
graf[i] := nil;
for i := 1 to m do
begin
// Wierzchołki krawędzi
read(u, v);
// Tworzymy element listy
new(p);
p^.v := u;
// Element dołączamy
// do listy sąsiadów v
p^.next := graf[v];
graf[v] := p;
// To samo dla krawędzi
// w drugą stronę
new(p);
p^.v := v;
// Element dołączamy
// do listy sąsiadów u
p^.next := graf[u];
graf[u] := p;
end;
// W zbiorze SP ustawiamy
// wszystkie wierzchołki
s_full(SP);
writeln;
// Zerujemy licznik
// wywołań rekurencyjnych
rcnt := 0;
// Szukamy rekurencyjnie
// klik maksymalnych
BronKerbosch(SR, SP, SX);
writeln;
writeln('RECURSIVE CALLS = ',
rcnt);
writeln;
// Usuwamy zbiory
SetLength(SR.T, 0);
SetLength(SP.T, 0);
SetLength(SX.T, 0);
SetLength(SN.T, 0);
// Usuwamy tablicę
// list sąsiedztwa
for v := 0 to n-1 do
begin
p := graf[v];
while p <> nil do
begin
r := p;
p := p^.next;
dispose(r);
end;
end;
SetLength(graf, 0);
end.
|
// Algorytm Brona-Kerboscha
// z piwotem
// Data: 15.07.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------
#include <iostream>
#include <iomanip>
using namespace std;
// Definicja elementu
// listy sąsiedztwa
struct SLel
{
// Następny element listy
SLel *next;
// Wierzchołek docelowy
int v;
};
// Definicja struktury zbioru
struct Set
{
int vmin, nmax;
bool *T;
};
// Zmienne globalne
//-----------------
// Liczba wierzchołków
// i krawędzi grafu
int n, m;
// Tablica list sąsiedztwa
SLel ** graf;
// Zbiór sąsiadów
Set SN;
// Licznik wywołań
// rekurencyjnych
int rcnt = 0;
// Obsługa zbiorów
//----------------
// Procedura dodaje
// element x do zbioru A
//----------------------
void s_add(Set & A, int x)
{
// Obliczamy indeks
int i = x - A.vmin;
// Ustawiamy bit na 1
A.T[i] = true;
}
// Procedura usuwa
// element x ze zbioru
//--------------------
void s_remove(Set & A, int x)
{
// Obliczamy indeks
int i = x - A.vmin;
// Ustawiamy bit na 0
A.T[i] = false;
}
// Procedura usuwa wszystkie
// elementy ze zbioru
//--------------------------
void s_clear(Set & A)
{
// Zerujemy tablicę
for(int i = 0; i <= A.nmax; i++)
A.T[i] = false;
}
// Procedura tworzy zbiór
//-----------------------
void s_create(Set & A,
int vmin,
int vmax)
{
// Wypełniamy strukturę danymi
A.vmin = vmin;
// Indeks ostatniego
// elementu tablicy
A.nmax = vmax - vmin;
// Tworzymy dynamicznie
// tablicę logiczną
A.T = new bool [A.nmax+1];
// Tworzymy zbiór pusty
s_clear(A);
}
// Procedura wyznacza
// sumę zbiorów A i B
//-------------------
void s_union(Set & A,
Set & B,
Set & C)
{
for(int i = 0; i <= A.nmax; i++)
C.T[i] = A.T[i] || B.T[i];
}
// Procedura wyznacza część
// wspólną zbiorów A i B
//-------------------------
void s_intersection(Set & A,
Set & B,
Set & C)
{
for(int i = 0; i <= A.nmax; i++)
C.T[i] = A.T[i] && B.T[i];
}
// Procedura kopiuje
// zbiór A do zbioru B
//--------------------
void s_copy(Set & A, Set & B)
{
for(int i = 0; i <= A.nmax; i++)
B.T[i] = A.T[i];
}
// Procedura ustawia
// zbiór pełny
//-------------------
void s_full(Set & A)
{
for(int i = 0; i <= A.nmax; i++)
A.T[i] = true;
}
// Funkcja sprawdza,
// czy zbiór A jest pusty
// true - tak, jest pusty
// false - nie, nie jest pusty
//----------------------------
bool s_empty(Set A)
{
// Pobieramy bity do m
bool m = A.T[0];
// Sumujemy logicznie bity
for(int i = 1; i <= A.nmax; i++)
m = m || A.T[i];
return !m;
}
// Funkcja bada, czy element
// x należy do zbioru A
// true - tak, należy
// false - nie, nie należy
//--------------------------
bool s_isin(Set & A, int x)
{
// Obliczamy indeks
int i = x - A.vmin;
return A.T[i];
}
// Procedura wyświetla
// elementy zbioru A
//--------------------
void s_print(Set A)
{
int i;
for(i = 0; i <= A.nmax; i++)
if(A.T[i])
cout << i + A.vmin << " ";
}
// Procedura wyszukuje
// kliki maksymalne
//--------------------
void BronKerbosch(Set & SR,
Set & SP,
Set & SX)
{
Set RP, PP, XP, PPP;
SLel * p;
int v, u, ncmax, nc;
// Zwiększamy licznik
// wywołań rekurencyjnych
rcnt++;
// Jeśli zbiory SP i SX są puste,
// to SR zawiera wierzchołki
// kliki maksymalnej
if(s_empty(SP) &&
s_empty (SX))
{
cout << "MAXIMAL CLIQUE: ";
s_print(SR);
cout << endl;
}
else
{
// Tworzymy zbiór
// pomocniczy PPP
s_create(PPP, 0, n-1);
// W PPP tworzymy sumę
// zbiorów SP i SX
s_union(SP, SX, PPP);
// Zerujemy licznik sąsiadów
ncmax = 0;
// Przechodzimy przez
// kolejne wierzchołki grafu
for(u = 0; u < n; u++)
// Jeśli wierzchołek
// jest w PPP, to
// przetwarzamy go
if(s_isin(PPP, u))
{
// Zerujemy bieżący
// licznik sąsiadów
nc = 0;
// Badamy sąsiadów
// wierzchołka v
for(p = graf[u];
p; p = p->next)
// Jeśli sąsiad w SP,
// zwiększamy nc
if(s_isin(SP, p->v))
nc++;
// Jeśli liczba sąsiadów
// w P jest większa
// od ncmax,
if(nc >= ncmax)
{
// to zapamiętujemy
// wierzchołek
v = u;
// oraz jego liczbę
// sąsiadów w P
ncmax = nc;
}
}
// W PPP umieszczamy zbiór SP
s_copy(SP, PPP);
// Przeglądamy listę
// sąsiadów piwota
for(p = graf[v]; p; p = p->next)
// Z PPP usuwamy każdego
// sąsiada piwota
s_remove(PPP, p->v);
// Tworzymy puste
// zbiory pomocnicze
s_create(RP, 0, n-1);
s_create(PP, 0, n-1);
s_create(XP, 0, n-1);
// Przechodzimy przez kolejne
// wierzchołki grafu
for(v = 0; v < n; v++)
// Jeśli wierzchołek v
// jest w zbiorze PPP, to
// go przetwarzamy
if(s_isin(PPP, v))
{
// Najpierw zerujemy
// zbiór sąsiadów
s_clear(SN);
// Przeglądamy listę
// sąsiedztwa wierzchołka v
for(p = graf[v];
p; p = p->next)
// Każdego sąsiada
// umieszczamy w SN
s_add(SN, p->v);
// W RP tworzymy zbiór SR
// z dodanym wierzchołkiem v
s_copy(SR, RP);
s_add(RP, v);
// W PP tworzymy
// iloczyn SP i SN
s_intersection(SP, SN, PP);
// W XP tworzymy
// iloczyn SX i SN
s_intersection(SX, SN, XP);
// Wywołanie rekurencyjne
BronKerbosch(RP, PP, XP);
// Z SP usuwamy
// wierzchołek v
s_remove(SP, v);
// i dodajemy go do SX
s_add(SX, v);
}
// Usuwamy zbiory pomocnicze
delete[] RP.T;
delete[] PP.T;
delete[] XP.T;
delete[] PPP.T;
}
}
// **********************
// *** PROGRAM GŁÓWNY ***
// **********************
int main()
{
Set SR, SP, SX;
int i, u, v;
SLel *p, *r;
// Odczytujemy liczbę
// wierzchołków i krawędzi
cin >> n >> m;
// Tworzymy puste zbiory
s_create(SR, 0, n-1);
s_create(SP, 0, n-1);
s_create(SX, 0, n-1);
s_create(SN, 0, n-1);
// Tworzymy tablicę
// list sąsiedztwa
graf = new SLel * [n];
// Zerujemy listy sąsiedztwa
for(i = 0; i < n; i++)
graf[i] = nullptr;
for(i = 0; i < m; i++)
{
// Wierzchołki krawędzi
cin >> u >> v;
// Tworzymy element listy
p = new SLel;
p->v = u;
// Element dołączamy
// do listy sąsiadów v
p->next = graf[v];
graf[v] = p;
// To samo dla krawędzi
// w drugą stronę
p = new SLel;
p->v = v;
// Element dołączamy
// do listy sąsiadów u
p->next = graf[u];
graf[u] = p;
}
// W zbiorze SP ustawiamy
// wszystkie wierzchołki
s_full(SP);
cout << endl;
// Szukamy rekurencyjnie
// klik maksymalnych
BronKerbosch(SR, SP, SX);
cout << endl
<< "RECURSIVE CALLS = "
<< rcnt << endl << endl;
// Usuwamy zbiory
delete [] SR.T;
delete [] SP.T;
delete [] SX.T;
delete [] SN.T;
// Usuwamy tablicę
// list sąsiedztwa
for(v = 0; v < n; v++)
{
p = graf[v];
while(p)
{
r = p;
p = p->next;
delete r;
}
}
delete [] graf;
return 0;
}
|
Basic' Algorytm Brona-Kerboscha
' z piwotem
' Data: 15.07.2014
' (C)2014 mgr Jerzy Wałaszek
'---------------------------
' Definicja elementu
' listy sąsiedztwa
Type SLel
' Następny element listy
next As SLel Ptr
' Wierzchołek docelowy
v As Integer
End Type
' Definicja struktury zbioru
Type Set
vmin As Integer
nmax As Integer
T As Boolean Ptr
End Type
' Zmienne globalne
'-----------------
' Liczba wierzchołków
' i krawędzi grafu
Dim Shared As Integer n, m
' Tablica list sąsiedztwa
Dim Shared As SLel Ptr Ptr graf
' Zbiór sąsiadów
Dim Shared As Set SN
' Licznik wywołań rekurencyjnych
Dim Shared As Integer rcnt = 0
' Obsługa zbiorów
'----------------
' Procedura dodaje
' element x do zbioru A
'----------------------
Sub s_add(ByRef A As Set, _
ByVal x As Integer)
Dim As Integer i
' Obliczamy indeks
i = x - A.vmin
' Ustawiamy bit na 1
A.T[i] = True
End Sub
' Procedura usuwa
' element x ze zbioru
'--------------------
Sub s_remove(ByRef A As Set, _
ByVal x As Integer)
Dim As Integer i
' Obliczamy indeks
i = x - A.vmin
' Ustawiamy bit na 0
A.T[i] = False
End Sub
' Procedura usuwa
' wszystkie elementy
' ze zbioru
'-------------------
Sub s_clear(ByRef A As Set)
Dim As Integer i
' Zerujemy tablicę
For i = 0 To A.nmax
A.T[i] = False
Next
End Sub
' Procedura tworzy zbiór
'-----------------------
Sub s_create(ByRef A As Set, _
ByVal vmin As Integer, _
ByVal vmax As Integer)
' Wypełniamy strukturę danymi
A.vmin = vmin
' Indeks ostatniego
' elementu tablicy
A.nmax = vmax - vmin
' Tworzymy dynamicznie
' tablicę mapy bitowej
A.T = New Boolean [A.nmax + 1]
' Tworzymy zbiór pusty
s_clear(A)
End Sub
' Procedura wyznacza
' sumę zbiorów A i B
'-------------------
Sub s_union(ByRef A As Set, _
ByRef B As Set, _
ByRef C As Set)
Dim As Integer i
For i = 0 To A.nmax
C.T[i] = A.T[i] Or B.T[i]
Next
End Sub
' Procedura wyznacza
' część wspólną
' zbiorów A i B
'-------------------
Sub s_intersection(ByRef A As Set, _
ByRef B As Set, _
ByRef C As Set)
Dim As Integer i
For i = 0 To A.nmax
C.T[i] = A.T[i] And B.T[i]
Next
End Sub
' Procedura kopiuje
' zbiór A do zbioru B
'--------------------
Sub s_copy(ByRef A As Set, _
ByRef B As Set)
Dim As Integer i
For i = 0 To A.nmax
B.T[i] = A.T[i]
Next
End Sub
' Procedura ustawia
' zbiór pełny
'------------------
Sub s_full(ByRef A As Set)
Dim As Integer i
For i = 0 To A.nmax
A.T[i] = True
Next
End Sub
' Funkcja sprawdza,
' czy zbiór A jest pusty
' true - tak, jest pusty
' false - nie, nie jest pusty
'----------------------------
Function s_empty(ByRef A As Set) _
As Boolean
Dim As Integer i
' Pobieramy bity do m
Dim As Boolean m = A.T[0]
For i = 1 To A.nmax
' Sumujemy logicznie bity
m = m Or A.T[i]
Next
Return Not m
End Function
' Funkcja bada,
' czy element x
' należy do zbioru A
' true - tak, należy
' false - nie, nie należy
'------------------------
Function s_isin(ByRef A As Set, _
ByVal x As Integer) _
As Boolean
' Obliczamy indeks
Dim As Integer i = x - A.vmin
Return A.T[i]
End Function
' Procedura wyświetla
' elementy zbioru A
'--------------------
Sub s_print(ByRef A As Set)
Dim As Integer i
For i = 0 To A.nmax
If A.T[i] Then _
Print Using "& ";i + A.vmin;
Next
End Sub
' Procedura wyszukuje
' kliki maksymalne
'--------------------
Sub BronKerbosch(ByRef SR As Set, _
ByRef SP As Set, _
ByRef SX As Set)
Dim As Set RP, PP, XP, PPP
Dim As SLel Ptr p
Dim As Integer v, u, ncmax, nc
' Zwiększamy licznik
' wywołań rekurencyjnych
rcnt += 1
' Jeśli zbiory SP i SX są puste,
' to SR zawiera wierzchołki
' kliki maksymalnej
If s_empty(SP) AndAlso _
s_empty(SX) Then
Print "MAXIMAL CLIQUE: ";
s_print(SR)
Print
Else
' Tworzymy zbiór
' pomocniczy PPP
s_create(PPP, 0, n-1)
' W PPP tworzymy sumę
' zbiorów SP i SX
s_union(SP, SX, PPP)
' Zerujemy
' licznik sąsiadów
ncmax = 0
' Przechodzimy przez
' kolejne wierzchołki grafu
For u = 0 To n-1
' Jeśli wierzchołek
' jest w PPP,
' to przetwarzamy go
If s_isin(PPP, u) Then
' Zerujemy bieżący
' licznik sąsiadów
nc = 0
p = graf[u]
' Badamy sąsiadów
' wierzchołka v
While p <> 0
If s_isin(SP, p->v) Then
' Jeśli sąsiad w SP,
' zwiększamy nc
nc += 1
End If
' Następny sąsiad
p = p->next
Wend
' Jeśli liczba sąsiadów
' w P jest większa od ncmax,
If nc >= ncmax Then
' to zapamiętujemy
' wierzchołek
v = u
' oraz jego liczbę
' sąsiadów w P
ncmax = nc
End If
End If
Next
' W PPP umieszczamy zbiór SP
s_copy(SP, PPP)
p = graf[v]
' Przeglądamy listę
' sąsiadów piwota
While p <> 0
' Z PPP usuwamy każdego
' sąsiada piwota
s_remove(PPP, p->v)
' Następny sąsiad
p = p->next
Wend
' Tworzymy puste
' zbiory pomocnicze
s_create(RP, 0, n-1)
s_create(PP, 0, n-1)
s_create(XP, 0, n-1)
' Przechodzimy przez
' kolejne wierzchołki grafu
For v = 0 To n-1
' Jeśli wierzchołek v
' jest w zbiorze PPP, to
' go przetwarzamy
If s_isin(PPP, v) Then
' Najpierw zerujemy
' zbiór sąsiadów
s_clear(SN)
' Przeglądamy listę
' sąsiedztwa wierzchołka v
p = graf[v]
While p <> 0
' Każdego sąsiada
' umieszczamy w SN
s_add(SN, p->v)
p = p->next
Wend
' W RP tworzymy
' zbiór SR z dodanym
' wierzchołkiem v
s_copy(SR, RP)
s_add(RP, v)
' W PP tworzymy
' iloczyn SP i SN
s_intersection(SP, SN, PP)
' W XP tworzymy
' iloczyn SX i SN
s_intersection(SX, SN, XP)
' Wywołanie rekurencyjne
BronKerbosch(RP, PP, XP)
' Z SP usuwamy wierzchołek v
s_remove(SP, v)
' i dodajemy go do SX
s_add(SX, v)
End If
Next
' Usuwamy zbiory pomocnicze
Delete [] RP.T
Delete [] PP.T
Delete [] XP.T
Delete [] PPP.T
End If
End Sub
' **********************
' *** PROGRAM GŁÓWNY ***
' **********************
Dim As Set SR, SP, SX
Dim As integer i, u, v
Dim As SLel Ptr p, r
Open Cons For Input As #1
' Odczytujemy liczbę
' wierzchołków i krawędzi
Input #1, n, m
' Tworzymy puste zbiory
s_create(SR, 0, n-1)
s_create(SP, 0, n-1)
s_create(SX, 0, n-1)
s_create(SN, 0, n-1)
' Tworzymy tablicę
' list sąsiedztwa
graf = New SLel Ptr [n]
For i = 0 To n-1
' Zerujemy listy sąsiedztwa
graf[i] = 0
Next
For i = 1 To m
' Wierzchołki krawędzi
Input #1, u, v
' Tworzymy element listy
p = New SLel
p->v = u
' Element dołączamy
' do listy sąsiadów v
p->next = graf[v]
graf[v] = p
' To samo dla krawędzi
' w drugą stronę
p = New SLel
p->v = v
' Element dołączamy
' do listy sąsiadów u
p->next = graf[u]
graf[u] = p
Next
Close #1
' W zbiorze SP ustawiamy
' wszystkie wierzchołki
s_full(SP)
Print
' Szukamy rekurencyjnie
' klik maksymalnych
BronKerbosch(SR, SP, SX)
Print
Print "RECURSIVE CALLS =";rcnt
Print
' Usuwamy zbiory
Delete [] SR.T
Delete [] SP.T
Delete [] SX.T
Delete [] SN.T
' Usuwamy tablicę
' list sąsiedztwa
For v = 0 To n-1
p = graf[v]
While p <> 0
r = p
p = p->next
Delete r
Wend
Next
Delete [] graf
End
|
Python
(dodatek)# Algorytm Brona-Kerboscha
# z piwotem
# Data: 26.03.2025
# (C)2025 mgr Jerzy Wałaszek
#---------------------------
# Definicja elementu
# listy sąsiedztwa
class SLel:
def __init__(self):
# Następny element listy
self.next = None
# Wierzchołek docelowy
self.v = 0
# Definicja struktury zbioru
class Set:
def __init__(self):
self.vmin = 0
self.nmax = 0
self.t = []
# Obsługa zbiorów
#----------------
# Procedura dodaje
# element x do zbioru A
def s_add(a,x):
# Obliczamy indeks
i = x - a.vmin
# Ustawiamy komórkę
a.t[i] = True
## Procedura usuwa
# element x ze zbioru
def s_remove(a,x):
# Obliczamy indeks
i = x - a.vmin
# Zerujemy komórkę
a.t[i] = False
# Procedura usuwa
# wszystkie elementy
# ze zbioru
def s_clear(a):
# Zerujemy wszystkie komórki
a.t = [False] * (a.nmax + 1)
# Procedura tworzy zbiór
def s_create(a,vmin,vmax):
# Wypełniamy strukturę danymi
a.vmin = vmin
# Indeks ostatniego
# elementu tablicy
a.nmax = vmax - vmin
# Tworzymy dynamicznie
# tablicę mapy
a.t = [False] * (a.nmax + 1)
# Procedura wyznacza
# sumę zbiorów A i B
def s_union(a,b,c):
for i in range(a.nmax+1):
c.t[i] = a.t[i] or b.t[i]
# Procedura wyznacza
# część wspólną
# zbiorów A i B
def s_intersection(a,b,c):
for i in range(a.nmax+1):
c.t[i] = a.t[i] and b.t[i]
# Procedura kopiuje
# zbiór A do zbioru B
def s_copy(a,b):
b.t = a.t[:]
# Procedura ustawia
# zbiór pełny
def s_full(a):
a.t = [True] * (a.nmax + 1)
# Funkcja sprawdza,
# czy zbiór A jest pusty
# true - tak, jest pusty
# false - nie, nie jest pusty
def s_empty(a):
# sumujemy komórki
m = a.t[0]
for i in range(1,a.nmax+1):
m = m or a.t[i]
return not m
# Funkcja bada,
# czy element x
# należy do zbioru A
# true - tak, należy
# false - nie, nie należy
def s_isin(a,x):
# Obliczamy indeks
i = x - a.vmin
return a.t[i]
# Procedura wyświetla
# elementy zbioru A
def s_print(a):
for i in range(a.nmax+1):
if a.t[i]:
print(i + a.vmin,end=" ")
# Procedura wyszukuje
# kliki maksymalne
def bron_kerbosch(sr,sp,sx):
global rcnt,n,graf
rp = Set()
pp = Set()
xp = Set()
ppp = Set()
# Zwiększamy licznik
# wywołań rekurencyjnych
rcnt += 1
v = 0
# Jeśli zbiory SP i SX są puste,
# to SR zawiera wierzchołki
# kliki maksymalnej
if s_empty(sp) and s_empty(sx):
print("MAXIMAL CLIQUE:",end=" ")
s_print(sr)
print()
else:
# Tworzymy zbiór
# pomocniczy PPP
s_create(ppp, 0, n-1)
# W PPP tworzymy sumę
# zbiorów SP i SX
s_union(sp, sx, ppp)
# Zerujemy
# licznik sąsiadów
ncmax = 0
# Przechodzimy przez
# kolejne wierzchołki grafu
for u in range(n):
# Jeśli wierzchołek
# jest w PPP,
# to przetwarzamy go
if s_isin(ppp, u):
# Zerujemy bieżący
# licznik sąsiadów
nc = 0
p = graf[u]
# Badamy sąsiadów
# wierzchołka v
while p:
if s_isin(sp, p.v):
# Jeśli sąsiad
# w SP,
# zwiększamy nc
nc += 1
# Następny sąsiad
p = p.next
# Jeśli liczba sąsiadów
# w P jest większa
# od ncmax,
if nc >= ncmax:
# to zapamiętujemy
# wierzchołek
v = u
# oraz jego liczbę
# sąsiadów w P
ncmax = nc
# W PPP umieszczamy zbiór SP
s_copy(sp, ppp)
p = graf[v]
# Przeglądamy listę
# sąsiadów piwota
while p:
# Z PPP usuwamy każdego
# sąsiada piwota
s_remove(ppp, p.v)
# Następny sąsiad
p = p.next
# Tworzymy puste
# zbiory pomocnicze
s_create(rp, 0, n-1)
s_create(pp, 0, n-1)
s_create(xp, 0, n-1)
# Przechodzimy przez
# kolejne wierzchołki grafu
for v in range(n):
# Jeśli wierzchołek v
# jest w zbiorze PPP, to
# go przetwarzamy
if s_isin(ppp, v):
# Najpierw zerujemy
# zbiór sąsiadów
s_clear(sn)
# Przeglądamy listę
# sąsiedztwa
# wierzchołka v
p = graf[v]
while p:
# Każdego sąsiada
# umieszczamy w SN
s_add(sn, p.v)
p = p.next
# W RP tworzymy
# zbiór SR z dodanym
# wierzchołkiem v
s_copy(sr, rp)
s_add(rp, v)
# W PP tworzymy
# iloczyn SP i SN
s_intersection(sp,sn,pp)
# W XP tworzymy
# iloczyn SX i SN
s_intersection(sx,sn,xp)
# Wywołanie rekurencyjne
bron_kerbosch(rp,pp,xp)
# Z SP usuwamy
# wierzchołek v
s_remove(sp, v)
# i dodajemy go do SX
s_add(sx, v)
# Usuwamy zbiory pomocnicze
del rp.t, pp.t, xp.t, ppp.t
# **********************
# *** PROGRAM GŁÓWNY ***
# **********************
sr = Set()
sp = Set()
sx = Set()
sn = Set()
rcnt = 0
# Odczytujemy liczbę
# wierzchołków i krawędzi
x = input().split()
n = int(x[0])
m = int(x[1])
# Tworzymy puste zbiory
s_create(sr, 0, n-1)
s_create(sp, 0, n-1)
s_create(sx, 0, n-1)
s_create(sn, 0, n-1)
# Tworzymy tablicę
# list sąsiedztwa
graf = [None] * n
for i in range(m):
# Wierzchołki krawędzi
x = input().split()
u = int(x[0])
v = int(x[1])
# Tworzymy element listy
p = SLel()
p.v = u
# Element dołączamy
# do listy sąsiadów v
p.next = graf[v]
graf[v] = p
# To samo dla krawędzi
# w drugą stronę
p = SLel()
p.v = v
# Element dołączamy
# do listy sąsiadów u
p.next = graf[u]
graf[u] = p
# W zbiorze SP ustawiamy
# wszystkie wierzchołki
s_full(sp)
print()
# Szukamy rekurencyjnie
# klik maksymalnych
bron_kerbosch(sr, sp, sx)
print()
print("RECURSIVE CALLS =",rcnt)
print()
# Usuwamy zbiory
del sr.t, sp.t, sx.t, sn.t
# Usuwamy tablicę
# list sąsiedztwa
for v in range(n):
while graf[v]:
graf[v] = graf[v].next
del graf
|
| Wynik: | |
| 8 19 0 1 0 2 0 5 0 6 1 2 1 3 1 5 1 6 2 4 2 5 2 6 2 7 3 4 3 6 3 7 4 6 5 6 5 7 6 7 MAXIMAL CLIQUE: 2 4 6 MAXIMAL CLIQUE: 0 1 2 5 6 MAXIMAL CLIQUE: 2 5 6 7 MAXIMAL CLIQUE: 1 3 6 MAXIMAL CLIQUE: 3 4 6 MAXIMAL CLIQUE: 3 6 7 RECURSIVE CALLS = 12 |
![]() |
W pierwszej wersji liczba wywołań rekurencyjnych wynosiła 52. Tutaj mamy ich 12.
Algorytm Brona-Kerboscha da się łatwo przystosować do wyszukiwania kliki największej. W tym celu w tworzymy pusty zbiór RM, który będzie zapamiętywał klikę największą. Następnie w pierwszej części algorytmu Brona-Kerbosha, gdy zbiory P i X są zerowe, nie wypisujemy zbioru R, lecz sprawdzamy, czy jego liczebność jest większa od liczebności zbioru RM. Jeśli tak, to zbiór R kopiujemy do RM. Aby nie zliczać ciągle elementów zbiorów, można w osobnej zmiennej zapamiętywać ich liczebność. Gdy algorytm Brona-Kerboscha zakończy działanie, w zbiorze RM będzie największa klika grafu.
Przy pierwszym wywołaniu zbiór RM powinien być pusty, a rmsize powinno zawierać 0. Oba te elementy mogą być globalne, aby zaoszczędzić pamięć na przekazywanie parametrów.
K01: Jeśli (P nie jest pusty)(X nie jest pusty), ; znaleziona klika maksymalna to idź do kroku K05 K02: rms ← rozmiar R ; wyznaczamy liczbę wierzchołków w klice maksymalnej K03: Jeśli rms > rmsize, to ; sprawdzamy, czy obecna klika jest większa od pamiętanej. RM ← R ; Jeśli tak, to zapamiętaj klikę oraz jej nowy rozmiar rmsize ← rms K04: Zakończ K05: P" ← suma zbiorów P i X ; będziemy szukać piwota w sumie zbiorów P i X K06: ncmax ← 0 ; zerujemy globalny licznik sąsiadów K07: Dla każdego wierzchołka u w P": ; przeglądamy graf, wykonuj kroki K08…K10 ; biorąc pod uwagę tylko wierzchołki w P K08: nc ← 0 ; zerujemy bieżący licznik sąsiadów K09: Dla każdego sąsiada w wierzchołka u: wykonuj: Jeśli w należy do P, ; jeśli sąsiad jest w P, to nc ← nc + 1 ; to zwiększamy bieżący licznik sąsiadów K10: Jeśli nc ≥ ncmax, to ; jeśli wierzchołek u ma nie mniej sąsiadów niż jest w ncmax, v ← u ; to zapamiętujemy ten wierzchołek i zapamiętujemy liczbę jego sąsiadów ncmax ← nc K11: P" ← P K12: Dla każdego sąsiada u wierzchołka v: ; tworzymy zbiór P bez sąsiadów piwota wykonuj: P" ← P"-u K13: Dla każdego wierzchołka v w P": wykonuj K14…K21 K14: Zeruj zbiór N K15: Dla każdego sąsiada u wierzchołka v: ; tworzymy zbiór sąsiadów wykonuj: N ← N+u K16: R' ← R+v ; w R' tworzymy zbiór R z dodanym wierzchołkiem v K17: P' ← iloczyn zbiorów P i N ; z P' usuwamy wierzchołki, które nie są sąsiadami v K18: X' ← iloczyn zbiorów X i N ; z X' usuwamy wierzchołki, które nie są sąsiadami v K19: BronKerbosch(n,graf,R',P',X') ; wywołanie rekurencyjne K20: P ← P - v ; ze zbioru P usuwamy wierzchołek v K21: R ← R + v ; do zbioru R dodajemy wierzchołek v K22: Zakończ
|
Uwaga: Zanim uruchomisz program, przeczytaj wstęp do tego artykułu, w którym wyjaśniamy funkcje tych programów oraz sposób korzystania z nich. |
| Program odczytuje
definicję
grafu nieskierowanego i wyszukuje w nim wszystkie kliki maksymalne
za pomocą algorytmu Brona-Kerboscha. Zbiory zostały zaimplementowane
w mapach bitowych. Program dodatkowo wypisuje liczbę wywołań rekurencyjnych algorytmu Brona-Kerboscha dla celów porównawczych. |
![]() |
8 19 0 1 0 2 0 5 0 6 1 2 1 3 1 5 1 6 2 4 2 5 2 6 2 7 3 4 3 6 3 7 4 6 5 6 5 7 6 7 |
Pascal// Wyszukiwanie kliki największej
// Data: 15.07.2014
// (C)2014 mgr Jerzy Wałaszek
//-------------------------------
program cliques;
// Definicja elementu
// listy sąsiedztwa
type
PSLel = ^SLel;
SLel = record
// Następny element listy
next : PSLel;
// Wierzchołek docelowy
v : integer;
end;
// Definicja struktury zbioru
SSet = record
vmin : integer;
nmax : integer;
T : array of boolean;
end;
// Zmienne globalne
var
// Liczba wierzchołków
// i krawędzi grafu
n, m : integer;
// Tablica list sąsiedztwa
graf : array of PSLel;
// Zbiór sąsiadów
// i zbiór kliki największej
SN, RM : SSet;
// Liczba wierzchołków
// w klice największej
rmsize: integer;
// Obsługa zbiorów
// Procedura dodaje
// element x do zbioru A
//----------------------
procedure s_add(var A : SSet;
x : integer);
var
i : integer;
begin
// Obliczamy indeks
i := x - A.vmin;
// Ustawiamy element
A.T[i] := true;
end;
// Procedura usuwa
// element x ze zbioru
//--------------------
procedure s_remove(var A : SSet;
x : integer);
var
i : integer;
begin
// Obliczamy indeks
i := x - A.vmin;
// Zerujemy element
A.T[i] := false;
end;
// Procedura usuwa wszystkie
// elementy ze zbioru
//--------------------------
procedure s_clear(var A : SSet);
var
i : integer;
begin
// Zerujemy tablicę
for i := 0 to A.nmax do
A.T[i] := false;
end;
// Procedura tworzy zbiór
//-----------------------
procedure s_create(var A : SSet;
vmin, vmax : integer);
begin
// Wypełniamy
// strukturę danymi
A.vmin := vmin;
// Indeks ostatniego
// elementu tablicy
A.nmax := vmax - vmin;
// Tworzymy dynamicznie
// tablicę logiczną
SetLength(A.T, A.nmax + 1);
// Tworzymy zbiór pusty
s_clear(A);
end;
// Procedura wyznacza
// sumę zbiorów A i B
//-------------------
procedure s_union
(var A, B, C : SSet);
var
i : integer;
begin
for i := 0 to A.nmax do
C.T[i] := A.T[i] or B.T[i];
end;
// Procedura wyznacza
// część wspólną zbiorów A i B
//----------------------------
procedure s_intersection
(var A, B, C :SSet);
var
i : integer;
begin
for i := 0 to A.nmax do
C.T[i] := A.T[i] and B.T[i];
end;
// Procedura kopiuje
// zbiór A do zbioru B
//--------------------
procedure s_copy(var A, B : SSet);
var
i : integer;
begin
for i := 0 to A.nmax do
B.T[i] := A.T[i];
end;
// Procedura ustawia
// zbiór pełny
procedure s_full(var A : SSet);
var
i : integer;
begin
for i := 0 to A.nmax do
A.T[i] := true;
end;
// Funkcja sprawdza,
// czy zbiór A jest pusty
// true - jest
// false - nie jest
//-----------------------
function s_empty(var A : SSet)
: boolean;
var
i : integer;
m : boolean;
begin
// Pobieramy bity do m
m := A.T[0];
for i := 1 to A.nmax do
// Sumujemy logicznie bity
m := m or A.T[i];
s_empty := (m = false);
end;
// Funkcja bada,
// czy element x
// należy do zbioru A
// true - tak, należy
// false - nie, nie należy
//------------------------
function s_isin(var A : SSet;
x : integer)
: boolean;
var
i : integer;
begin
// Obliczamy indeks
i := x - A.vmin;
s_isin := A.T[i]
end;
// Procedura wyświetla
// elementy zbioru A
//--------------------
procedure s_print(var A : SSet);
var
i : integer;
begin
for i := 0 to A.nmax do
if A.T[i] then
write(i + A.vmin,' ');
end;
// Funkcja oblicza
// liczbę elementów w A
//---------------------
function s_size(var A : SSet)
: integer;
var
i, c : integer;
begin
// Zerujemy licznik
c := 0;
// Przechodzimy przez
// kolejne komórki
for i := 0 to A.nmax do
if A.T[i] then inc(c);
s_size := c;
end;
// Procedura wyszukuje
// klikę największą
//--------------------
procedure BronKerbosch
(var SR, SP, SX : SSet);
var
RP, PP, XP, PPP : SSet;
p : PSLel;
v, u, ncmax, nc, rms : integer;
begin
// Jeśli zbiory
// SP i SX są puste,
// to SR zawiera wierzchołki
// kliki maksymalnej
if s_empty(SP) and
s_empty(SX) then
begin
// Wyznaczamy liczbę
// wierzchołków w klice
rms := s_size(SR);
// Jeśli mamy większą klikę
// od dotychczas znalezionej,
if rms > rmsize then
begin
// to zapamiętujemy ją
s_copy(SR, RM);
// Zapamiętujemy również
// jej rozmiar
rmsize := rms;
end;
end
else
begin
// Tworzymy zbiór
// pomocniczy PPP
s_create(PPP, 0, n-1);
// W PPP tworzymy sumę
// zbiorów SP i SX
s_union(SP, SX, PPP);
// Zerujemy licznik
// sąsiadów
ncmax := 0;
// Przechodzimy przez
// kolejne wierzchołki
// grafu
for u := 0 to n-1 do
// Jeśli wierzchołek jest
// w PPP, to przetwarzamy go
if s_isin(PPP, u) then
begin
// Zerujemy bieżący
// licznik sąsiadów
nc := 0;
// Badamy sąsiadów
// wierzchołka v
p := graf[u];
while p <> nil do
begin
// Jeśli sąsiad w SP,
// zwiększamy nc
if s_isin(SP, p^.v) then
inc(nc);
// Następny sąsiad
p := p^.next;
end;
// Jeśli liczba sąsiadów
// w P jest większa
// od ncmax,
if nc >= ncmax then
begin
// to zapamiętujemy
// wierzchołek
v := u;
// oraz jego liczbę
// sąsiadów w P
ncmax := nc;
end;
end;
// W PPP umieszczamy
// zbiór SP
s_copy(SP, PPP);
p := graf[v];
// przeglądamy listę
// sąsiadów piwota
while p <> nil do
begin
s_remove(PPP, p^.v);
// Następny sąsiad
p := p^.next;
end;
// Tworzymy puste
// zbiory pomocnicze
s_create(RP, 0, n-1);
s_create(PP, 0, n-1);
s_create(XP, 0, n-1);
// Przechodzimy przez
// kolejne wierzchołki
// grafu
for v := 0 to n-1 do
// Jeśli wierzchołek v
// jest w zbiorze PPP, to
// go przetwarzamy
if s_isin(PPP, v) then
begin
// Najpierw zerujemy
// zbiór sąsiadów
s_clear(SN);
// Przeglądamy listę
// sąsiedztwa wierzchołka v
p := graf[v];
while p <> nil do
begin
// Każdego sąsiada
// umieszczamy w SN
s_add(SN, p^.v);
p := p^.next;
end;
// W RP tworzymy
// zbiór SR z dodanym
// wierzchołkiem v
s_copy(SR, RP);
s_add(RP, v);
// W PP tworzymy
// iloczyn SP i SN
s_intersection(SP, SN, PP);
// W XP tworzymy
// iloczyn SX i SN
s_intersection(SX, SN, XP);
// Wywołanie rekurencyjne
BronKerbosch(RP, PP, XP);
// Z SP usuwamy
// wierzchołek v
s_remove(SP, v);
// i dodajemy go do SX
s_add(SX, v);
end;
// Usuwamy zbiory pomocnicze
SetLength(RP.T, 0);
SetLength(PP.T, 0);
SetLength(XP.T, 0);
SetLength(PPP.T, 0);
end;
end;
// **********************
// *** PROGRAM GŁÓWNY ***
// **********************
var
SR, SP, SX : SSet;
i, u, v : integer;
p, r : PSLel;
begin
// Odczytujemy liczbę
// wierzchołków i krawędzi
read(n, m);
// Tworzymy puste zbiory
s_create(SR, 0, n-1);
s_create(SP, 0, n-1);
s_create(SX, 0, n-1);
s_create(SN, 0, n-1);
// Pusty zbiór
// kliki największej
s_create(RM, 0, n-1);
// Liczba wierzchołków
// w klice największej
rmsize := 0;
// Tworzymy tablicę
// list sąsiedztwa
SetLength(graf, n);
// Zerujemy listy sąsiedztwa
for i := 0 to n-1 do
graf[i] := nil;
for i := 1 to m do
begin
// Wierzchołki krawędzi
read(u, v);
// Tworzymy element listy
new(p);
p^.v := u;
// Element dołączamy
// do listy sąsiadów v
p^.next := graf[v];
graf[v] := p;
// To samo dla krawędzi
// w drugą stronę
new(p);
p^.v := v;
// Element dołączamy
// do listy sąsiadów u
p^.next := graf[u];
graf[u] := p;
end;
// W zbiorze SP ustawiamy
// wszystkie wierzchołki
s_full(SP);
writeln;
// Szukamy rekurencyjnie
// kliki największej
BronKerbosch(SR, SP, SX);
write('MAXIMUM CLIQUE: ');
s_print(RM);
writeln;
writeln('NUMBER OF VERTICES'+
' IN MAXIMUM CLIQUE = ',
rmsize);
writeln;
// Usuwamy zbiory
SetLength(SR.T, 0);
SetLength(SP.T, 0);
SetLength(SX.T, 0);
SetLength(SN.T, 0);
SetLength(RM.T, 0);
// Usuwamy tablicę
// list sąsiedztwa
for v := 0 to n-1 do
begin
p := graf[v];
while p <> nil do
begin
r := p;
p := p^.next;
dispose(r);
end;
end;
SetLength(graf, 0);
end.
|
// Wyszukiwanie kliki
// największej
// Data: 15.07.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------
#include <iostream>
#include <iomanip>
using namespace std;
// Definicja elementu
// listy sąsiedztwa
struct SLel
{
// Następny element listy
SLel *next;
// Wierzchołek docelowy
int v;
};
// Definicja
// struktury zbioru
struct Set
{
int vmin, nmax;
bool *T;
};
// Zmienne globalne
// Liczba wierzchołków
// i krawędzi grafu
int n, m;
// Tablica
// list sąsiedztwa
SLel ** graf;
// Zbiór sąsiadów
// i zbiór kliki największej
Set SN, RM;
// Liczba wierzchołków
// w klice największej
int rmsize;
// Obsługa zbiorów
//----------------
// Procedura dodaje
// element x do zbioru A
//----------------------
void s_add(Set & A, int x)
{
// Obliczamy indeks
int i = x - A.vmin;
// Ustawiamy bit na 1
A.T[i] = true;
}
// Procedura usuwa
// element x ze zbioru
//--------------------
void s_remove(Set & A, int x)
{
// Obliczamy indeks
int i = x - A.vmin;
// Ustawiamy bit na 0
A.T[i] = false;
}
// Procedura usuwa wszystkie
// elementy ze zbioru
//--------------------------
void s_clear(Set & A)
{
// Zerujemy tablicę
for(int i = 0; i <= A.nmax; i++)
A.T[i] = false;
}
// Procedura tworzy zbiór
//-----------------------
void s_create(Set & A,
int vmin,
int vmax)
{
// Wypełniamy strukturę danymi
A.vmin = vmin;
// Indeks ostatniego
// elementu tablicy
A.nmax = vmax - vmin;
// Tworzymy dynamicznie
// tablicę logiczną
A.T = new bool [A.nmax+1];
// Tworzymy zbiór pusty
s_clear(A);
}
// Procedura wyznacza
// sumę zbiorów A i B
//-------------------
void s_union(Set & A,
Set & B,
Set & C)
{
for(int i = 0; i <= A.nmax; i++)
C.T[i] = A.T[i] || B.T[i];
}
// Procedura wyznacza część
// wspólną zbiorów A i B
//-------------------------
void s_intersection(Set & A,
Set & B,
Set & C)
{
for(int i = 0; i <= A.nmax; i++)
C.T[i] = A.T[i] && B.T[i];
}
// Procedura kopiuje
// zbiór A do zbioru B
//--------------------
void s_copy(Set & A, Set & B)
{
for(int i = 0; i <= A.nmax; i++)
B.T[i] = A.T[i];
}
// Procedura ustawia
// zbiór pełny
//-------------------
void s_full(Set & A)
{
for(int i = 0; i <= A.nmax; i++)
A.T[i] = true;
}
// Funkcja sprawdza,
// czy zbiór A jest pusty
// true - tak, jest pusty
// false - nie, nie jest pusty
//----------------------------
bool s_empty(Set A)
{
// Pobieramy bity do m
bool m = A.T[0];
// Sumujemy logicznie bity
for(int i = 1; i <= A.nmax; i++)
m = m || A.T[i];
return !m;
}
// Funkcja bada, czy element
// x należy do zbioru A
// true - tak, należy
// false - nie, nie należy
//--------------------------
bool s_isin(Set & A, int x)
{
// Obliczamy indeks
int i = x - A.vmin;
return A.T[i];
}
// Procedura wyświetla
// elementy zbioru A
//--------------------
void s_print(Set A)
{
int i;
for(i = 0; i <= A.nmax; i++)
if(A.T[i])
cout << i + A.vmin << " ";
}
// Funkcja oblicza
// liczbę elementów w A
//---------------------
int s_size(Set & A)
{
// Zerujemy licznik
int c = 0;
// Przechodzimy przez
// kolejne komórki
for(int i = 0; i <= A.nmax; i++)
// Zliczamy bity równe 1
if(A.T[i]) c++;
return c;
}
// Procedura wyszukuje
// klikę największą
//--------------------
void BronKerbosch(Set & SR,
Set & SP,
Set & SX)
{
Set RP, PP, XP, PPP;
SLel * p;
int v, u, ncmax, nc, rms;
// Jeśli zbiory
// SP i SX są puste,
// to SR zawiera wierzchołki
// kliki maksymalnej
if(s_empty(SP) &&
s_empty(SX))
{
// Wyznaczamy liczbę
// wierzchołków w klice
rms = s_size(SR);
// Jeśli mamy większą
// klikę od dotychczas
// znalezionej,
if(rms > rmsize)
{
// to zapamiętujemy ją
s_copy(SR, RM);
// Zapamiętujemy
// również jej rozmiar
rmsize = rms;
}
}
else
{
// Tworzymy zbiór
// pomocniczy PPP
s_create(PPP, 0, n-1);
// W PPP tworzymy sumę
// zbiorów SP i SX
s_union(SP, SX, PPP);
// Zerujemy
// licznik sąsiadów
ncmax = 0;
// Przechodzimy przez
// kolejne wierzchołki
// grafu
for(u = 0; u < n; u++)
// Jeśli wierzchołek
// jest w PPP, to
// przetwarzamy go
if(s_isin(PPP, u))
{
// Zerujemy bieżący
// licznik sąsiadów
nc = 0;
// Badamy sąsiadów
// wierzchołka v
for(p = graf[u];
p; p = p->next)
// Jeśli sąsiad w SP,
// zwiększamy nc
if(s_isin(SP, p->v))
nc++;
// Jeśli liczba sąsiadów
// w P jest większa
// od ncmax,
if(nc >= ncmax)
{
// to zapamiętujemy
// wierzchołek
v = u;
// oraz liczbę jego
// sąsiadów w P
ncmax = nc;
}
}
// W PPP umieszczamy
// zbiór SP
s_copy(SP, PPP);
// przeglądamy listę
// sąsiadów piwota
for(p = graf[v];
p; p = p->next)
// Z PPP usuwamy każdego
// sąsiada piwota
s_remove(PPP, p->v);
// Tworzymy puste
// zbiory pomocnicze
s_create(RP, 0, n-1);
s_create(PP, 0, n-1);
s_create(XP, 0, n-1);
// Przechodzimy przez
// kolejne wierzchołki
// grafu
for(v = 0; v < n; v++)
// Jeśli wierzchołek v
// jest w zbiorze PPP, to
// go przetwarzamy
if(s_isin(PPP, v))
{
// Najpierw zerujemy
// zbiór sąsiadów
s_clear(SN);
// Przeglądamy listę
// sąsiedztwa
// wierzchołka v
for(p = graf[v];
p; p = p->next)
// Każdego sąsiada
// umieszczamy w SN
s_add(SN, p->v);
// W RP tworzymy zbiór SR
// z dodanym
// wierzchołkiem v
s_copy(SR, RP);
s_add(RP, v);
// W PP tworzymy
// iloczyn SP i SN
s_intersection(SP, SN, PP);
// W XP tworzymy
// iloczyn SX i SN
s_intersection(SX, SN, XP);
// Wywołanie rekurencyjne
BronKerbosch(RP, PP, XP);
// Z SP usuwamy
// wierzchołek v
s_remove(SP, v);
// i dodajemy
// go do SX
s_add(SX, v);
}
// Usuwamy zbiory pomocnicze
delete [] RP.T;
delete [] PP.T;
delete [] XP.T;
delete [] PPP.T;
}
}
// **********************
// *** PROGRAM GŁÓWNY ***
// **********************
int main()
{
Set SR, SP, SX;
int i, u, v;
SLel *p, *r;
// Odczytujemy liczbę
// wierzchołków i krawędzi
cin >> n >> m;
// Tworzymy puste zbiory
s_create(SR, 0, n-1);
s_create(SP, 0, n-1);
s_create(SX, 0, n-1);
s_create(SN, 0, n-1);
// Pusty zbiór
// kliki największej
s_create(RM, 0, n-1);
// Liczba wierzchołków
// w klice największej
rmsize = 0;
// Tworzymy tablicę
// list sąsiedztwa
graf = new SLel * [n];
// Zerujemy
// listy sąsiedztwa
for(i = 0; i < n; i++)
graf[i] = nullptr;
for(i = 0; i < m; i++)
{
// Wierzchołki krawędzi
cin >> u >> v;
// Tworzymy element listy
p = new SLel;
p->v = u;
// Element dołączamy
// do listy sąsiadów v
p->next = graf[v];
graf[v] = p;
// To samo dla krawędzi
// w drugą stronę
p = new SLel;
p->v = v;
// Element dołączamy
// do listy sąsiadów u
p->next = graf[u];
graf[u] = p;
}
// W zbiorze SP
// ustawiamy
// wszystkie wierzchołki
s_full(SP);
cout << endl;
// Szukamy rekurencyjnie
// kliki największej
BronKerbosch(SR, SP, SX);
cout << "MAXIMUM CLIQUE: ";
s_print(RM);
cout << endl
<< "NUMBER OF VERTICES"
" IN MAXIMUM CLIQUE = "
<< rmsize << endl << endl;
// Usuwamy zbiory
delete [] SR.T;
delete [] SP.T;
delete [] SX.T;
delete [] SN.T;
delete [] RM.T;
// Usuwamy tablicę
// list sąsiedztwa
for(v = 0; v < n; v++)
{
p = graf[v];
while(p)
{
r = p;
p = p->next;
delete r;
}
}
delete [] graf;
return 0;
}
|
Basic' Wyszukiwanie kliki największej
' Data: 15.07.2014
' (C)2014 mgr Jerzy Wałaszek
'-------------------------------
' Definicja elementu
' listy sąsiedztwa
Type SLel
' Następny element listy
next As SLel Ptr
' Wierzchołek docelowy
v As Integer
End Type
' Definicja struktury zbioru
Type Set
vmin As Integer
nmax As Integer
T As Boolean Ptr
End Type
' Zmienne globalne
' Liczba wierzchołków
' i krawędzi grafu
Dim Shared As Integer n, m
' Tablica list sąsiedztwa
Dim Shared As SLel Ptr Ptr graf
' Zbiór sąsiadów
' i zbiór kliki największej
Dim Shared As Set SN, RM
' Liczba wierzchołków
' w klice największej
Dim Shared As Integer rmsize
' Obsługa zbiorów
'----------------
' Procedura dodaje
' element x do zbioru A
'----------------------
Sub s_add(ByRef A As Set, _
ByVal x As Integer)
Dim As Integer i
' Obliczamy indeks
i = x - A.vmin
' Ustawiamy bit na 1
A.T[i] = True
End Sub
' Procedura usuwa
' element x ze zbioru
'--------------------
Sub s_remove(ByRef A As Set, _
ByVal x As Integer)
Dim As Integer i
' Obliczamy indeks
i = x - A.vmin
' Ustawiamy bit na 0
A.T[i] = False
End Sub
' Procedura usuwa
' wszystkie elementy
' ze zbioru
'-------------------
Sub s_clear(ByRef A As Set)
Dim As Integer i
' Zerujemy tablicę
For i = 0 To A.nmax
A.T[i] = False
Next
End Sub
' Procedura tworzy zbiór
'-----------------------
Sub s_create(_
ByRef A As Set, _
ByVal vmin As Integer,_
ByVal vmax As Integer)
' Wypełniamy strukturę danymi
A.vmin = vmin
' Indeks ostatniego
' elementu tablicy
A.nmax = vmax - vmin
' Tworzymy dynamicznie
' tablicę mapy bitowej
A.T = New Boolean [A.nmax + 1]
' Tworzymy zbiór pusty
s_clear(A)
End Sub
' Procedura wyznacza
' sumę zbiorów A i B
'-------------------
Sub s_union(ByRef A As Set,_
ByRef B As Set,_
ByRef C As Set)
Dim As Integer i
For i = 0 To A.nmax
C.T[i] = A.T[i] Or B.T[i]
Next
End Sub
' Procedura wyznacza
' część wspólną
' zbiorów A i B
'-------------------
Sub s_intersection(ByRef A As Set,_
ByRef B As Set,_
ByRef C As Set)
Dim As Integer i
For i = 0 To A.nmax
C.T[i] = A.T[i] And B.T[i]
Next
End Sub
' Procedura kopiuje
' zbiór A do zbioru B
'--------------------
Sub s_copy(ByRef A As Set, _
ByRef B As Set)
Dim As Integer i
For i = 0 To A.nmax
B.T[i] = A.T[i]
Next
End Sub
' Procedura ustawia
' zbiór pełny
'------------------
Sub s_full(ByRef A As Set)
Dim As Integer i
For i = 0 To A.nmax
A.T[i] = True
Next
End Sub
' Funkcja sprawdza,
' czy zbiór A jest pusty
' true - tak, jest pusty
' false - nie, nie jest pusty
'----------------------------
Function s_empty(ByRef A As Set) _
As Boolean
Dim As Integer i
' Pobieramy bity do m
Dim As Boolean m = A.T[0]
For i = 1 To A.nmax
' Sumujemy logicznie bity
m = m Or A.T[i]
Next
Return Not m
End Function
' Funkcja bada,
' czy element x
' należy do zbioru A
' true - tak, należy
' false - nie, nie należy
'------------------------
Function s_isin(_
ByRef A As Set, _
ByVal x As Integer)_
As Boolean
' Obliczamy indeks
Dim As Integer i = x - A.vmin
Return A.T[i]
End Function
' Procedura wyświetla
' elementy zbioru A
'--------------------
Sub s_print(ByRef A As Set)
Dim As Integer i
For i = 0 To A.nmax
If A.T[i] Then _
Print Using "& ";i + A.vmin;
Next
End Sub
' Funkcja oblicza
' liczbę elementów w A
'---------------------
Function s_size(ByRef A As Set)_
As Integer
' Zerujemy licznik
Dim As Integer i, c = 0
For i = 0 To A.nmax
If A.T[i] then c += 1
Next
Return c
End Function
' Procedura wyszukuje
' klikę największą
'--------------------
Sub BronKerbosch(ByRef SR As Set,_
ByRef SP As Set,_
ByRef SX As Set)
Dim As Set RP,PP,XP,PPP
Dim As SLel Ptr p
Dim As Integer v,u,ncmax,nc,rms
' Jeśli zbiory
' SP i SX są puste,
' to SR zawiera
' wierzchołki
' kliki maksymalnej
If s_empty(SP) AndAlso _
s_empty(SX) Then
' Wyznaczamy liczbę
' wierzchołków w klice
rms = s_size(SR)
' Jeśli mamy większą
' klikę od dotychczas
' znalezionej,
If rms > rmsize Then
' to zapamiętujemy ją
s_copy(SR, RM)
' Zapamiętujemy
' również jej rozmiar
rmsize = rms
End If
Else
' Tworzymy zbiór
' pomocniczy PPP
s_create(PPP, 0, n-1)
' W PPP tworzymy
' sumę zbiorów SP i SX
s_union(SP, SX, PPP)
' Zerujemy
' licznik sąsiadów
ncmax = 0
' Przechodzimy przez
' kolejne wierzchołki
' grafu
For u = 0 To n-1
' Jeśli wierzchołek
' jest w PPP,
' to przetwarzamy go
If s_isin(PPP, u) Then
' Zerujemy bieżący
' licznik sąsiadów
nc = 0
p = graf[u]
' Badamy sąsiadów
' wierzchołka v
While p <> 0
' Jeśli sąsiad w SP,
' zwiększamy nc
If s_isin(SP,p->v) Then _
nc += 1
' Następny sąsiad
p = p->next
Wend
' Jeśli liczba
' sąsiadów w P jest
' większa od ncmax,
If nc >= ncmax Then
' to zapamiętujemy
' wierzchołek
v = u
' oraz jego liczbę
' sąsiadów w P
ncmax = nc
End If
End If
Next
' W PPP umieszczamy
' zbiór SP
s_copy(SP, PPP)
p = graf[v]
' Przeglądamy listę
' sąsiadów piwota
While p <> 0
' Z PPP usuwamy
' każdego sąsiada
' piwota
s_remove(PPP, p->v)
' Następny sąsiad
p = p->next
Wend
' Tworzymy puste
' zbiory pomocnicze
s_create(RP, 0, n-1)
s_create(PP, 0, n-1)
s_create(XP, 0, n-1)
' Przechodzimy przez
' kolejne wierzchołki
' grafu
For v = 0 To n-1
' Jeśli wierzchołek v
' jest w zbiorze PPP, to
' go przetwarzamy
If s_isin(PPP, v) Then
' Najpierw zerujemy
' zbiór sąsiadów
s_clear(SN)
' Przeglądamy listę
' sąsiedztwa
' wierzchołka v
p = graf[v]
While p <> 0
' Każdego sąsiada
' umieszczamy w SN
s_add(SN, p->v)
p = p->next
Wend
' W RP tworzymy
' zbiór SR z dodanym
' wierzchołkiem v
s_copy(SR, RP)
s_add(RP, v)
' W PP tworzymy
' iloczyn SP i SN
s_intersection(SP, SN, PP)
' W XP tworzymy
' iloczyn SX i SN
s_intersection(SX, SN, XP)
' Wywołanie rekurencyjne
BronKerbosch(RP, PP, XP)
' Z SP usuwamy
' wierzchołek v
s_remove(SP, v)
' i dodajemy go do SX
s_add(SX, v)
End If
Next
' Usuwamy
' zbiory pomocnicze
Delete [] RP.T
Delete [] PP.T
Delete [] XP.T
Delete [] PPP.T
End If
End Sub
' **********************
' *** PROGRAM GŁÓWNY ***
' **********************
Dim As Set SR, SP, SX
Dim As integer i, u, v
Dim As SLel Ptr p, r
Open Cons For Input As #1
' Odczytujemy liczbę
' wierzchołków i krawędzi
Input #1, n, m
' Tworzymy puste zbiory
s_create(SR, 0, n-1)
s_create(SP, 0, n-1)
s_create(SX, 0, n-1)
s_create(SN, 0, n-1)
' Pusty zbiór
' kliki największej
s_create(RM, 0, n-1)
' Liczba wierzchołków
' w klice największej
rmsize = 0
' Tworzymy tablicę
' list sąsiedztwa
graf = New SLel Ptr [n]
For i = 0 To n-1
' Zerujemy
' listy sąsiedztwa
graf[i] = 0
Next
For i = 1 To m
' Wierzchołki krawędzi
Input #1, u, v
' Tworzymy
' element listy
p = New SLel
p->v = u
' Element dołączamy
' do listy sąsiadów v
p->next = graf[v]
graf[v] = p
' To samo dla krawędzi
' w drugą stronę
p = New SLel
p->v = v
' Element dołączamy
' do listy sąsiadów u
p->next = graf[u]
graf[u] = p
Next
Close #1
' W zbiorze SP
' ustawiamy wszystkie
' wierzchołki
s_full(SP)
Print
' Szukamy rekurencyjnie
' kliki największej
BronKerbosch(SR, SP, SX)
Print "MAXIMUM CLIQUE: ";
s_print(RM)
Print
Print "NUMBER OF VERTICES"+_
" IN MAXIMUM CLIQUE =";_
rmsize
Print
' Usuwamy zbiory
Delete [] SR.T
Delete [] SP.T
Delete [] SX.T
Delete [] SN.T
Delete [] RM.T
' Usuwamy tablicę
' list sąsiedztwa
For v = 0 To n-1
p = graf[v]
While p <> 0
r = p
p = p->next
Delete r
Wend
Next
Delete [] graf
End
|
Python
(dodatek)# Wyszukiwanie kliki największej
# Data: 27.03.2025
# (C)2025 mgr Jerzy Wałaszek
#-------------------------------
# Klasa elementu
# listy sąsiedztwa
class SLel:
def __init__(self):
# Następny element listy
self.next = None
# Wierzchołek docelowy
self.v = 0
# Klasa struktury zbioru
class Set:
def __init__(self):
self.vmin = 0
self.nmax = 0
self.t = []
# Obsługa zbiorów
#----------------
# Procedura dodaje
# element x do zbioru A
def s_add(a,x):
# Obliczamy indeks
i = x - a.vmin
# Ustawiamy bit na 1
a.t[i] = True
# Procedura usuwa
# element x ze zbioru
def s_remove(a,x):
# Obliczamy indeks
i = x - a.vmin
# Ustawiamy bit na 0
a.t[i] = False
# Procedura usuwa
# wszystkie elementy
# ze zbioru
def s_clear(a):
# Zerujemy tablicę
a.t = [False] * (a.nmax + 1)
# Procedura tworzy zbiór
def s_create(a,vmin,vmax):
# Wypełniamy strukturę danymi
a.vmin = vmin
# Indeks ostatniego
# elementu tablicy
a.nmax = vmax - vmin
# Tworzymy dynamicznie
# tablicę mapy bitowej
a.t = [False] * (a.nmax + 1)
# Procedura wyznacza
# sumę zbiorów A i B
def s_union(a,b,c):
for i in range(a.nmax + 1):
c.t[i] = a.t[i] or b.t[i]
# Procedura wyznacza
# część wspólną
# zbiorów A i B
def s_intersection(a,b,c):
for i in range(a.nmax + 1):
c.t[i] = a.t[i] and b.t[i]
# Procedura kopiuje
# zbiór A do zbioru B
def s_copy(a,b):
b.t = a.t[:]
# Procedura ustawia
# zbiór pełny
def s_full(a):
a.t = [True] * (a.nmax + 1)
# Funkcja sprawdza,
# czy zbiór A jest pusty
# true - tak, jest pusty
# false - nie, nie jest pusty
def s_empty(a):
# Pobieramy bity do m
m = a.t[0]
for i in range(1, a.nmax + 1):
# Sumujemy logicznie bity
m = m or a.t[i]
return not m
# Funkcja bada,
# czy element x
# należy do zbioru A
# true - tak, należy
# false - nie, nie należy
def s_isin(a,x):
# Obliczamy indeks
i = x - a.vmin
return a.t[i]
# Procedura wyświetla
# elementy zbioru A
def s_print(a):
for i in range(a.nmax + 1):
if a.t[i]:
print(i + a.vmin, end=" ")
# Funkcja oblicza
# liczbę elementów w A
def s_size(a):
# Zerujemy licznik
c = 0
for i in range(a.nmax + 1):
if a.t[i]: c += 1
return c
# Procedura wyszukuje
# klikę największą
#--------------------
def bron_kerbosch(sr,sp,sx):
global n,rmsize,graf
rp = Set()
pp = Set()
xp = Set()
ppp = Set()
v = 0
# Jeśli zbiory
# SP i SX są puste,
# to SR zawiera
# wierzchołki
# kliki maksymalnej
if s_empty(sp) and s_empty(sx):
# Wyznaczamy liczbę
# wierzchołków w klice
rms = s_size(sr)
# Jeśli mamy większą
# klikę od dotychczas
# znalezionej,
if rms > rmsize:
# to zapamiętujemy ją
s_copy(sr, rm)
# Zapamiętujemy
# również jej rozmiar
rmsize = rms
else:
# Tworzymy zbiór
# pomocniczy PPP
s_create(ppp, 0, n-1)
# W PPP tworzymy
# sumę zbiorów SP i SX
s_union(sp, sx, ppp)
# Zerujemy
# licznik sąsiadów
ncmax = 0
# Przechodzimy przez
# kolejne wierzchołki
# grafu
for u in range(n):
# Jeśli wierzchołek
# jest w PPP,
# to przetwarzamy go
if s_isin(ppp, u):
# Zerujemy bieżący
# licznik sąsiadów
nc = 0
p = graf[u]
# Badamy sąsiadów
# wierzchołka v
while p:
# Jeśli sąsiad w SP,
# zwiększamy nc
if s_isin(sp,p.v):
nc += 1
# Następny sąsiad
p = p.next
# Jeśli liczba
# sąsiadów w P jest
# większa od ncmax,
if nc >= ncmax:
# to zapamiętujemy
# wierzchołek
v = u
# oraz jego liczbę
# sąsiadów w P
ncmax = nc
# W PPP umieszczamy
# zbiór SP
s_copy(sp, ppp)
p = graf[v]
# Przeglądamy listę
# sąsiadów piwota
while p:
# Z PPP usuwamy
# każdego sąsiada
# piwota
s_remove(ppp, p.v)
# Następny sąsiad
p = p.next
# Tworzymy puste
# zbiory pomocnicze
s_create(rp, 0, n-1)
s_create(pp, 0, n-1)
s_create(xp, 0, n-1)
# Przechodzimy przez
# kolejne wierzchołki
# grafu
for v in range(n):
# Jeśli wierzchołek v
# jest w zbiorze PPP, to
# go przetwarzamy
if s_isin(ppp, v):
# Najpierw zerujemy
# zbiór sąsiadów
s_clear(sn)
# Przeglądamy listę
# sąsiedztwa
# wierzchołka v
p = graf[v]
while p:
# Każdego sąsiada
# umieszczamy w SN
s_add(sn, p.v)
p = p.next
# W RP tworzymy
# zbiór SR z dodanym
# wierzchołkiem v
s_copy(sr, rp)
s_add(rp, v)
# W PP tworzymy
# iloczyn SP i SN
s_intersection(sp,sn,pp)
# W XP tworzymy
# iloczyn SX i SN
s_intersection(sx,sn,xp)
# Wywołanie rekurencyjne
bron_kerbosch(rp,pp,xp)
# Z SP usuwamy
# wierzchołek v
s_remove(sp, v)
# i dodajemy go do SX
s_add(sx, v)
# **********************
# *** PROGRAM GŁÓWNY ***
# **********************
sr = Set()
sp = Set()
sx = Set()
sn = Set()
rm = Set()
# Odczytujemy liczbę
# wierzchołków i krawędzi
x = input().split()
n = int(x[0])
m = int(x[1])
# Tworzymy puste zbiory
s_create(sr, 0, n-1)
s_create(sp, 0, n-1)
s_create(sx, 0, n-1)
s_create(sn, 0, n-1)
# Pusty zbiór
# kliki największej
s_create(rm, 0, n-1)
# Liczba wierzchołków
# w klice największej
rmsize = 0
# Tworzymy tablicę
# list sąsiedztwa
graf = [None] * n
for i in range(m):
# Wierzchołki krawędzi
x = input().split()
u = int(x[0])
v = int(x[1])
# Tworzymy
# element listy
p = SLel()
p.v = u
# Element dołączamy
# do listy sąsiadów v
p.next = graf[v]
graf[v] = p
# To samo dla krawędzi
# w drugą stronę
p = SLel()
p.v = v
# Element dołączamy
# do listy sąsiadów u
p.next = graf[u]
graf[u] = p
# W zbiorze SP
# ustawiamy wszystkie
# wierzchołki
s_full(sp)
print()
# Szukamy rekurencyjnie
# kliki największej
bron_kerbosch(sr, sp, sx)
print("MAXIMUM CLIQUE: ",end="")
s_print(rm)
print()
print("NUMBER OF VERTICES"+
" IN MAXIMUM CLIQUE =",
rmsize)
print()
# Usuwamy zbiory
del sr,sp,sx,sn,rm
# Usuwamy tablicę
# list sąsiedztwa
for v in range(n):
while graf[v]:
graf[v] = graf[v].next
del graf
|
| Wynik: | |
| 8 19 0 1 0 2 0 5 0 6 1 2 1 3 1 5 1 6 2 4 2 5 2 6 2 7 3 4 3 6 3 7 4 6 5 6 5 7 6 7 MAXIMUM CLIQUE: 0 1 2 5 6 NUMBER OF VERTICES IN MAXIMUM CLIQUE = 5 |
![]() |
![]() |
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2026 mgr Jerzy Wałaszek |
Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email:
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.