Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

[ Миронченко ] Императивное и объектно-ориентированное програмирование на Turbo Pascal и Delphi

.pdf
Скачиваний:
68
Добавлен:
25.04.2014
Размер:
3.16 Mб
Скачать

91

Пример 2: Вы держите деньги в n банках. В массиве А хранятся размеры ваших вкладов, а в массиве В процентные ставки банков. Вы должны подсчитать свой

доход к концу месяца.

1 : const

2 : n=5;

3 : type

4 : Mas=array[1..n] of real;

5 : var

6 : A:Mas; {суммы вкладов в банках}

7 : B:Mas; {процентные ставки в банках}

8 : i:integer;

9 : s:real;

10:begin

11:for i:=1 to n do

12:begin

13:writeln('Введите ваш вклад в ',i,'-м банке');

14:read(A[i]);

15:writeln('Введите процентную ставку в (% годовых)');

16:read(B[i]);

17:end;

18:for i:=1 to n do

19:S:=S+A[i]*B[i];

20:S:=S/1200;

21:writeln('В конце месяца вы заработаете ',S,' грн.');

22:end.

По большому счету дело сводится к подсчету скалярного произведения массивов А и В, только его надо еще разделить на 1200 (12 месяцев, 100 процентов).

Пример 3: Программа, которая меняет порядок следования элементов в массиве на обратный. Например, если исходный массив 1 2 3 4 5, то результатом будет массив 5 4 3 2 1.

1 : const n=4; {n - количество элементов массива}

2 : type Mas=array[1..n] of integer; {Объявление нового типа} 3 : var

4 : i:byte;

5 : A:Mas; {Объявление переменной типа Mas}

6 : tmp:integer;

7 : BEGIN

8 : writeln('Введите, пожалуйста, ',n,' элементов массива');

9 : for i:=1 to n do {В этом цикле user вводит элементы массива}

10:read(A[i]);

11:writeln('Исходный массив');

12:for i:=1 to n do {Печать массива А на экран}

13:write(A[i],' ');

14:writeln;

15:for i:=1 to (n div 2) do {1-й элемент меняется с n-тым}

16:

begin

{2-й с n-1-ым, и.т.д.}

17:tmp:=A[i];

18:A[i]:=A[n-i+1];

0 2 3 4

92

19:A[n-i+1]:=tmp;

20:end;

21:writeln('Новый массив');

22:for i:=1 to n do {Печать массива А на экран}

23:write(A[i],' ');

24:END.

Смысл программы такой: мы должны поменять первый элемент с последним, второй с предпоследним, и т.д. Но обратите внимание, что цикл надо прокручивать только до середины массива (что произошло бы с массивом, если бы в строке 15 стояло вместо n div 2 просто n?)

6.3. Моделирование многочленов

Pn [x] = an xn + an−1xn−1 + ... + a1x + a0 - общий вид многочлена n -й степени от 1-й переменной. Очевидно, что многочлен можно представить в виде массива коэффициентов.

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

Например, многочлен 2x2 + 3x + 4 будет представлен (если максимальная степень многочлена = 3) в виде массива (если максимальная степень многочлена

больше трех, то нулей перед двойкой будет больше).

Есть много стандартных полиномов, использующихся особенно часто, например, полиномы Чебышева, задающиеся рекуррентной формулой:

P (x) = 1 , P (x) = x , P (x) = 2xP

(x) − P

(x) , n > 1 .

 

0

1

n

n−1

 

n−2

 

 

Вычислять многочлен Чебышева n -й степени можно так:

Пусть у нас есть A = Pn−2 (x) ,

B = Pn−1 (x) .

 

Надо вычислить C = Pn (x) = 2xPn−1 (x) − Pn−2 (x) = 2xB A

 

Для этого надо:

 

 

 

 

 

 

C := 2x B A;

 

 

 

 

 

 

A := B;

 

 

 

 

 

 

 

B := C;

 

 

 

 

 

 

 

Теперь

A = Pn−1 (x)

и B = Pn (x) ,

- значит можно

вычислять следующий многочлен

Чебышева.

 

 

 

 

 

 

Но решение

можно

улучшить: вычисление

коэффициентов массива Pn (x) и

присваивание их в массив В проводить параллельно с копированием массива В в массив А. В таком случае вместо вспомогательного массива будет достаточно одной переменной. В примере 4 этот алгоритм реализован на ТР.

На i-м витке цикла (20-я строка) вычисляется полином (n-i)-й степени. Перед вычислением в массиве А храниться полином (n-i-2)-й степени, а в массиве В - (n-i-1)-й степени. В конце витка цикла в массиве В будет храниться многочлен (n-i)-й степени, а

вмассиве А - многочлен (n-i-1)-й степени.

Встроках 22-27 вычисляются все коэффициенты многочлена Чебышева, кроме самого младшего. С помощью строк 24 и 26 в массив А присваивается массив В, а в строке 25 – вычисляются новые коэффициенты массива В.

93

Вычисление младших коэффициентов не укладывается в общую схему, поэтому их надо вычислять отдельно (в строках 28-30).

Пример 4: Вычисление многочлена Чебышева n-й степени.

1

: uses Crt;

 

2

: const n=20;

 

3

: type

 

4

:

Poly=array [1..n] of integer;{Polynom - многочлен}

5

: var

 

6

:

p:integer;

{Potenz - степень}

7

:

tmp,i,j:integer;

 

8

:

A,B:Poly;

 

9

: begin

 

10:Clrscr;

11:writeln('Введите порядок многочлена Чебышева');

12:readln(p);

13:if (p=0) then {P(0)=1}

14:begin

15:writeln(1);

16:exit;

17:end;

18:

A[n]:=1;

{В массиве А хранится P(0)}

19:B[n-1]:=1; {В массиве B хранится P(1)}

20:for i:=n-2 downto n-p-1 do

21:begin

22:for j:=i to n do

23:begin

24:tmp:=B[j-1]; {сохраняем значение B[j-1]}

25:B[j-1]:=2*B[j]-A[j-1];{используем рекуррентную формулу}

26:A[j-1]:=tmp;

27:end;

28:tmp:=B[n]; {вычисляем последний коэффициент}

29:B[n]:=-A[n];

30:A[n]:=tmp;

31:end;

32:{Печать многочлена}

33:for i:=n-p to n do

34:write(A[i], ' ');

35:readln;

36:end.

Замечание: На самом деле вычислять полиномы Чебышева можно вообще с помощью лишь одного массива (см. упражнение 9).

Давайте теперь научимся производить арифметические действия с многочленами. Сейчас мы напишем программу, позволяющую умножать многочлены.

Пусть A = a

xn + a

n−1

xn−1 + ... + a x + a , а B = b xn + b

xn−1 + ... + b x + b . Произведение i-й

n

 

1

0

n

n−1

1

0

компоненты многочлена А на j-ю компоненту многочлена В будет иметь вид aibj xi+ j , поэтому нам надо лишь вычислить aibj и прибавить его к нужному элементу массива С.

Главное – не запутаться с коэффициентами. На i-м месте массива стоит коэффициент при n i -й степени многочлена. Поэтому 2n i j - это степень многочлена, при

94

котором должен стоять коэффициент aibj , а n − (2n i j) - индекс элемента в массиве С, к которому это aibj надо прибавить.

Пример 5: Умножение 2-х многочленов.

1 : uses Crt;

2 : const n=20;

3 : type

4 : Poly=array [1..n] of integer; {Polynom - многочлен} 5 : var

6 : p1,p2:integer; {Potenz - степень}

7 : i,j:integer;

8 : A,B,C:Poly;

9 : begin

10:Clrscr;

11:writeln('Введите степень первого многочлена');

12:readln(p1);

13:writeln('Введите коэффициенты первого многочлена');

14:for i:=n downto n-p1 do

15:readln(A[i]);

16:writeln('Введите степень второго многочлена');

17:readln(p2);

18:writeln('Введите коэффициенты второго многочлена');

19:for i:=n downto n-p2 do

20:readln(B[i]);

21:{Теперь само умножение C:=A*B}

22:for i:=n downto n-p1 do

23:for j:=n downto n-p2 do

24:begin

25:C[n-(2*n-i-j)]:=C[n-(2*n-i-j)]+A[i]*B[j];

26:end;

27:{Печать многочлена С}

28:for i:=n-(p1+p2) to n do

29:write(C[i], ' ');

30:readln;

31:end.

Аналогично можно реализовать сложение, вычитание, деление, возведение в степень и другие операции над многочленами. Это будет одно из упражнений в главе «Модули и записи».

Я надеюсь, что вы ощутили мощь массивов и то, что ваши программистские мышцы растут не по дням, а по часам. Но, к сожалению, метод моделирования многочленов с помощью массивов не всегда хорош. Есть 2 проблемы:

1.Чтобы хранить многочлен x1000 + x495 понадобится массив из 1001 элемента, а хотелось бы ограничиться 2-мя переменными, иначе вычисления будут идти значительно дольше, и расход памяти будет очень большим.

2.Если обобщать задачу, рассматривая многочлены не от одной, а от нескольких переменных (число которых также может быть любым, заранее не фиксированным числом), то массивы принципиально не подходят.

3.Всегда может возникнуть переполнение, если степень многочлена превосходит размер массива.

{сдвигаемся на соседние элементы} {без сдвигов ошибки тоже не будет}

95

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

Рассмотрим еще одну задачу, которая является подзадачей для «быстрой» сортировки массива, которую мы изучим в главе 8.

Пример 6: Дан целочисленный массив А. Надо переформировать его элементы так, чтобы вначале стояли положительные числа и нули, а затем шли

отрицательные числа.

1 : const

2 : n=10;

3 : type

4 : Mas=array[1..n] of integer;

5 : var

6 : i,j,tmp:integer;

7 : A:Mas;

8 : begin

9 : randomize;

10:for i:=1 to n do

11:A[i]:=random(n)-(n div 2);

12:for i:=1 to n do

13:write(A[i], ' ');

14:writeln;

15:i:=1;

16:j:=n;

17:while (i<j) do

18:begin

19:while (i<j) and (A[i]>=0) do {Ищем отрицательный элемент}

20:inc(i);

21:while (i<j) and (A[j]<0) do {Ищем положительный элемент}

22:dec(j);

23:if (i>=j) then{если положительный элемент стоит дальше, }

24:

break;

{чем отрицательный, то можно выходить}

25:tmp:=A[i]; {Меняем А[i] и A[j] местами}

26:A[i]:=A[j];

27:A[j]:=tmp;

28: inc(i); 29: dec(j); 30: end; 31:

32:for i:=1 to n do

33:write(A[i], ' ');

34:readln;

35:end.

Алгоритм прост: движеемся с обоих концов и ищем элементы, которые нарушают порядок, установленный по условию задачи. Как только такие числа найдены, меняем их местами и ищем следующую пару. Когда станет i j (т. е. индексы

96

встретятся или заползут друг за друга), то необходимый порядок следования будет достигнут. Детали реализации, я думаю, будут понятны из комментариев в программе.

В строках 28-29, после обмена местами найденных элементов, индексы сдвигаются на соседние элементы. Эти две строки не обязательны, - и без них программа будет работать правильно, но с ними – красивее.

6.4. Поиск в массиве

Найти элемент в массиве означает указать его индекс, или дать понять, что такого элемента нет. Индексация у массивов будет [1..n], поэтому если элемента в массиве нет, то будем считать, что его индекс = 0.

Допустим, задан массив целых чисел А и число х, которое надо найти. Наиболее естественный способ поиска заданного числа в массиве, - просто сравнивать поочередно каждый элемент A[i] с числом х. Этот метод называется последовательным поиском.

Пример 7: Последовательный поиск.

1 : const n=4; {n - количество элементов массива}

2 : type Mas=array[1..n] of integer; {Объявление нового типа} 3 : var

4 : i:byte;

5 : A:Mas;

6 : x:integer;

7 : BEGIN

8 : writeln('Введите, пожалуйста, ',n,' элементов массива');

9 : for i:=1 to n do {В этом цикле user вводит элементы массива}

10:read(A[i]);

11:writeln('Исходный массив');

12:for i:=1 to n do {Печать массива А на экран}

13:write(A[i],' ');

14:writeln;

15:writeln('Введите число, индекс которого надо найти');

16:readln(x);

17:for i:=1 to n do

18:if (A[i]=x) then

19:begin

20:writeln('у элемента ',x,' индекс =',i);

21:exit;

22:end;

23:writeln('Элемента в массиве нет');

24:readln;

25:END.

Очевидно, что последовательный поиск – единственно возможный способ отыскать элемент в массиве, о внутренней структуре которого ничего не известно. Удивительно, но реализация поиска, которая приведена в примере 7 – не самая эффективная. Улучшить скорость быстрого поиска вам предлагается в упражнении 8.

А вот упорядоченность массива (по неубыванию или невозрастанию) позволяет сократить время поиска во много раз. Итак, встречайте, - бинарный поиск.

97

Пусть для определенности массив будет отсортирован по неубыванию.

Идея метода такая: сначала сравниваем число х с центральным элементом массива А ( Aцентр ). Если х = Aцентр , то мы нашли индекс числа х. Если х > Aцентр , то ясно, что элемент х, если он в массиве вообще имеется, может лежать только во 2-й половине массива, а если х < Aцентр , то х может лежать лишь в первой части массива. Выходит, что за один этап алгоритма область отбора сокращена вдвое (отсюда и название – бинарный). Теперь, когда выбрана нужная половина массива, сравниваем х с центральным элементом этой половины, и т.д.

В итоге величина области поиска станет равна 1 элементу, и будет ясно, есть ли элемент х в массиве, и если да, то каков его индекс.

Пример 8: Бинарный поиск.

1

: const

2

:

n=34;

3

: var

4

:

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

5

:

i,s:byte;

6

:

x:integer;

7

:

r,l,h:integer;

8

:

zahl:integer; {Zahl - число}

9

: BEGIN

10:{Заполнение случайными элементами по неубыванию}

11:randomize;

12:M[1]:=1;

13:for i:=2 to n do

14:M[i]:=random(10)+M[i-1];

15:

16:writeln(' Исходный массив');

17:for i:=1 to n do

18:write(M[i],' ');

19:writeln;

20:

21:write('Введите число, которое надо найти');

22:readln(zahl);

23:{----------- Сам бинарный поиск -------------}

24:l:=1;{Левая граница области поиска}

25:r:=n;{Правая граница области поиска}

26:while (l<=r) do{Пока границы не пересеклись}

27:begin

28:h:=(r+l) div 2;

29:if (M[h]=zahl) then

30:begin

31:writeln('Индекс числа ',zahl,' = ',h);

32:readln;

33:exit;

34:end;

35:if M[h]>zahl then

36:r:=h-1 {выбираем левую половину области поиска}

37:else

38:l:=h+1;{выбираем левую половину области поиска}

сn log2 n

98

39:end;

40:writeln('Числа ', zahl, ' в массиве нет');

41:readln;

42:end.

6.5. Сортировка массива

Т.к. упорядоченность массива значительно ускоряет поиск элементов, то сортировка массива становится первоочередной проблемой. Для того чтобы сортировать массивы есть много методов, различающихся по скорости работы.

Простейшие алгоритмы (пузырьковая сортировка, сортировка выбором и сортировка вставками) работают за время сn2 , где n - длина массива, а с - некоторая константа. Они очень медленны при больших n , но для маленьких массивов они работают достаточно быстро.

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

раздел 8.9.), которая в худшем случае требует сn2 действий, но в среднем работающая очень быстро.

В некоторых случая можно применять сортировки, упорядочивающие массив за сn операций. С одной из них – сортировкой подсчетом – мы познакомимся в разделе

10.6.

Вособых случаях можно применять комбинированные сортировки.

Вэтой главе мы рассмотрим 3 простейшие сортировки:

1.Сортировка методом пузырька (обмена)

2.Сортировка методом выбора

3.Сортировка методом вставок

Увсех трех алгоритмов есть одно сходство: массив делится на 2 части – отсортированную и неотсортированную, причем с каждым шагом внешнего цикла количество отсортированных элементов увеличивается на 1.

В дальнейшем для определенности будем считать, что массив надо упорядочить по возрастанию.

6.6. Сортировка методом пузырька (обмена)

Алгоритм пузырька основан на следующем наблюдении: если два элемента стоят не в нужном порядке, то если их поменять местами, то массив станет более упорядоченным, чем прежде. Стало быть, чтобы отсортировать массив, надо только определить порядок, в котором будут обмениваться местами элементы. Самый простой алгоритм такой: сравниваем первый элемент со вторым, потом – второй с третьим и т.д. до конца массива. В результате, кроме того, что упорядоченность массива возрастет, можно с уверенностью сказать, что наибольший элемент точно будет находиться в конце массива (т.е. на своем месте). Поэтому сортировку и назвали пузырьковой: каждый раз по крайней мере один элемент всплывает на положенное место. Теперь можно провести еще одну точно такую же серию обменов, только пробегать можно до предпоследнего элемента, т.к. последний уже находится на своем месте.

99

Давайте посмотрим, как работает алгоритм на следующем массиве:

2

1

4

3

-2

1 этап (в примере j = n-1):

M[1] > M[2] => меняем элементы M[1] и M[2] местами:

 

1

 

2

4

3

-2

M[2] < M[3] => ничего не делаем:

 

 

 

 

 

1

 

2

4

3

-2

M[3] > M[4] => меняем элементы M[3] и M[4] местами:

1

2

3

4

-2

M[4] > M[5] => меняем элементы M[4] и M[5] местами:

1

2

3

-2

4

На этом первый этап (один виток внешнего цикла) заканчивается. После этого j становится равным n-2, и начинается второй этап. После окончания 2-го этапа массив примет вид:

1

2

-2

3

4

Думаю, что дальше алгоритм можно не расписывать. Ясно, что нужны еще 2 итерации алгоритма, чтобы массив был полностью отсортирован.

Пример 9: Сортировка методом пузырька.

1 : uses Crt;

2 : const

3 : n=100;

4 : var

5 : M:array [1..n] of integer;

6 : i,j:word;

7 : tmp:integer; {Вспомогательная переменная}

8 : BEGIN

9 : Clrscr;

10:randomize;

11:for i:=1 to n do {Заполняем массив случайными числами}

12:M[i]:= random(100);

13:writeln('Исходный массив:');

14:for i:=1 to n do

15:write(M[i],' ');

16:writeln;

17:{Алгоритм сортировки методом пузырька}

18:for j:=n-1 downto 1 do {j - граница области сортировки}

19:

for i:=1 to j do

{Проход по текущей области сортировки}

20:

if M[i]>M[i+1] then

{Если требуемый порядок нарушается,}

21:

begin

 

{то переставляем местами элементы}

22:tmp:=M[i+1];

23:M[i+1]:=M[i];

24:M[i]:=tmp;

25:end;

26:

27:writeln('Отсортированный массив');

28:for i:=1 to n do

29:write(M[i],' ');

30:readln;

31:end.

100

6.7.Сортировка выбором

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

Давайте испытаем сортировку выбором на примере:

2

1

4

3

-2

Этап 1: минимальный элемент – М[5]. Его надо поменять местами с M[1]. В результате получим массив:

 

-2

 

1

4

3

 

2

 

Этап 2: минимальный элемент (среди элементов M[2]…M[5]) – М[2]. Его надо

поменять местами с самим собой:

 

 

 

 

 

 

 

-2

 

1

4

3

 

2

 

Этап 3: минимальный элемент (среди элементов M[3]…M[5]) – М[5]. Его надо

поменять местами с M[3]. В результате получим массив:

 

 

 

-2

 

1

2

3

 

4

 

Массив уже отсортирован, но это только потому, что массив попался хороший. Так как мы рассматриваем общий случай, то надо провести еще одну итерацию алгоритма – поиск минимального элемента среди M[4],M[5].

Пример 10: Сортировка методом выбора.

1 : uses Crt;

2 : const

3 : n=100;

4 : var

5 : M:array [1..n] of integer;

6 : i,j:word;

7 : tmp:integer; {Вспомогательная переменная}

8 : ind:word;

9 : BEGIN

10:Clrscr;

11:randomize;

12:for i:=1 to n do {Заполняем массив случайными числами}

13:M[i]:= random(1000);

14:writeln('Исходный массив:');

15:for i:=1 to n do

16:write(M[i],' ');

17:writeln;

18:{Алгоритм сортировки методом выбора}

19:for i:=1 to n-1 do {Внешний цикл изменяет область сортировки}

20:begin

21: ind:=i; {индекс минимального элемента}

22:for j:=i+1 to n do {Проходим по области поиска}

23:if M[j]<M[ind] then {Если нашли элемент меньше M[ind]}

24:ind:=j; {Запоминаем индекс минимального элемента}