Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
АиПА / Лекции / 6_Комбинаторные задачи и вычисл на множествах.doc
Скачиваний:
16
Добавлен:
07.02.2016
Размер:
150.53 Кб
Скачать

Void main()

{ int i = 0, j, n, p;

unsigned long l, number;

printf("n=");

scanf("%d", &n); // ввести число элементов исходного множества

int* d = new unsigned char[n]; // Массив – бинарный код подмножества

number = 0x00000001 << n; // number - число всех подмножеств

printf("number=%ld \n", number);

for (j = 0; j < n; j++) d[j] = 0; // инициализация массива d

for (l = 0; l < number;l ++)

{ p = 0; i++; printf("%d ", i); // номер очередного подмножества

while(d[p] == 1) d[p++] = 0; // Установка первых единичных эл-тов в 0

d[p] = 1; // Установка след. эл-та в 1

for (j = 0; j < n; j++) if (d[j] == 1) printf("%d ",j);

puts(""); // вывод на экран очередного подмножества

}

delete[] d;

}

Множество представлено массивом B.

Int next_subset(int n, int r, int*& b)

{ int i, k, q = 1;

static int* A;

static m;

m++;

if (m == 1) { A = new int[n];

for (i = 0; i < n; i++) A[i] = i;

B = A; return m;

}

K = r - 1;

while (q) { A[k]++;

if (A[k] + r k 1 < n) q = 0;

else k--; if (k < 0) { m = 0; break; }

for (i = k + 1; i < r; i++) A[i] = A[i-1] + 1;

}

if (m == 0) delete[] A;

else B = A;

return m;

}

Работу функции next_subset проиллюстрируем следующим примером. Пусть имеем 5-элементное множество: X = {0,1,2,3,4} . Первые несколько подмножеств для r=3 будут такими:

X = {0,1,2}

X = {0,1,3}

X = {0,1,4}

X = {0,2,3}

X = {0,2,4}

X = {0,3,4}

X = {1,2,3}

...................

Для того, чтобы получить на экране все подмножества из 8-элементного множества A = {0,1,2,3,4,5,6,7} можно воспользоваться такой программой:

const n=8;

Void main()

{ int i, m, r, N = 0;

int* A;

for (r = 0; r <= n; r++)

{ while (m = next_subset(n, r, A)) { printf("%2d ", m);

for (i = 0; i < r; i++) printf("%d ", A[i]);

printf("\n");

N++;

}

}

printf("N=%d \n", N);

if (N == 256) puts("Test was passed OK");

else puts("Test was Failed");

}

6. Разбиения

Число разбиений (порядок записи подмножеств - элементов разбиения считаем значимым) n-элементного множества на k  n непустых подмножеств равно:

.

Общее число разбиений:

.

[Донской В.И. Дискретная математика. Уч. пос. – Симферополь, "Сонат", 2000.- 360 с.]

Процедура partitions позволяет получить все разбиения заданного множества на не пустые подмножества. Порядок записи подмножеств - элементов разбиения считается значимым.

Искомые разбиения представлены в виде двухмерного массива – одномерного массива одномерных массивов типа vector. Используются структура vector из стандартной библиотеки STL. Процедура print(S) выводит на экран очередное разбиение.

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

Переменная b типа unsigned long используется для битового представления очередного подмножества, отсюда имеем максимальный размер исходного множества для приведенной реализации - 32. Рассматривать исходное множество большего размера не имеет смысла, т.к. число разбиений оказывается слишком велико для любого численного анализа.

#include <syst.h>

#include <vector.h>

void partitions(vector<int> & A) // A – исходное множество, затем оч. подмнож.

{ static M; // Номер очередного разбиения

int i, n = A.size();

unsigned long b, t;

vector<int> R, C;

static vector < vector<int> > S;

for (b=1; b < pow(2,n); b++)

{ t=0x0001; R=C=vector<int>();

for (i=0;i<n;i++) { if (b&t) R.push_back(A[i]);

else C.push_back(A[i]);

t <<= 1;

}

S.push_back(R);

if (C.empty()) { M++; printf("%4d -> ", M);

print(S); return;

}

partitions(C);

S.pop_back(); S.pop_back();

}

}