- •Глава 5
- •1. Понятие множества. Представление множества для вычислений
- •2. Набор операций для множеств
- •3. Алгоритмы выполнения основных операций над множествами и их эффективность
- •4. Получение полных наборов комбинаторных объектов. Перестановки
- •Void main()
- •Int next_permut(int* p, int n)
- •Void main()
- •5. Подмножества
- •Void main()
- •Int next_subset(int n, int r, int*& b)
- •Void main()
- •6. Разбиения
- •7. Поиск наибольшей монотонно возрастающей подпоследовательности
- •Int subsetnext(int n, int r, int* q)
- •Int subs2(int n, int* X)
- •Int subs3(int n, int* b)
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();
}
}