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