(доповнюємо незначущий нулями) p>
Write (s) p>
End; p>
WriteLn p>
End; p>
Третє завдання.
Попередня робота по опису способу зберігання, вводу та виводу
"довгих" чисел виконана. p>
У нас є все
необхідні "цеглинки", наприклад, для написання програми складання
двох "довгих" позитивних чисел. Вихідні числа і результат зберігаємо
у файлах. Назвемо процедуру складання SumLongTwo. P>
Тоді програма
введення двох "довгих" чисел та виведення результату їх складання буде мати
наступний вигляд: p>
Var A, B, C: Tlong; p>
Begin p>
Assign (Input, 'Input.txt');
Reset (Input); p>
ReadLong (A); ReadLong (B); p>
Close (Input); p>
SumLongTwo (A, B, C); p>
Assign (Output, 'Output.txt'); p>
Rewrite (Output); p>
WriteLong (C); p>
Close (Output) p>
End. p>
Алгоритм процедури
складання можна пояснити на простому прикладі. Нехай А = 870613029451,
В = 3475912100517461. P>
i p>
A [i] p>
B [i] p>
C [1] p>
C [2] p>
C [3] p>
C [4] p>
1 p>
9451 p>
7461 p>
6912 p>
1 p>
0 p>
0 p>
2 p>
1302 p>
51 p>
6912 p>
1354 p>
0 p>
0 p>
3 p>
8706 p>
9121 p>
6912 p>
1354 p>
7827 p>
1 p>
4 p>
0 p>
3475 p>
6912 p>
1354 p>
7827 p>
3476 p>
Алгоритм
імітує звичне додавання стовпчиком, починаючи з молодших розрядів. І саме
для простоти реалізації арифметичних операцій над "довгими"
числами використовується машинне подання "задом наперед". p>
Результат: С =
3476782713546912. P>
Нижче наведено
текст процедури складання двох "довгих" чисел. p>
Procedure SumLongTwo (A, B: Nlong;
Var C: Tlong); p>
Var i, k:
Integer; p>
Begin p>
FillChar (C, SizeOf (C), 0); p>
If A [0]> B [0] Then k: = A [0]
Else k: = B [0]; p>
For i: = l To k Do p>
Begin З [i +1]
: = (C [i] + A [i] + B [i]) Div Osn; p>
C [i]: = (C [i] + A [i] + B [i]) Mod Osn p>
(Чи є в цих операторах помилка?) p>
End; p>
If C [k + l] = 0 Then C [0]: = k Else
C [0]: = k + l p>
End; p>
Четверта
завдання. Реалізація операцій порівняння для "довгих" чисел (А = В,
А <В, А> В, А <= В, А> = В). P>
Function Eq (A, B: TLong):
Boolean; p>
Var i:
Integer; p>
Begin p>
Eq: = False; p>
If A [0] <> B [0] Then Exit p>
Else Begin p>
i: = l; p>
While (i <= A [0]) And (A [i] =
B [i]) Do Inc (i); p>
Eq: = i = A [0] + l p>
End p>
End; p>
Реалізація
функції А> В також прозора. p>
Function More (A, B: Tlong):
Boolean; p>
Var i:
Integer; p>
Begin If A [0]
Else
If A [0]> B [0] Then More: =
True p>
Else Begin p>
i: =
A [0]; p>
While (i
> 0) And (A [i] = B [i]) Do Dec (i); p>
If i = 0
Then More: = False p>
Else
If A [i]> B [i] Then More: = True p>
Else
More: = False p>
End p>
End; p>
Решта
функції реалізуються через функції Eq і More. p>
Function Less (A, B: Tlong):
Boolean; (A
Begin p>
Less: = Not (More (A, B) Or Eq (A, B)) p>
End; p>
Function
More_Eq (A, B: Tlong): Boolean; (A> = B) p>
Begin p>
More_Eq: = More (A, B) Or Eq (A, B) p>
End; p>
Function
Less_Eq (A, B: Tlong): Boolean; (A <= B) p>
Begin p>
Less_Eq: = Not More (A, B) p>
End; p>
Для
самостійного рішення може бути запропонована наступна, більш складна, завдання.
Потрібно розробити функцію, яка видає 0, якщо А більше В, 1, якщо А
менше В, і 2 при рівності чисел. Але порівняння повинно бути виконано з урахуванням
зсуву. Про що йде мова? Пояснимо на прикладі. Нехай А одно 56784, а В - 634.
При зсуві числа В на 2 позиції вліво функція повинна сказати, що більше В А,
без зсуву, що А більше В. Інший приклад. При А рівному 56700, а В - 567 і
зрушенні 2 функція має "сказати", що числа рівні. Рішення може
мати такий вигляд: p>
Function More (Const А, В: Tlong; Const sdvig: Integer): Byte; p>
Var i: Integer; p>
Begin p>
If A [0]>
B [0] + sdvig Then More: = 0 p>
Else
p>
If A [0]
Else Begin p>
i: =
A [0]; p>
While (i
> Sdvig) And p>
(A [i]
= B [i-sdvig]) Do Dec (i); p>
If i =
sdvig Then Begin p>
More: = 0; p>
(збіг
чисел з урахуванням зсуву) p>
For i
: = 1 To sdvig Do p>
If A [i]> 0 Then Exit; p>
More: = 2; p>
(числа
рівні, "хвіст" числа А дорівнює нулю) p>
End p>
Else
More: = Byte (A [i]
End p>
End; p>
П'ята задача.
Множення довгого числа на коротке. Під коротким розуміється ціле число типу
LongInt. P>
Процедура дуже
походить на процедуру складення двох довгих чисел. p>
Procedure Mul (Const A: TLong;
Const До:
Longlnt; Var З: TLong); p>
Var i:
Integer; p>
(результат - значення змінної С) p>
Begin p>
FillChar (С, SizeOf (С), 0); p>
If K = 0 Then Inc (С [0]) (множення на нуль) p>
Else
Begin p>
For i: = l To A [0] Do p>
Begin p>
C [i + l]
: = (LongInt (A [i]) * K + C [i]) Div Osn; p>
C [i]
: = (LongInt (A [i]) * K + C [i]) Mod Osn p>
End; p>
If C [A [0] +1]> 0 Then C [0]: = A [0]
+ 1 p>
Else C [0]: = A [0] p>
(визначаємо довжину результату) p>
End p>
End; p>
Шоста задача.
Віднімання двох довгих чисел з урахуванням зсуву p>
Якщо поняття
зсуву поки не зрозуміло, то залиште його в спокої, насправді віднімання з
урахуванням зсуву буде потрібно при реалізації операції ділення. На початку з'ясуйте
логіку роботи процедури при нульовому зсуві. p>
Введемо
обмеження: число, з якого віднімають, більше числа, яке віднімається. Працювати
з "довгими" негативними числами ми не вміємо. p>
Процедура була
б схожа на процедури додавання та множення, якби не одне "але" --
запозичення одиниці зі старшого розряду замість перенесення одиниці в старший
розряд. Наприклад, у звичайній системі числення ми віднімаємо 9 з 11 - йде
запозичення 1 з розряду десятків, а якщо з 10000 віднімаємо 9 - процес
запозичення трохи складніше. p>
Procedure Sub (Var A: TLong; Const
B: TLong; Const sp: Integer); p>
Var i, j:
Integer; p>
(з А віднімаємо В з урахуванням зсуву sp, результат віднімання в А) p>
Begin p>
For i: = l To B [0] Do p>
Begin Dec (A [i + sp], B [i ]); p>
j: = i ;{*} p>
(реалізація
складного запозичення) p>
while
(A [j + sp] <0) and (j <= A [0]) Do p>
Begin {*} p>
Inc (A [j + sp],
Osn); p>
Dec (A [j + sp + l]);
Inc (j); {*} p>
end; {*} p>
(Реалізація
простого запозичення. p>
Якщо
оператори, позначені *, замінити p>
на
наведені нижче оператори в фігурних дужках, то, p>
по
зрозумілих причин, логіка не будепрацювати p>
при
усіх вихідних даних. Можна свідомо зробити p>
помилку
і запропонувати знайти її - принцип "навчання через помилку") p>
(If
A [i + sp] <0 Then Begin Inc (A [i + sp], Osn); p>
Dec (A [i + sp + l]); End;) p>
End; p>
i: = A [0]; p>
While (i> l) And (A [i] = 0) Do
Dec (i); p>
A [0]: = i p>
(коректування
довжини результату операції) p>
End; p>
Рекомендується
виконати трасування роботи даної процедури, наприклад, для наступних вихідних
даних. Число А одно 100000001000000000000, число В - 2000073859998. P>
Сьома завдання.
Ділення двох довгих чисел, тобто знаходження цілої частини приватного та залишку. p>
Написати
вихідну (без уточнень) частина логіки не складає труднощів. Це: p>
Procedure
Long_Div_Long (Const А, В: TLong; Var Res, Ost: TLong); p>
Begin p>
FillChar (Res, SizeOf (Res), 0);
Res [0]: = 1; p>
FillChar (Ost, SizeOf (Ost), 0);
0st [0]: = 1; p>
Case More (A, B, 0) Of p>
0: MakeDel (A, B, Res, Ost); p>
(А більше В, поки не знаємо, як виконувати операцію --
"виносимо" в процедуру) p>
1:
Ost: = A; (А менше В) p>
2:
Res [l]: = l; (А одно У) p>
End; p>
End; p>
А далі?
Далі починаються проблеми. Ділити стовпчиком нас навчили у школі. Наприклад, p>
1000143123567 | 73859998 p>
- 73859998 |---------- p>
--------- | 13541 (Ціла частина приватного) p>
261543143 p>
- 221579994 p>
---------- p>
399631495 p>
- 369299990 p>
--------- p>
303315056 p>
- 295439992 p>
---------- p>
78750647 p>
- 73859998 p>
-------- p>
4890649 (Залишок) p>
Що ми робили?
На кожному етапі в розумі підбирали цифру (1, 3, 5 і т.д.), таку, що твір
цієї цифри на дільник дає число менше, але найбільш близьке до числа ...
Якому? Це важко сказати словами, але з прикладу ясно. Навіщо нам це робити в
розумі, нехай робить комп'ютер. Однак спростимо приклад, залишимо його для
тестування остаточної логіки процедури, тим більше що і числа
"довгі". Нехай число А буде менше В * 10, тоді в результаті (цілої
частини поділу) буде одна цифра. Наприклад, А дорівнює 564, а В - 63 і проста
десяткова система числення. Спробуємо підібрати цифру результату, але не методом
прямого перебору, а методом поділу відрізка навпіл. Нехай Down - верхня
межа інтервалу зміни підбираємо цифри, Up - нижня межа інтервалу,
Ost дорівнює діленим. P>
Down p>
Up p>
С = В * ((Down + Up)
Div 2) p>
Ost =
564 p>
0 p>
10 p>
315 =
63 * ((0 + 10) Div 2) p>
C
5 p>
10 p>
441 =
63 * ((5 + 10) Div 2) p>
C
7 p>
10 p>
504 =
63 * ((7 + 10) Div 2) p>
C
8 p>
10 p>
567 =
63 * ((8 + 10) Div 2) p>
C
> Ost p>
8 p>
9 p>
504 =
63 * ((8 + 9) Div 2) p>
C
Отже, результат
- Ціла частина приватного - рівний (Up + Down) Div 2, залишок від ділення - різниця
між значеннями Ost і С. Нижню кордон (Down) змінюємо, якщо результат (С)
менше залишку, верхню (Up), - якщо більше. p>
Ускладнимо
приклад. Нехай А одно 27856, а В - 354. Підставою системи числення є
не 10, а 10000. p>
Down p>
Up p>
С p>
Ost =
27856 p>
0 p>
10000 p>
1770000 p>
C
> Ost p>
0 p>
5000 p>
885000 p>
C
> Ost p>
0 p>
2500 p>
442500 p>
C
> Ost p>
0 p>
1250 p>
221250 p>
C> Ost p>
0 p>
625 p>
110448 p>
C
> Ost p>
0 p>
312 p>
55224 p>
C
> Ost p>
0 p>
156 p>
27612 p>
C
78 p>
156 p>
41418 p>
C
> Ost p>
78 p>
117 p>
34338 p>
C
> Ost p>
78 p>
97 p>
30798 p>
C
> Ost p>
78 p>
87 p>
29028 p>
C
> Ost p>
78 p>
82 p>
28320 p>
C
> Ost p>
78 p>
80 p>
27966 p>
C> Ost p>
78 p>
79 p>
27612 p>
C
Ціла частина
приватного дорівнює 78, залишок від ділення - 27856 мінус 27612, тобто 244. P>
Пора приводити
процедуру. Використовувані "цеглинки": функція порівняння чисел (More) з
урахуванням зсуву і функція множення довгого числа на короткий (Mul) описані
вище. p>
Function FindBin (Var Ost: Tlong; Const В: TLong; Const sp: Integer):
Longint; p>
Var Down, Up: Word; C: TLong; p>
Begin p>
Down: = 0; Up
: = 0sn; p>
(основа системи числення) p>
While Up - l> Down Do p>
Begin p>
(Є
можливість викладачу зробити p>
свідому
помилку. Змінити умова p>
циклу
на Up> Down. Результат - зациклення програми.) P>
Mul (В, (Up + Down) Div 2, С); p>
Case More (Ost, C, sp) Of p>
0: Down: = (Down + Up) Div 2; p>
1: Up: = (Up + Down) Div 2; p>
2: Begin Up: = (Up + Down) Div 2;
Down: = Up End; p>
End; p>
End; p>
Mul (B, (Up +
Down) Div 2, C); p>
If More (Ost,
C, 0) = 0 Then Sub (Ost, C, sp) p>
(знаходимо залишок від ділення) p>
Else begin Sub (C, Ost, sp); Ost: =
C end; p>
FindBin: = (Up
+ Down) Div 2; p>
(ціла частина приватного) p>
End; p>
Залишилось
розібратися зі зрушенням, значенням змінної sp в нашому викладі. Знову
повернемося до звичайної системі числення і спробуємо розділити, наприклад, 635 на
15. Що ми робимо? Спочатку ділимо 63 на 15 і формуємо, підбираємо в розумі першим
цифру результату. Підбирати за допомогою комп'ютера ми навчилися. Підібрали - це
цифра 4, і це старша цифра результату. Змінимо залишок. Якщо спочатку він був
635, то зараз став 35. Віднімати з урахуванням зсуву ми вміємо. Знову підбираємо
цифру. Другу цифру результату. Це цифра 2 і залишок 5. Отже, результат (ціла
частина) 42, залишок від ділення 5. А що зміниться, якщо підставою буде не 10,
а 10000? Логіка співпадає, тільки в розумі вважати дещо важче, але ж у
нас же є молоток під назвою комп'ютер - нехай він забиває цвяхи. p>
Procedure MakeDel (Const А, В: TLong; Var Res, Ost: TLong); p>
Var sp: Integer; p>
Begin p>
Ost: = A; (первинне значення залишку) p>
sp: = А [0] - В [0]; p>
If More (А, В, sp) = l Then Dec (sp); p>
(B * Osn> A, в результаті одна цифра) p>
Res [0]: = sp + l; p>
While sp> =
0 Do p>
Begin p>
(знаходимо чергову цифру результату) p>
Res [sp
+ 1]: = FindBin (Ost, B, sp); p>
Dec (sp) p>
End p>
End; p>
Методичні
рекомендації. Представлений матеріал викладається на чотирьох заняттях з
відомою схемою: 10-15-хвилинне виклад ідей, а потім робота учнів під
керівництвом викладача. p>
1-е заняття.
Введення, висновок і складання довгих чисел (завдання 1, 2, 3). P>
2-е заняття.
Функції порівняння (задача 4). P>
3-е заняття.
Множення і віднімання довгих чисел (завдання 5, 6). P>
4-е заняття.
Розподіл довгих чисел (завдання 7). Безумовно, ця схема не догма. Залежно
від рівня підготовки учнів на самостійне виконання може бути винесена
значна частина матеріалу. Зазначу тільки, що в силу традицією, що склалася в
ряді випадків допускаються при викладі свідомі помилки. У результаті роботи
кожен учень повинен мати власний модуль для роботи з
"довгими" числами. p>
Теми для
досліджень p>
1. Рішення
задач: пошук найбільшого загального дільника двох "довгих" чисел; пошук
найменшого спільного кратного двох "довгих" чисел; витяг
квадратного кореня з "довгого" числа і т.д. p>
2.
"Довгі" числа можуть бути негативними. Як зміняться описані
вище операції для цього випадку? p>
3. Для зберігання
"довгих" чисел використовується не масив, а стек, реалізований з
допомогою списку. Змінити модуль роботи з "довгими" числами. P>
Список
літератури h2>
С.М. Окулов.
"Довга" арифметика. P>
Для підготовки
даної роботи були використані матеріали з сайту http://www.comp-science.ru/
p>