Задачі на
довгу арифметику  h2>
 Розглянемо
досить популярну в програмуванні завдання на роботу з
"довгими" числами. Реально з "астрономічними" або
"мікроскопічними" числами доводиться стикатися не так вже й
часто. Тим не менше, вправи, що розглядаються в цій публікації, можуть
послужити хорошою тренуванням в області програмування і зайняти гідне
місце в класах з поглибленим вивченням інформатики або на гуртках з програмування.
Алгоритми, представлені нижче, записані на Turbo Pascal, версія7.0. При
бажанні або необхідності вони можуть легко бути адаптовані до будь-якої іншої
програмному середовищі.  p>
 Діапазон
подання цілих чисел (Integer, Word, LongInt) обмежений, про що не раз вже
говорилося (втім, для дійсних величин це зауваження теж актуально).
Тому при вирішенні завдань завжди доводиться діяти з осторогою, - як би не
допустити виникнення помилки виходу за діапазон або переповнення. Наприклад,
обчислюючи факторіал (n! = 1 * 2 * 3 * ... * n), в діапазоні подання величин типу
Integer вдасться правильно отримати тільки 7! = 5040, а в діапазоні подання
типу LongInt - 12! = 479001600. Для більших значень, звичайно, можна використовувати
дійсні типи даних, але це вже не гарантує точного результату.
Тому корисно для отримання точних значень при дії з багатозначними
числами розробити інші способи представлення таких чисел, алгоритми
виконання арифметичних та інших операцій, процедури введення та виведення
результатів і т.д.  p>
 Покажемо
реалізацію рішення такого роду завдань на прикладі множення одного багатозначного
числа на інше. Саме ця арифметична операція найбільш часто використовується
при вирішенні інших завдань.  p>
 Найбільш
природним способом представлення багатозначного числа є запис кожного
його розряду у вигляді окремого елемента лінійного масиву (або списку, де
пам'ять під цифру буде приділятися в міру потреби, в той час як в масиві
доводиться наперед задавати максимальну кількість елементів у ньому). Нехай
(для зручності подальших дій) розряди "довгого" числа при
записи в масив нумеруються з одиниці, починаючи з розряду одиниць, тобто фігура з
розряду одиниць - елемент масиву з номером один, цифра з розряду десятків - елемент
масиву з номером два і т.д. Визначимо для роботи з "довгими"
невід'ємними числами тип даних:  p>
 Const MNax = 2000;  p>
 Type
Digit = 0 .. 9;  p>
DlChislo = Array [1 .. Nmax] Of Digit;  p>
 Для вирішення
поставленого завдання необхідно вміти виконувати наступні дії:  p>
 1) введення
"довгого" числа;  p>
 2) власне
множення двох "довгих" чисел;  p>
 3) висновок
"довгого" числа;  p>
 4) визначення
кількості цифр у записі числа.  p>
 Кожну з
підзадач реалізуємо у вигляді окремої підпрограми. Почнемо з введення. Ввести велике
число доцільно у вигляді рядка, а в подальшому перетворити в масив цифр.
У процедурі врахований вказаний вище спосіб розміщення "довгого" числа в
масиві, тобто з точки зору користувача число записується як би у зворотному
порядку.  p>
 (Процедура перетворення
довгого числа, записаного  p>
 у вигляді рядка, в масив цифр; мінлива OK
приймає значення True,  p>
 якщо в запису числа немає сторонніх символів,
відмінних від десяткових  p>
 цифр, інакше - false)  p>
 Procedure Translate (S: String; Var
A: DlChislo; Var OK: Boolean);  p>
 Var I: Word;  p>
 Begin  p>
 Zero (A); I
: = Length (S); OK: = True;  p>
 While (I
> = 1) And OK Do  p>
 Begin  p>
 If
S [I] In ['0 '.. '9']  p>
 Then
A [Length (S) - I + 1]: = Ord (S [I]) - 48  p>
 Else
OK: = False; I: = I - 1  p>
 End  p>
 End;  p>
 У процедурі
викликається підпрограма Zero (A), призначення якої - запис нуля в кожен
розряд довгого числа. Ось текст цієї процедури:  p>
 (Процедура обнулення довгого числа)  p>
 Procedure Zero (Var A: DlChislo);  p>
 Var I:
Integer;  p>
 Begin  p>
 For I
: = 1 To NMax Do A [I]: = 0;  p>
 End;  p>
 Таким чином,
довге число записано в масив, де попереду (як елементи з великими
номерами) стоять незначущий нулі. При виконанні дій і виведення відповіді вони не
враховуються.  p>
 Зараз
розробимо функцію визначення кількості значущих цифр у записі числа,
оскільки вона буде потрібно при реалізації підпрограми множення.  p>
 (Функція визначення кількості цифр у
запису довгого числа)  p>
 Function Dlina (C: DlChislo):
Integer;  p>
 Var I:
Integer;  p>
 Begin  p>
 I: =
NMax;  p>
 While (I
> 1) And (C [I] = 0) Do I: = I - 1;  p>
 Dlina: =
I  p>
 End;  p>
 При її
розробці було використане таке міркування: якщо кількість не дорівнює нулю,
то кількість цифр у його записи одно номером перші цифри, відмінною від нуля,
якщо перегляд числа здійснюється від старшого розряду до молодшого. Якщо ж
довге число дорівнює нулю, то виходить, що кількість цифр у його записи одно
одній, що і було потрібно.  p>
 Ну і, нарешті,
головна процедура, заради якої і була зроблена вся попередня робота. При
складанні алгоритму використовується ідея множення "стовпчиком", хоча в
нашому варіанті складання виконується не після закінчення множення, а по ходу його,
тобто перемноживши чергові цифри, відразу ж додаємо результуючу цифру в
потрібний розряд і формуємо перенесення в наступний розряд.  p>
 (Процедура
множення довгих чисел.  p>
 A, B - множники, C - твір)  p>
 Procedure Multiplication (A, B:
DlChislo; Var C: DlChislo);  p>
 Var I, J:
Integer; P: Digit; VspRez: 0 .. 99;  p>
 Begin  p>
 Zero (C);  p>
 For I: = 1
To Dlina (A) Do (Цикл за кількістю цифр  p>
 в першому числі)  p>
 Begin  p>
 P: = 0; (Спочатку перенесення дорівнює
нулю)  p>
 For J: = 1 To Dlina (B) Do (Цикл по
кількістю цифр  p>
 в другому
числі)  p>
 Begin  p>
VspRez: = A [I] * B [J] + P + C [I + J - 1];  p>
 C [I + J - 1]: = VspRez Mod 10; (Чергове
значення цифри в  p>
розряді I + J - 1)  p>
 P: = VspRez Div 10 (Перенесення в наступний розряд)  p>
 End;  p>
 C [I + J]: = P (останній перенесення може
бути відмінний від нуля,  p>
 запишемо його в поки що
вільний розряд)  p>
 End  p>
 End;  p>
 Зараз приведемо
лістинг програми цілком.  p>
 Program DlUmn;  p>
 Const NMax = 2000;  p>
 Type Digit = 0 .. 9; DlChislo = Array [1 .. Nmax] Of Digit;  p>
 Var S: String;  p>
 M, N, R, F
: DlChislo;  p>
 I, MaxF:
Word;  p>
 Logic: Boolean;  p>
 (Процедура
обнулення довгого числа)  p>
 Procedure Zero (Var A: DlChislo);  p>
 Var I: Integer;  p>
 Begin  p>
 For I: = 1
To NMax Do A [I]: = 0;  p>
 End;  p>
 (Функція
визначення кількості цифр у записі довгого числа)  p>
 Function Dlina (C: DlChislo): Integer;  p>
 Var I: Integer;  p>
 Begin  p>
 I: = NMax;  p>
 While (I
> 1) And (C [I] = 0) Do I: = I - 1;  p>
 Dlina: = I  p>
 End;  p>
 (Процедура
друку довгого числа)  p>
 Procedure Print (A: DlChislo);  p>
 Var I: Integer;  p>
 Begin  p>
 For I: =
Dlina (A) DownTo 1 Do Write (A [I]: 1);  p>
 WriteLn  p>
 End;  p>
 (Процедура
перетворення довгого числа в масив цифр)  p>
 Procedure Translate (S: String; Var A: DlChislo;  p>
Var OK: Boolean);  p>
 Var I: Word;  p>
 Begin  p>
 Zero (A); I
: = Length (S); OK: = True;  p>
 While (I
> = 1) And OK Do  p>
 Begin  p>
 If S [I]
In ['0 '.. '9']  p>
 Then
A [Length (S) - I + 1]: = Ord (S [I]) - 48  p>
 Else OK: =
False;  p>
 I: = I --
1  p>
 End  p>
 End;  p>
 Procedure Multiplication (A, B: DlChislo; Var C:
DlChislo);  p>
 Var I, J: Integer; P: Digit; VspRez: 0 .. 99;  p>
 Begin  p>
 Zero (C);  p>
 For I: = 1 To
Dlina (A) Do  p>
 Begin P: = 0;  p>
 For J
: = 1 To Dlina (B) Do  p>
 Begin  p>
VspRez: = A [I] * B [J] + P + C [I + J - 1];  p>
 C [I + J - 1]: = VspRez Mod 10;  p>
 P: = VspRez Div 10  p>
 End;  p>
 C [I +
J]: = P  p>
 End  p>
 End;  p>
 (Основна
програма)  p>
 Begin  p>
 Repeat (повторюємо введення, поки число не буде
введено правильно)  p>
 Write ( 'Введіть перший множник:'); p>
 ReadLn (S); Translate (S, M, Logic)  p>
 Until Logic;  p>
 Repeat  p>
 Write ( 'Введіть другий множник:'); p>
 ReadLn (S); Translate (S, N, Logic)  p>
 Until Logic;  p>
Multiplication (M, N, R); Print (R)  p>
 End.  p>
 У наведеному
лістингу Print - процедура виведення довгого числа. Надамо читачеві
самостійно розібратися в алгоритмі її роботи.  p>
 Повернемося до
обчислення факторіалу. Використовуючи розроблені підпрограми, визначимо,
значення факторіалу якого максимального числа можна розмістити в пам'яті при
таке подання довгих чисел.  p>
 Ось змінений
фрагмент основної програми, що вирішує поставлене завдання.  p>
 Begin  p>
 MaxF: =
810;  p>
 Zero (F);  p>
 F [1]: = 1;  p>
 For I: = 1
To MaxF Do  p>
 Begin  p>
 Str (I, S); (перетворення числа I до
строковому типу S)  p>
 Translate (S, M,
Logic);  p>
Multiplication (F, M, F);  p>
Print (F);  p>
WriteLn ( 'Факторіал числа', I: 4, 'містить', Dlina (F), 'цифр .') p>
 End  p>
 End.  p>
 Розрахунки
показали, що можна обчислювати факторіали до значення 810! включно, в
запису якого 1999 цифр. Далі знову виникає переповнення. Розрахунки за
програмі тривають близько 5 хвилин (IBM PC з процесором Pentium-100).  p>
 Нижче буде
запропонований список завдань для самостійного виконання. З них, на думку
автора, найбільшу складність представляють реалізації алгоритмів поділу одного
довгого числа на інше і витяг квадратного кореня. Алгоритм добування
квадратного кореня докладно описаний у довіднику В.А. Гусєва та А.Г. Мордковіча
[7]. У деяких випадках складені програми можуть виступати як
підпрограми при розробці алгоритмів вирішення інших, більш складних (як у
прикладі з факторіали), завдань. Крім авторських задач і завдань зі списку
літератури тут наведені завдання з олімпіад школярів з програмування,
що проводилися в Пермської області в 1989-99гг.  p>
 Завдання для
самостійного рішення  p>
 Скласти
програму порівняння двох багатозначних чисел (кількість знаків у записі чисел
более20).  p>
 Скласти
програму, що підсумовує два натуральних багатозначних числа з кількістю знаків
более20.  p>
 Скласти
програму обчислення ступеня an, якщо a> MaxInt, n> 10.  p>
 Скласти
програму обчислення числа 264 - 1, в результаті зберегти всі цифри.  p>
 Скласти
програму обчислення 100!.  p>
 Скласти програму
витягу точного квадратного кореня з n-розрядного числа (n> 40).  p>
 Скласти
програму обчислення точного значення n!, де n> 12.  p>
 Скласти
програму обчислення точного значення nn, де n> 10.  p>
 Скласти
програму ділення числа a на число b, якщо a, b - багатозначні числа.  p>
 Обчислити 100!
+ 2100.  p>
 Обчислити 100!
- 2100.  p>
 Обчислити 7123.
 p>
 Зустрічаються чи
серед цифр числа 211213 - 1 дві поспіль йдуть дев'ятки?  p>
 Обчислити
2-200.  p>
 Скласти
програму знаходження приватного та залишку від ділення m-значного числа на
n-значне (m, n> 20).  p>
 З'ясувати, яке
з чисел am, bn більше і на скільки (a, b <= 40000; m, n <= 10).  p>
 Знайти n знаків
в десяткового запису квадратного кореня з цілого числа m (n> = 50).  p>
 Знайти
кількість дільників n-значний натурального числа (n> 20).  p>
 Обчислити
точне значення (n!)! (n> = 3).  p>
 Скласти
програму обчислення точного значення суми 1! + 2! + 3! + ... + N! при
n> 10.  p>
 Скласти
програму обчислення точного значення суми дробів  p>
  p>
  p>
 при n> 10.
Відповідь має бути представлений у вигляді несократімой дробу P/Q, де P, Q --
натуральні числа.  p>
 Обчислити
точне значення (nn)! при n> = 3.  p>
 Скласти
програму обчислення точного значення суми перших n членів послідовності
1, k, k2, k3, ..., kn (n> MaxInt). Вказівка: використовуйте формулу суми n
членів геометричної прогресії.  p>
 Скласти
програму обчислення точного значення суми перших n членів послідовності
чисел, кратних даного натурального числа k (n> MaxInt). Вказівка:
використовуйте формулу суми n членів арифметичної прогресії.  p>
 Обчислити
точне значення суми 12 + 22 + 32 + ... + n2 (n> = 20000).  p>
 Обчислити
точне значення суми 1n + 2n + 3n + ... + Nn (n> = 10).  p>
 Знайти першу
просте число, яке більше 1011.  p>
 Скласти
програму обчислення точного значення многочлена anxn + an - 1xn - 1 + ... +
a1x + a0, де aiіx - цілі числа більше 1011.  p>
 Знайти
найбільший спільний дільник і найменше спільне кратне чисел m і n (m,
n> = 1011).  p>
 Перевірити,
чи є числа m і n (m, n> = 1011) взаємно простими.  p>
 Доведіть, що
число 219936 * (219937 - 1) є досконалим, тобто дорівнює сумі всіх своїх
дільників, крім самого себе.  p>
 "Обертове
число ". Написати програму, яка знаходить число, що володіє наступними
властивостями:  p>
 1) число
закінчується на 5;  p>
 2) при
примноження його на 5 утворюється нове число, яке може бути отримано з
вихідного викресленням цифри 5 на кінці і переписуванням її в початок числа.  p>
 33. Дана
послідовність, задана рекурентних формулою  p>
 an + 1 = 7an mod 2023, a1 = 1,  p>
 де x mod y
означає залишок від розподілу x на y. Написати програму, яка обчислює an при
1 <= n <= 1000000000000000000000.  p>
 Дана
послідовність  p>
  p>
  p>
 Написати
програму, що знаходить точне значення an при 1 <= n <= 150.  p>
 Приклад. При n =
58 отримуємо an = 10359022039470231387111424.  P>
 35. Напишіть
програму перекладу багатозначного числа (з кількістю знаків більше 20) в
системи числення з основою два, вісім, шістнадцять.  p>
 36. Розкласти
на прості множники натуральне число з кількістю знаків більш 11.  p>
 37. Множення
періодичної дробу. Задана деяка позитивна правильна періодична
дріб Q і натуральне число N. Числа Q і N такі, що кількість цифр,
використовуються для їх опису, не перевершує 100. При зображенні дробу Q періодична
частина полягає в круглі дужки.  p>
 Потрібно
написати програму, яка визначає результат множення Q на N, тобто
неперіодичних частину і мінімальний період числа Q * N.  p>
 У разі
отримання результату множення у вигляді кінцевої дробу дужки опускаються.  p>
 Приклад роботи
правильної програми  p>
 Введіть
періодичну дріб: 0.1 (6)  p>
 Введіть
натуральне число: 2  p>
 Відповідь: 0. (3)  p>
 Список
літератури  h2>
 Абрамов С.А.,
Гнездилова Г.Г., Капустина Е.Н., Селюн М.І. Завдання з програмування. М.:
Наука, 1988.  p>
 Олімпіади з
інформатики. Завдання й рішення. Методичні рекомендації для вчителів і
учнів шкіл. Красноярськ, 1991.  p>
 Пильщиків В.Н.
Збірник вправ з мови Паскаль. М.: Наука, 1989.  p>
 Касаткин В.Н.
Інформація. Алгоритми. ЕОМ. М.: Просвещение, 1991.  p>
 Хонсбергер Р.
Математичні родзинки. М.: Наука, 1992.  p>
 Семакін І.Г.,
Шестаков А.П. Лекції з програмування. Перм: изд-во ПГУ, 1998.  p>
 Гусєв В.А.,
Мордковіч А.Г. Математика. Довідкові матеріали. М.: Просвещение, 1990.  p>
 Гладков В.П.
Курс лабораторних робіт з програмування. Перм: изд-во ПДТУ, 1998.  p>
 Шестаков А.П.
Завдання на довгу арифметику/ p>
 Для підготовки
даної роботи були використані матеріали з сайту http://www.comp-science.narod.ru/
 p>