Файл: 2. Лекции Паскаль (Часть 2).doc

Добавлен: 19.10.2018

Просмотров: 4415

Скачиваний: 9

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

СОДЕРЖАНИЕ

Оглавление

9. Файловые типы данных

9.1. Инициализация файла

9.2. Файлы и работа с ними

Лабораторная работа №11.

Работа с внешними файлами

Лабораторная работа №11, вариант № 5.

Работа с внешними файлами

Варианты заданий.

9.3. Сортировка файлов.

9.3.1. Слияние упорядоченных последовательностей.

9.3.2. Сортировка сбалансированным слиянием

9.3.3. Сортировка простым слиянием

9.3.4. Сортировка естественным слиянием.

9.3.5. Сортировка многофазным слиянием.

Лабораторная работа №12.

Сортировка файлов.

Лабораторная работа №12.

Сортировка файлов.

10. Динамическая память.

10.1. Указатели.

10.2. Списки.

Лабораторная работа № 13.

Исключение элементов списка.

Образец выполнения работы.

Лабораторная работа № 13.

Исключение элементов списка.

Варианты задания.

Лабораторная работа № 14.

Работа со списками.

Образец выполнения работы.

Лабораторная работа № 14.

Работа со списками.

Варианты задания.

Лабораторная работа № 15.

Выполнение операций над списковыми структурами.

Образец выполнения работы.

Лабораторная работа № 15.

Выполнение операций над списковыми структурами.

Варианты заданий.

10.3. Деревья.

10.4. Стеки, очереди.

Образец выполнения работы.

Лабораторная работа № 16.

Работа со стеками и очередями.

Лабораторная работа № 16.

Работа со стеками и очередями.

11. Организация меню с использованием средств среды Turbo Pascal

Лабораторная работа №17.

Составления меню.

Образец выполнения работы.

Лабораторная работа № 17.

Составления меню.


В том случае, когда не используются средства прямого доступа (процедура типа Seek) алгоритм усложняется.


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


Здесь также необходимо учитывать, что когда достигнут конец файла, в буфере остаётся его последняя запись, которую надо правильно переписать в выходной файл С.


При распределении серий в файла А и В необходимо правильно определять конец серии. Здесь для определения конца серии сравниваем ключ записи, находящийся в буфере (переменная buf) с ключом предыдущей записи (хранится в переменной Х). Если buf.key<x, то в буфере находится запись уже другой серии, которую надо переписать в другой файл.


Ниже приведены тексты процедур разделения и слияния серий, а также основной части программы с подробными комментариями, которые помогут Вам лучше понять предложенный здесь алгоритм.


{сформировать признак конца сортировки}

procedure ended;

begin

reset(a);reset(b);

if eof(a) or eof(b) then {получили одну серию} z:=1;

close(a);close(b);

end;


{разделение файла С на файлы А и В}


procedure distribute;

var buf:item;

x:integer;

pt:boolean;{переключатель выходных файлов}

{если pt=true, то запись в файл А, иначе в файл В}

ok:boolean;{признак нахождения в буфере последней записи серии}

begin

reset(c);rewrite(a);rewrite(b);

pt:=true;

ok:=false;

if not eof(c) then {переписываем в А первую запись из С, её ключ запоминаем в X}

begin

read(c,buf);write(a,buf);x:=buf.key;

end;

if not eof(c) then

begin

read(c,buf);{читаем в буфер вторую запись из С}

repeat {повторять пока не закончится файл С}

while not eof(c) and ((buf.key>=x) or ok) do

{пока не конец С и либо не конец серии, либо в буфере находится запись другой серии}

begin

ok:=false;

if pt then write(a,buf) else write(b,buf);

x:=buf.key;read(c,buf);

end;

if buf.key<x then

{в буфере находится запись уже другой серии и её надо переписать в другой файл}

begin;pt:=not pt;ok:=true;end;

if eof(c) then

{записать в выходной файл последнюю запись файла С, уже прочитанную в буфер}

if pt then write(a,buf) else write(b,buf);

until eof(c);

end;

close(a);close(b);close(c);

end;


{процедура слияния серий}


procedure merge;

var bufa,bufb:item;

xa,xb:integer;

ok,pr1,pr2:boolean;

k1,k2:boolean;

{pr1-признак того, что было прочитано не меньше двух записей файла А}

{pr2-признак того, что было прочитано не меньше двух записей файла В}

{k1-признак того, что в буфере есть запись файла А}

{k2-признак того, что в буфере есть запись файла В}

begin

reset(a);reset(b);rewrite(c);

pr1:=false;pr2:=false;k1:=false;k2:=false;

if not eof(a) then begin;read(a,bufa);k1:=true;end;

if not eof(b) then begin;read(b,bufb);k2:=true;end;

repeat {повторять пока не закончится либо файл А, либо файл В}


ok:=eof(a) or eof(b);

while not ok do

{повторять пока не закончится либо один из файлов, либо серия}

begin

if bufa.key<bufb.key

then begin

write(c,bufa);xa:=bufa.key;read(a,bufa);pr1:=true;

ok:=eof(a) or (xa>bufa.key);

end

else begin

write(c,bufb);xb:=bufb.key;read(b,bufb);pr2:=true;

ok:=eof(b) or (xb>bufb.key);

end;

end;

if (xa>bufa.key)and pr1 {конец текущей серии файла А}

then while (xb<bufb.key)and not eof(b) do

{переписать в файл С остаток текущей серии файла В}

begin

write(c,bufb);xb:=bufb.key;read(b,bufb)

end;

if (xb>bufb.key)and pr2 {конец текущей серии файла В}

then while (xa<bufa.key)and not eof(a) do

{переписать в файл С остаток текущей серии файла A}

begin

write(c,bufa);xa:=bufa.key;read(a,bufa)

end;

until eof(a) or eof(b);

if not eof(a) and k2

{в bufb есть запись и файл А не закончился}

{переписать в файл С записи из файла А с ключами меньше, чем ключ записи в bufb}

then while(bufa.key<bufb.key)and not eof(a) do

begin;write(c,bufa);read(a,bufa);end;

else if not eof(b) and k1

{в bufа есть запись и файл В не закончился}

{переписать в файл С записи из файла В с ключами меньше, чем ключ записи в bufа}

then while(bufb.key<bufa.key) and not eof(b) do

begin;write(c,bufb);read(b,bufb);end;

if k1 and k2{переписать в С записи из буферов bufa и bufb}

then if bufa.key<bufb.key

then begin;write(c,bufa);write(c,bufb);end;

else begin;write(c,bufb);write(c,bufa);end;

else if k1 then write(c,bufa)

else if k2 then write(c,bufb);

{переписать в С остаток файла А}

while not eof(a) do begin;read(a,bufa);write(c,bufa);end;

{переписать в С остаток файла В}

while not eof(b) do begin;read(b,bufb);write(c,bufb);end;

close(a);close(b);close(c);

end;


Внешняя сортировка файлов естественным слиянием (используется только последовательный доступ к файлам).

Программа использует уже готовый файл содержащий не отсортированные данные и не распечатывает отсортированный файл (программа только сортирует файл file1).


program natmeg;

type item=record

key:integer;

{описание других полей}

end;

tfile=file of item;

var a,b,c:tfile;

procedure ended;

begin

reset(a);reset(b);

if eof(a) or eof(b) then {получили одну серию} z:=1;

close(a);close(b);

end;

procedure distribute;

var buf:item;

x:integer;

pt:boolean;{переключатель выходных файлов}

{если pt=true, то запись в файл А, иначе в файл В}

ok:boolean;{признак нахождения в буфере последней записи серии}

begin

reset(c);rewrite(a);rewrite(b);

pt:=true;

ok:=false;

if not eof(c) then {переписываем в А первую запись из С, её ключ запоминаем в X}

begin

read(c,buf);write(a,buf);x:=buf.key;

end;

if not eof(c) then

begin

read(c,buf);{читаем в буфер вторую запись из С}

repeat {повторять пока не закончится файл С}

while not eof(c) and ((buf.key>=x) or ok) do

{пока не конец С и либо не конец серии, либо в буфере находится запись другой серии}

begin

ok:=false;

if pt then write(a,buf) else write(b,buf);

x:=buf.key;read(c,buf);

end;

if buf.key<x then

{в буфере находится запись уже другой серии и её надо переписать в другой файл}

begin;pt:=not pt;ok:=true;end;

if eof(c) then

{записать в выходной файл последнюю запись файла С, уже прочитанную в буфер}

if pt then write(a,buf) else write(b,buf);

until eof(c);

end;

close(a);close(b);close(c);

end;

procedure merge;

var bufa,bufb:item;

xa,xb:integer;

ok,pr1,pr2:boolean;

k1,k2:boolean;

{pr1-признак того, что было прочитано не меньше двух записей файла А}


{pr2-признак того, что было прочитано не меньше двух записей файла В}

{k1-признак того, что в буфере есть запись файла А}

{k2-признак того, что в буфере есть запись файла В}

begin

reset(a);reset(b);rewrite(c);

pr1:=false;pr2:=false;k1:=false;k2:=false;

if not eof(a) then begin;read(a,bufa);k1:=true;end;

if not eof(b) then begin;read(b,bufb);k2:=true;end;

repeat {повторять пока не закончится либо файл А, либо В}

ok:=eof(a) or eof(b);

while not ok do

{повторять пока не закончится либо один из из файлов, либо серия}

begin

if bufa.key<bufb.key

then begin

write(c,bufa);xa:=bufa.key;read(a,bufa);pr1:=true;

ok:=eof(a) or (xa>bufa.key);

end

else begin

write(c,bufb);xb:=bufb.key;read(b,bufb);pr2:=true;

ok:=eof(b) or (xb>bufb.key);

end;

end;

if (xa>bufa.key)and pr1 {конец текущей серии файла А}

then while (xb<bufb.key)and not eof(b) do

{переписать в файл С остаток текущей серии файла В}

begin

write(c,bufb);xb:=bufb.key;read(b,bufb)

end;

if (xb>bufb.key)and pr2 {конец текущей серии файла В}

then while (xa<bufa.key)and not eof(a) do

{переписать в файл С остаток текущей серии файла А}

begin

write(c,bufa);xa:=bufa.key;read(a,bufa)

end;

until eof(a) or eof(b);

if not eof(a) and k2

{в bufb есть запись и файл А не закончился}

{переписать в файл С записи из файла А с ключами меньшими, чем ключ записи в bufb}

then while(bufa.key<bufb.key)and not eof(a) do

begin;write(c,bufa);read(a,bufa);end;

else if not eof(b) and k1

{в bufа есть запись и файл В не закончился}

{переписать в файл С записи из файла В с ключами меньшими, чем ключ записи в bufа}

then while(bufb.key<bufa.key) and not eof(b) do

begin;write(c,bufb);read(b,bufb);end;

if k1 and k2{переписать в С записи из буферов bufa и bufb}

then if bufa.key<bufb.key

then begin;write(c,bufa);write(c,bufb);end;

else begin;write(c,bufb);write(c,bufa);end;

else if k1 then write(c,bufa)

else if k2 then write(c,bufb);

{переписать в С остаток файла А}

while not eof(a) do begin;read(a,bufa);write(c,bufa);end;

{переписать в С остаток файла В}

while not eof(b) do begin;read(b,bufb);write(c,bufb);end;

close(a);close(b);close(c);

end;

begin {main}

assign(a,'{имя внешнего файла}');

assign(b,'{имя внешнего файла}');

assign(c,'{имя внешнего файла}');

repeat

z:=0;

distribute;

merge;

ended;

until z=1;

end.

Результат работы:

0 3 86 20 27 67 32 16 37 43 8 47 7 84 6 29 92 37 77 33 70

84 72 31 16 33 47 25 83 28 48 15 87 29 77 98 49 89 83 2 14 1

4 50 2 59 1 77 65 77 71 56 21 68 59 96 64 100 24 68 30 9 77

50 88 51 57 95 68 34 1 71 99 77 75 20 14 91 78 59 86 69 29

9 63 28 88 16 27 54 96 17 16 27 18 58 50 29 16 61 74

Нажмите Enter для продолжения !


0 1 1 2 2 3 6 7 8 9 9 14 14 14 15 16 16 16 16 16 17 18 20

20 21 24 25 27 27 27 28 28 29 29 29 29 30 31 32 33 33 34 37

37 43 47 47 48 49 50 50 50 51 54 56 57 58 59 59 59 61 63 64

65 67 68 68 68 69 70 71 71 72 74 75 77 77 77 77 77 77 78 83

83 84 84 86 86 87 88 88 89 91 92 95 96 96 98 99 100

Нажмите Enter для продолжения !



9.3.5. Сортировка многофазным слиянием.


Р
ассмотренные выше методы сортировки простым и естественным слиянием достаточно просты, однако не эффективны, так как сортировка выполняется за значительное число проходов. Например, при сортировке М записей простым слиянием требуется:

К -проходов.

Е
стественный путь повышения эффективности сортировки заключается в предварительной внутренней сортировке записей, формирование из них серий возможно большей длинны и распределение этих серий по нескольким файлам. При Р-путевом сбалансированным слияний число проходов



Г
де R-начальное число серий. Поскольку на каждом проходе обрабатывается N записей, то эффективность всего процесса сортировки будет пропорциональна


А
с учётом внутренней сортировки


Из этой формулы виден второй путь повышения эффективности сортировки, а именно увеличивать число файлов P. Однако при сбалансированном многофазном слиянии одновременно используются лишь чуть больше половины имеющихся файлов, а остальные простаивают. Возникает вопрос, можно ли ещё лучше использовать имеющиеся файлы? Да, это возможно, если распределить начальные серии не поровну между всеми файлами, а более хитрым способом, так чтобы на каждой фазе слияния, кроме самой последней, только один файл оказывался пустым. Такой метод называется многофазным слиянием.



Вернёмся к предыдущему примеру сортировки 1700 записей, предварительно отсортированных в серии по 100 записей. Однако на первом этапе распределим эти серии на 3 файла следующим образом: в файл 1-7 серий, в файл 2-6 серий, в файл 3-4 серии.

Далее будем сливать эти серии в файл 4. В результате в файле 4 мы получим 4 серии длиною 300 записей, в файле 1 останется 3 серии, в файле 2 будет 2 серии, а файл 3 станет пустой. Далее сливаем серии в файл 3 и так далее, пока не получим отсортированный файл, состоящий из одной серии длиною 1700 записей. Процесс сортировки показан на рисунке.



1

F4

700(1)


Внутренняя сортировка и распределение





F2

F3

F4

F1


7(100) 6(100) 4(100)


Слияние 1



F1

F2

F3

F4



3(100) 2(100) 4(300)

Слияние 1




F1

F2

F3

F4



1(100) 2(500) 2(300)

Слияние 1



F1

F2

F3

F4



1(900) 1(500) 1(300)

Слияние 1



F1

F2

F3

F4



1(1700)

Рисунок. Сортировка 1700 записей с использованием 4 файлов методом многофазного слияния.


Например, для 4 файлов имели следующую последовательность Фибоначчи 2-го порядка

0 , 0 , 1 , 1 , 2 , 4 , 7 , 13 , 24 , 44 , 81.


Возможные начальные распределения серий показаны в таблице.



Таблица.

Начальное распределение серий для 4 лент.

F1

4

7

13

24

44

F2

6

11

20

37

68

F3

7

13

24

44

81

Количество серий в исходном файле

17

31

57

105

193


Из сказанного выше следует, что алгоритм многофазного слияния применим только к таким входным данным, в которых число начальных серий имеет волне определённое значение. А что же делать, если число начальных серий отличается от требуемого? В этом случае поступают следующим образом: вводится соответствующее число фиктивных пустых серий, так, чтобы сумма реальных и фиктивных серий давала нужное значение. Однако возникает сразу следующий вопрос: как распределить фиктивные серии между файлами? Давайте посмотрим, как сливаются реальные и фиктивные серии. Понятно, что выбор фиктивной серии с i-ый файла означает, что i-ый файл не участвует в слиянии; в результате слияние происходит с менее чем N-1 файлами. Слияние фиктивных серий со всех N-1 входных файлов не предполагает никакой действительной операции слияния, а означает просто запись фиктивных серий на входной файл. Из этого можно сделать вывод, что фиктивные серии нужно распределить на N-1 файлов как можно более равномерно, так как мы заинтересованы в слиянии с возможно большего числа файлов. Можно также показать, что фиктивные серии лучше располагать в начале файлов.

Пример программы Сортировка многофазным слиянием:

Программа использует уже готовый файл содержащий не отсортированные данные и не распечатывает отсортированный файл (программа только сортирует файл file0).

program Многофазное слияние;

uses crt;

const n=10;

type item=record

key:integer;

{описания других полей}

end;

filetype=file of item;

var f0,f1,f2,f3,a,b,c,d:filetype;

ser0,ser1,ser2,ser3,kolser,disk0,disk1,disk2,disk3,z,l,all,zero0,zero1,zero2,zero3:integer;

eora,eorb,eorc,per,eor:boolean;

mas:array[1..n]of integer;

data:item;

procedure fibonachi;

procedure fib;

begin

disk0:=disk1+disk2+disk3;

while disk0<kolser do

begin

disk3:=disk2;disk2:=disk1;disk1:=disk0;disk0:=disk1+disk2+disk3;

end;

end;

begin

disk1:=1;disk2:=1;disk3:=1;fib;kolser:=disk0;

disk1:=0;disk2:=0;disk3:=1;fib;disk1:=disk2;disk2:=kolser-(disk1+disk3);

end;

procedure readfile;

begin

l:=0;

while not eof(f0)and not(l=n) do

begin l:=l+1;read(f0,data);mas[l]:=data.key;end;

all:=l;

end;

procedure massort;

var buf,units:integer;

m:boolean;

begin

units:=all;

repeat

m:=true;;

units:=units-1;

for l:=1 to units do

begin

if mas[l]>mas[l+1]

then begin buf:=mas[l];mas[l]:=mas[l+1];mas[l+1]:=buf;m:=false;end;

end;

until m or (units=1);

end;

procedure sort(var x:filetype);

begin

readfile;massort;

for l:=1 to all do begin data.key:=mas[l];write(x,data);end;

end;

procedure sortrun;

begin

reset(f0);rewrite(f1);rewrite(f2);rewrite(f3);

kolser:=1;zero0:=0;zero1:=0;zero2:=0;zero3:=0;ser0:=0;ser1:=0;ser2:=0;ser3:=0;

repeat

fibonachi;

if disk1<>ser1 then begin

if not eof(f0) then sort(f1)

else zero1:=zero1+1;

ser1:=ser1+1;

end;

if disk2<>ser2 then begin

if not eof(f0) then sort(f2)

else zero2:=zero2+1;

ser2:=ser2+1;

end;

if disk3<>ser3 then begin

if not eof(f0) then sort(f3)

else zero3:=zero3+1;

ser3:=ser3+1;

end;

if (not eof(f0))and(kolser=(ser1+ser2+ser3)) then kolser:=kolser+1;