Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
delphi / песни о паскале.pdf
Скачиваний:
63
Добавлен:
26.03.2016
Размер:
5.16 Mб
Скачать

Глава 46 Огромные числа

Давно минули времена, когда для счета всем хватало своих пальцев — первого «калькулятора» человечества, а наши потребности в счете всё растут и растут...

Сколько звезд на небе?

Некий король — любитель науки, порядка и немножко чудак — распорядился пересчитать всё, что ни есть, в своих владениях. Ладно бы, его интересовали крупные предметы вроде домов, машин и всего такого… Но нет, король велел пересчитать и капли в морях, и песчинки на берегах, и травинки в степях! Пока ученые придумывали способы подсчета, программисты возились с другой задачей

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

Первый из них — тип LongInt — длинное целое число. Наибольшее значение для этого типа чисел составляет 2'147’483’647, то есть несколько больше двух миллиардов. Маловато будет, — рассудили мудрецы, и продолжили поиск.

Вскоре они наткнулись на другой тип чисел — Extended, который мог вмещать сказочные значения — вплоть до 104932 ! Иначе говоря, эти числа могли содержать почти пять тысяч цифр! Но радость математиков была недолгой; рассмотрев тип Extended пристальней, они отвергли его. Оказалось, что из этих пяти тысяч цифр только первые двадцать — точные (их называют значащими), а остальные не внушают доверия, и могут быть замены чем угодно, хоть нулями.

Есть ли толк от этих неточных чисел? Есть. Дело в том, что в инженерных и научных расчетах этой точности вполне хватает. Но здесь был иной случай, — требовался абсолютно точный подсчет, государь не терпел огрехов. «Точность — вежливость королей!» — говаривал он. И программисты ткнулись, как обычно, в тупик.

Сложение «в столбик» никто не отменял

Выход из тупика нашелся случайно. Один из королевских программистов помогал сынишке справиться со школьными уроками — складывали числа «в столбик». Тут его и осенило: почему бы и нам, — смекнул папаша, — не складывать числа тем же способом? Так тряхнем стариной и вспомним это сложение «в столбик»? Рис. 102 освежит вашу память.

350

Глава 46

Огромные числа

Перенос

1 0 1

Первое слагаемое

7 0 7

Второе слагаемое

+

3 4 5

Сумма

1

0

5

2

 

 

 

 

Рис. 102 – Пример сложения в столбик

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

Если обозначить складываемые цифры буквами A и B, то алгоритм сложения «в столбик» для одного разряда с учетом предыдущего переноса запишется так.

цифра := (A + B + перенос) mod 10

перенос := (A + B + перенос) div 10

Напомню, что операция MOD вычисляет остаток от деления одного целого числа на другое, а операция DIV — частное с отбрасыванием остатка. Перед сложением самого младшего разряда перенос берётся равным нулю. Обратите внимание, что условный оператор здесь излишен.

Великая стройка

Уловив основную идею — действия «в столбик», — программисты задумались над хранением цифр огромного числа. Они рассмотрели два равно подходящих средства: либо массив байтов, каждый из которых будет содержать числа от 0 до 9, либо массив символов «0»…«9». Ученые остановились на символах, но от строки отказались, поскольку строка вмещает лишь 255 символов,

аим требовалось больше.

Витоге объявление сверхбольшого числа получилось таким, как показано в программе P_46_1, — она была написана для отладки процедуры распечатки сверхбольшого числа.

351

Глава 46

Огромные числа

{P_46_1 – Распечатка сверхбольших чисел }

{объявления для сверхбольшого числа } const CSize = 500; { размер массива для цифр } type TBigNumber = array [1..CSize] of char;

var BN : TBigNumber; { очень большое число! } { Процедура распечатки сверхбольшого числа

Младшие цифры числа располагаются в младших элементах массива.

Но распечатывать надо, начиная со старших цифр.

Поэтому обработку массива ведем от конца к началу.

При этом старшие позиции, заполненные пробелами, не печатаем.} procedure WriteBigNumber(var F: text; const aNum: TBigNumber);

var i : integer; begin

i:= SizeOf(aNum); { печать начинаем со старших цифр }

{Пока встречаются незначащие цифры, пропускаем их } while (i>0) and not (aNum[i] in ['1'..'9']) do Dec(i);

{Если весь массив заполнен пробелами, то печатаем ноль } if i=0 then Write(F, '0');

{Теперь печатаем оставшиеся цифры } while i>0 do begin

Write(F, aNum[i]); Dec(i);

end;

{Добавляем ещё одну пустую строчку для удобства созерцания } Writeln(F); Writeln(F);

end;

 

 

var

i : integer;

 

begin

{ === Главная программа === }

FillChar(BN, SizeOf(BN), ' ');

{ заполняем пробелами }

WriteBigNumber(Output, BN);

 

FillChar(BN, SizeOf(BN), '7');

{ заполняем семерками }

WriteBigNumber(Output, BN);

 

{ заполняем случайными цифрами }

for i:=1 to CSize-1 do BN[i]:= Char(Random(100) mod 10 + Ord('0')); WriteBigNumber(Output, BN);

Readln;

end.

Итак, тип данных TBigNumber — это сверхбольшое число в виде массива из 500 цифр. Процедура WriteBigNumber — печать сверхбольшого числа — выполняет то, о чем говорит её название. Напомню, что примененная здесь процедура Dec(i) выполняет быстрое вычитание единицы.

352

Глава 46

Огромные числа

В главной программе вы найдете процедуру FillChar — «заполнить символом». Для заполнения массива можно организовать цикл, но процедура FillChar делает это проще и быстрее, она объявлена в Паскале так.

procedure FillChar(var X; Count: Integer; Value: Byte);

Обратите внимание, что тип первого параметра X не указан, что крайне редко для Паскаля! По сути это ссылка на переменную любого типа. Второй параметр — Count — задает количество байтов, помещаемых в переменную X. Обычно значение Count совпадает с размером этой переменной и задается равным SizeOf(X). И, наконец, третий параметр Value — «значение», тоже не совсем обычен. Его тип объявлен как байт (то есть число), но в действительности может принимать любой однобайтовый тип данных, например, символ или булево значение. Вот несколько примеров.

var A : array 1..100

of char;

 

B : array 1..200

of byte;

 

С : array 1..50 of boolean;

 

. . .

 

 

FillChar(A, SizeOf(A), ’*’);

{ заполнение массива звездочками }

FillChar(B, SizeOf(B), 0);

{ заполнение массива нулем }

FillChar(C, SizeOf(C), false);

{ заполнение массива «ложью» }

 

 

 

Согласитесь, нелегко отказаться от применения столь удобной процедуры.

И последнее. В нашу процедуру WriteBigNumber передается ссылка на выходной файл, что придает ей универсальность. Вызывая её из главной программы, мы передаём туда файловую переменную Output, — это файл, связанный с экраном. Напомню, что файл Output не требует ни объявления, ни открытия, ни закрытия — он встроен в язык готовеньким. Существует и встроенный файл по имени Input — он служит для ввода данных с клавиатуры.

Длинная арифметика

Итак, испытав рассмотренную нами программу, королевские программисты сделали первый шаг к своей цели — освоили распечатку сверхбольших чисел. Теперь предстояло написать процедуру для сложения таких чисел, ей дали имя AddNumbers — «сложить числа». Она принимает два параметра — это ссылки на сверхбольшие числа, то есть на массивы. Работа процедуры основана на формулах сложения в столбик, причем младшей цифрой числа был выбран первый элемент массива.

Поскольку массив содержит символы ’0’…’9’, а не числа 0…9, при сложении символы преобразуем в числа и обратно (ведь символы складывать нельзя). Эти простые превращения выполняем по формулам.

353

Глава 46

Огромные числа

цифра := Ord (символ_цифры) – Ord (’0’)

символ_цифры := Char (Ord (’0’) + цифра)

Вот эта чудесная программа целиком.

{ P_46_2 – Сложение сверхбольших чисел }

const CSize = 500; { размер массива }

{ объявление типа для сверхбольшого числа }

type

TBigNumber = array [1..CSize] of char;

var BN1, BN2 : TBigNumber;

{ два очень больших числа }

{ Процедура распечатки сверхбольшого числа }

procedure WriteBigNumber(var F: text; const aNum: TBigNumber); var i : integer;

begin i:=CSize;

while (i>0) and not (aNum[i] in ['1'..'9']) do Dec(i); if i=0 then Write(F, '0');

while i>0 do begin Write(F, aNum[i]); Dec(i);

end;

Writeln(F); Writeln(F); end;

{Процедура сложения сверхбольших чисел в столбик.

Результат помещается в первое число, что равносильно оператору сложения aNum1 := aNum1 + aNum2 }

procedure AddNumbers(var aNum1, aNum2 : TBigNumber);

var i,j

: integer;

 

n1,

n2 : integer;

{ слагаемые цифры }

sum, ovr : integer;

{ сумма и перенос }

354

 

 

Глава 46

 

 

 

 

Огромные числа

 

 

 

 

 

 

 

 

begin

 

 

 

 

ovr:=0;

{ в начале переполнение = 0 }

 

 

 

{ цикл по всем цифрам, кроме последней }

 

 

 

for i:=1 to CSize-1 do begin

 

 

 

j:=i;

{ j используется после завершения цикла }

 

 

{ Если в текущей позиции пробел, то считаем его нулем,

 

 

а иначе символ цифры преобразуем в цифру 0..9 }

 

 

if aNum1[i]=' '

 

 

 

 

then n1:=0

 

 

 

 

else n1:=Ord(aNum1[i])-Ord('0');

{ n1 = 0..9 }

 

 

if aNum2[i]=' '

 

 

 

 

then n2:=0

 

 

 

 

else n2:=Ord(aNum2[i])-Ord('0');

{ n2 = 0..9 }

 

sum:= (n1+n2+ovr) mod 10;

{ сумма sum = 0..9 }

ovr:= (n1+n2+ovr) div 10;

{ перенос ovr = 0 или 1 }

{ Преобразуем цифру в символ цифры }

 

aNum1[i]:= Char(sum + Ord('0'));

 

end;

 

{ Если было переполнение, то за последней цифрой помещаем единицу } if ovr<>0 then aNum1[j+1]:='1';

end;

 

 

var F : text;

i : integer;

begin

{ === Главная программа === }

Assign(F, ''); Rewrite(F);

FillChar(BN1, SizeOf(BN1), ' '); FillChar(BN2, SizeOf(BN2), ' ');

for i:=1 to CSize-1

do BN1[i]:= Char(Random(100) mod 10 + Ord('0'));

for i:=1 to CSize-1

do BN2[i]:= Char(Random(100) mod 10 + Ord('0'));

WriteBigNumber(F, BN1);

{ первое слагаемое }

WriteBigNumber(F, BN2);

{ второе слагаемое }

AddNumbers(BN1, BN2);

 

WriteBigNumber(F, BN1);

{ сумма }

Close(F); Readln;

 

 

end.

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

355

Соседние файлы в папке delphi