Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Множества_Лекция_3.doc
Скачиваний:
8
Добавлен:
22.02.2015
Размер:
147 Кб
Скачать

ЛЕКЦИЯ 3.

Множества.

Существуют разные подходы к представлению множества:

1.Хранится каждый элемент, присутствующий в множестве. Это может быть смежное представление (например, массив элементов) или связанное размещение элементов в памяти (в дальнейшем рассмотрим организацию связанных списков).

2.Определяются все потенциально возможные злементы множества А: А={а0, а1, . . . . .аn-2, аn-1}. Всего n элементов. Затем для каждого подмножества X из А определяется для каждого элемента, принадлежит ли он этому подмножеству. Для этого каждому множеству сопоставляется бинарная (т.е. состоящая из 0 и 1) последовательность b0,b1,…bn-1, определяемая так:

i=0,1,2, . . .n-1

Наилучший метод представления множеств существенно зависит от операций, которые планируется выполнять над множествами. Это типичные операции: выяснить, имеется ли конкретный элемент в данном множестве, добавить в множество новые элементы, удалить из множества элементы, выполнить обычные теоретико-множественные операции, например объединение или пересечение двух множеств.

Вектор b= {b0,b1,…bn-1} называется характеристическим вектором из n элементов. Это представление удобнее тем, что основные операции над множествами можно выполнять как побитовые операции над двоичными векторами. Однако для больших n использование таких векторов практически невыполнимо.

Рассмотрим задачу формирования различных подмножеств заданного массивом множества А={а0, а1, . . . . .аn-2, аn-1}. Заводят некоторую строку битов и устанавливают взаимно-однозначное соответствие между битами строки b и числами из А: bi <=> а [i].

b: а[n-1] а[n-2] . . . . а[2] а[1] а[0]

.......

.......

n-1 n-2 2 1 0

Описать переменную b можно, например, так

unsigned int b; // если n<32

или

unsigned char b [NN] ; // если n<NN*8

Рассмотрим такое описание b:

unsigned int b;

Пусть в переменной b записана информация о некотором подмножестве С⋤А:

i=0,1,2, . . .n-1

b:

n-1 n-2 . . . . . . . .. . . . . 3 2 1 0

.. .

1

0

1

1

. . . . .

1

0

1

1

Содержимое b есть некоторое целое число

r= , где 0≤ r ≤2n-1,

т.е. для множества А={а[0], а[1], а[2], . . . . а[n-2],а[n-1]}

возможны 2n различных подмножеств.

Как, имея b, получить подмножество элементов из А?

Просматриваем все биты от 0-го до (n-1)-го из b, выводим элементы а[i], соответствующие единичным битам.

Void f1 (int a[], int n, unsigned int b)

{//вывод элементов подмноджества, заданного строкой b

cout<<’\n’;

.. .

1

0

1

1

. . . . .

1

0

1

1

int m=1;

b:

n-1 n-2 . . . . . . . .. . . . . 3 2 1 0

for (int i=0; i<n; i++)

{if (b&m) cout<<а[i]<<” ”;

m<<=1;

}

cout<<’\n’;

}

Void f2 (int а[ ], int n, unsigned char b [ ] )

{//вывод элементов подмножества, заданного массивом b.

int j=0, m=0;

cout<<’\n’;

for (int i=0; i<n; i++)

if (b[j] & 1<<m)

{ cout<<а[i]<<” ”;

m++;

if (m==8){m=0; j++;}

}

cout << ’\n’;

}

Как сформировать все подмножества из А?

Так как представление в строке b есть всегда некоторое целое число, то для формирования всех подмножеств можно формировать числа от нуля до 2n-1(начать с нуля и добавлять по 1 на каждом шаге), их двоичные представления дадут все подмножества n-элементного множества.

unsigned int b=0, k=(1<<n)-1; //k=0000…..0111111111

while (b<k) // n

{b=b+1;

//обработка подмножества, заданного битовой строкой b

. . . . . . . . . .

}

В этом алгоритме последовательно получаемые подмножества могут сильно отличаться друг от друга: после (n-1)-элементного множества (которому соответствует число 01111…..1) идёт одноэлементное множество(отвечающее числу 1000……0). Это часто неудобно.

. . . .000 . . . .000001

. . . .000. . . . 000010

. . . .000. . . . 000011

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

. . . .000. . . . 011111

. . . .000. . . .100000

. . . . 000 . . . .100001

. . . . 000. . . . 100010

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

. . . . 000. . . 0111100

. . . . 000. . . 1000111

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

. . . . 000. . . .1001011 и т.д.

Пример 1.

Рассмотрим алгоритм генерации k-элементных подмножеств множества из n элементов, т.е. последовательного формирования возрастающих по значению битовых строк с k единицами.

//Nelset.cpp k-элементные подмножества,

// маска - слово

#include <iostream>

#include <fstream>

#include <iomanip>

#include <math.h>

using namespace std;

//Дана совокупность не более n целых чисел

//и число k. Выбрать все подмножества из k

// простых чисел.

//Все подмножества (k – элементные) записать в файл.

const int L=32; // n=31 элемент +1

Ifstream fin;

ofstream fout;

// прототипы функций

Void print (ofstream &fout, int a[], int n, unsigned int b);

bool prost (int a);

Int main ()

{int i;

unsigned int b,n,k, nn;

int a[L];

fin.open("danN_el.txt");

if (fin.fail()){cout<<"error open danN.txt\n"; return(1);}

fout.open("rezNK_el.txt");

if (fout.fail()){cout<<"error open rezNK.txt\n";return(1);}

cout<<"k--> "; //кол-во эл-ов в подмножестве

cin>>k;

i=0;

fout<<"Данное множество: "<<endl;

while (fin>>a[i])

{fout<<setw(6)<<a[i];

i++;

if ((i%10)==0)fout<<endl;

}

fout<<endl;

fout<<k<<"- элементные подмножества простых чисел: "<<endl;

n=i; //cчитано n элементов: 0-ой, 1-й,... , (n-1)-ый

//выделение подмножеств, b – битовая строка (маска)

b=(1<<k)-1; //в маске k единиц 000...011...1 (1-ое подмножество)

nn=1<<n; //здесь единица n-го разряда

do

{print (fout, a, n, b); //обработка 1-го подмножества

//получение следующей маски из n единиц

unsigned int m;

m=1;

while ((b&m)==0) // пропуск крайних нулей

{ m=m<<1;}

int bb=0; //счетчик единиц

while ( (b&m)!=0) //пока не нуль, обработка единиц

{ bb++; //подсчёт единиц

b=b^m; //обнуление единиц

m=m<<1;

}

b=b|m; // первый 0 после единиц заменяем на 1

//установка bb-1 единиц в конец маски

b=b | ((1<<(bb-1))-1); //новая маска

}

while (b<nn);

fin.close(); fout.close();

return 0;

}

// запись в файл подмножеств из k простых элементов