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

advertisement
1. Длинная арифметика
Диапазон представления целых чисел (Integer, Word, LongInt) ограничен. Поэтому
при решении задач всегда приходится действовать с оглядкой, — как бы не допустить
возникновения ошибки выхода за диапазон или переполнения.
Например, вычисляя факториал (n! = 1 * 2 * 3 * … * n), в диапазоне представления
величин типа Integer в Turbo Pascal удается правильно получить только 7! = 5040, а в
диапазоне представления типа LongInt — 12! = 479001600. Для больших значений,
конечно, можно использовать действительные типы данных, но это уже не гарантирует
точного результата. Поэтому полезно для получения точных значений при действиях с
многозначными числами разработать другие способы представления таких чисел, алгоритмы
выполнения арифметических и других операций, процедуры ввода и вывода результатов и
т.д.
Наиболее естественным способом представления многозначного числа является
запись каждого его разряда в виде отдельного элемента линейного массива. Пусть (для
удобства дальнейших действий) разряды "длинного" числа при записи в массив нумеруются
с единицы, начиная с разряда единиц, т.е. цифра из разряда единиц — элемент массива с
номером один, цифра из разряда десятков — элемент массива с номером два и т.д.
Задача 1. Два натуральных числа хранятся в виде массива цифр. С точки зрения
пользователя число записывается как бы в обратном порядке. Создать новый массив, в
котором будет храниться сумма этих двух чисел.
Const NMax=10;
Type TArray=array[1..NMax] of byte;
var a,b,c:Tarray;
l1,l2,k,i,j:integer;
procedure summa(X:Tarray; len_X:Integer; Y:TArray; len_Y:Integer; var Z:Tarray;
var len_Z:Integer);
var i,p,len:Integer;
begin
p:=0; // Перенос в начале равен нулю
if len_x>len_y then len_z:=len_x else len_z:=len_y;
For i:=1 to len_Z Do Begin
Z[i]:=X[i]+Y[i]+p;
p:=Z[i] div 10;
Z[i]:=Z[i] mod 10;
end;
If p=1 then begin
len_Z:=len_Z+1;
Z[len_Z]:=1
end
end;
begin
readln(l1);
readln(l2);
for i:=1 to l1 do readln(a[i]);
for i:=1 to l2 do readln(b[i]);
summa(a,l1,b,l2,c,k);
for i:=k downto 1 do write(c[i]);
end.
Но вводить числа в виде массивов, да еще и в обратном порядке, очень неудобно.
Горзадо удобнее использовать для этих целей строковый тип данных., а в дальнейшем
преобразовать в массив цифр. В процедуре учтен указанный выше способ размещения
"длинного" числа в массиве, т.е. с точки зрения пользователя число записывается как бы в
обратном порядке.
1
{Процедура преобразования длинного числа, записанного
в виде
строки, в массив цифр; переменная OK принимает значение True, если
в записи числа нет посторонних символов, отличных от десятичных
цифр, иначе — false}
Procedure Translate(S:String; Var A:DlChislo; Var OK:Boolean);
Var I : Word;
Begin
Zero(A);
I := Length(S);
OK := True;
While (I >= 1) And OK Do
Begin
If (S[I]>='0') and (S[I]<='9'
Then A[Length(S)- I + 1]:= Ord(S[I]) – Ord(‘0’)
Else OK := False; I := I - 1
End
End;
В процедуре вызывается подпрограмма Zero(A), назначение которой — запись нуля в
каждый разряд длинного числа.
{Процедура обнуления длинного числа}
Procedure Zero(Var A : DlChislo);
Var I : Integer;
Begin
For I := 1 To NMax Do A[I] := 0;
End;
Таким образом, длинное число записано в массив, где впереди (в качестве элементов с
большими номерами) стоят незначащие нули. При выполнении действий и выводе ответа
они не учитываются.
Следующая функцию – определение количества значащих цифр в записи числа, она
потребуется при реализации подпрограммы умножения.
{Функция определения количества цифр в записи длинного числа}
Function Dlina(C : DlChislo) : Integer;
Var I : Integer;
Begin
I := NMax;
While (I > 1) And (C[I] = 0) Do I := I - 1;
Dlina := I
End;
При ее разработке было использовано следующее соображение: если число не равно
нулю, то количество цифр в его записи равно номеру первой цифры, отличной от нуля, если
просмотр числа осуществляется от старшего разряда к младшему. Если же длинное число
равно нулю, то получается, что количество цифр в его записи равно одной, что и
требовалось.
Окончательный вариант программы:
Const NMax=10;
Type TArray=array[1..NMax] of byte;
var a,b,c:Tarray;
l1,l2,k,i:integer;
s1,s2:string;
ok1,ok2:boolean;
procedure summa(X:Tarray; len_X:Integer; Y:TArray; len_Y:Integer; var Z:Tarray;
var len_Z:Integer);
var i,p:Integer;
begin
p:=0; // Перенос в начале равен нулю
2
if len_x>len_y then len_z:=len_x else len_z:=len_y;
For i:=1 to len_Z Do Begin
Z[i]:=X[i]+Y[i]+p;
p:=Z[i] div 10;
Z[i]:=Z[i] mod 10;
end;
If p=1 then begin
len_Z:=len_Z+1;
Z[len_Z]:=1
end
end;
Procedure Zero(Var A : TArray);
Var I : Integer;
Begin
For I := 1 To NMax Do A[I] := 0;
End;
Procedure Translate(S:String; Var A:TArray; Var OK:Boolean);
Var I : Word;
Begin
Zero(A);
I := Length(S);
OK := True;
While (I >= 1) And OK Do
Begin
If (S[I]>='0') and (S[I]<='9')
Then A[Length(S)- I + 1]:= Ord(S[I])-Ord('0')
Else OK := False; I := I - 1
End;
End;
Function Dlina(C :TArray) : Integer;
Var I : Integer;
Begin
I := NMax;
While (I > 1) And (C[I] = 0) Do I := I - 1;
Dlina := I
End;
begin
ok1:=false;
repeat
readln(s1);
translate(s1,a,ok1);
until ok1;
ok2:=false;
repeat
readln(s2);
translate(s2,b,ok2);
until ok2;
l1:=dlina(a);
l2:=dlina(b);
summa(a,l1,b,l2,c,k);
for i:=k downto 1 do write(c[i]);
writeln;
end.
Задача 2. Два натуральных числа хранятся в виде массива цифр. С точки зрения
пользователя число записывается как бы в обратном порядке. Создать новый массив, в
котором будет храниться произведение этих двух чисел.
При составлении алгоритма используется идея умножения "столбиком", в нашем
варианте сложение выполняется не по окончанию умножения, а по ходу его, т.е. перемножив
очередные цифры, сразу же добавляем результирующую цифру в нужный разряд и
формируем перенос в следующий разряд.
3
procedure mult (X:Tarray; len_X:Integer; Y:TArray; len_Y:Integer; var Z:Tarray;
var len_Z:Integer);
var i,j:Integer; M,P:Integer;
begin
For i:=1 to len_x Do Begin
p:=0;
For j:= 1 to len_y Do Begin
M:=X[i]*Y[j]+Z[i+j-1]+P;
Z[i+j-1]:=M mod 10;
P:=M div 10;
end;
Z[i+j]:=p
end;
len_Z:=I+J-1;
If P<>0 then
inc(len_Z);
end;
Оттранслировать алгоритм для чисел 23 и 45
i
j
m
p
Z
4
Download