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

ЛЕКЦИЯ 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 простых элементов

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

{unsigned int m; int i, t;

int B[L];

bool p=true;

i=0; m=1; t=0;

for (i=0; i<n && p; i++)

{if (b&m) //не нуль

{B[t]=a[i]; t++;

if (!prost(a[i])){p=false;}

}

m<<=1;

}

if (p)

{for (i=0;i<t;i++)

fout<<setw(6)<<B[i];

fout<<endl;

}

}

// проверка числа на простоту

bool prost (int a)

{int d,q;

bool p;

a=abs(a);

p=(a==2) || (a%2!=0);

if (p)

{d=3; q=int( sqrt (double(a)));

while (d<=q && a%d !=0)

d=d+2; //d+=2;

p=d>q;

}

return p;

}

danN_el.txt

123 13 5 7 43 17

29 11 33 41

rezNK_el.txt

Данное множество:

123 13 5 7 43 17 29 11 33 41

5- элементные подмножества простых чисел:

13 5 7 43 17

13 5 7 43 29

13 5 7 17 29

13 5 43 17 29

13 7 43 17 29

5 7 43 17 29

13 5 7 43 11

13 5 7 17 11

13 5 43 17 11

13 7 43 17 11

5 7 43 17 11

13 5 7 29 11

13 5 43 29 11

13 7 43 29 11

5 7 43 29 11

13 5 17 29 11

13 7 17 29 11

5 7 17 29 11

13 43 17 29 11

5 43 17 29 11

7 43 17 29 11

13 5 7 43 41

13 5 7 17 41

13 5 43 17 41

13 7 43 17 41

5 7 43 17 41

13 5 7 29 41

13 5 43 29 41

13 7 43 29 41

5 7 43 29 41

13 5 17 29 41

13 7 17 29 41

5 7 17 29 41

13 43 17 29 41

5 43 17 29 41

7 43 17 29 41

13 5 7 11 41

13 5 43 11 41

13 7 43 11 41

5 7 43 11 41

13 5 17 11 41

13 7 17 11 41

5 7 17 11 41

13 43 17 11 41

5 43 17 11 41

7 43 17 11 41

13 5 29 11 41

13 7 29 11 41

5 7 29 11 41

13 43 29 11 41

5 43 29 11 41

7 43 29 11 41

13 17 29 11 41

5 17 29 11 41

7 17 29 11 41

43 17 29 11 41

Данное множество:

123 13 5 7 43 17 29 11 33 41

8- элементные подмножества простых чисел:

13 5 7 43 17 29 11 41

Замечание. Сформировать новую битовую строку из k единиц можно с помощью следующих операторов:

s=b & (– int(b));

r=s + b;

b=r | (((b^r)>>2) / s);

где b- предыдущая битовая строка , s,r –переменные.

unsigned int x, s,r;

Пример 2. Решето Эратосфена.

Используя алгоритм “Решето атосфена” записать в файл все простые числа, не превышающие заданного N.

Решето представляет битовую строку для чисел от 0 до N. Сами числа не хранятся. Во все биты строки (кроме 0-го и 1-го,где -0) записываются 1. Выбирается самое маленькое число, из решета удаляются все кратные ему числа (их биты обнуляются), само число помещается в файл.Таким образом из решета удаляются все числа, не являющиеся простыми, остаются единичными только биты, соответствующие простым числам, а сами числа собираются в файле.

Для заданного числа n строится битовая строка s в виде массива из q элементов типа unsigned int (32 бита).

//Resh1.cpp

#include <iostream>

#include <fstream>

#include <iomanip>

using namespace std;

//Решето Эратосфена

const int n=1000; //N

const int q=(n>>5)+1; // это равно (n/32+1), но быстрее!

ofstream fout;

unsigned int u(unsigned int t)

//элемент из s c 1 в бите для t

{return 1<<(t&31);} //=(1<<(t%32);

unsigned int w(unsigned int t)

//номер элемента s с битом для t

{return t>>5;} // = t/32;