Задача № 22. Найти наибольший общий делитель двух натуральных чисел



Формулировка. Даны два натуральных числа. Найти их наибольший общий делитель.

Примечание: наибольшим общим делителем (сокращенно пишут НОД) двух натуральных чисел m и n называется наибольший из их общих делителей. Обозначение: НОД(m, n).

Примечание 2: общим делителем двух натуральных чисел называется натуральное число, на которое натуральное число, которое является делителем обоих этих чисел.

Например, найдем НОД(12, 8):

Выпишем все делители числа 12: 1, 2, 3, 4, 6, 12;

Выпишем все делители числа 8: 1, 2, 4, 8;

Выпишем все общие делители чисел 12 и 8: 1, 2, 4. Из них наибольшее число – 4. Это и есть НОД чисел 12 и 8.

Решение. Конечно, при решении мы не будем выписывать делители и выбирать нужный. В принципе, ее можно было бы решить как задачу 14, начав цикл с наименьшего из двух заданных чисел (так как оно тоже может быть НОД, например, НОД(12, 4) = 4). Но мы воспользуемся так называемым алгоритмом Евклида нахождения НОД, который выведен с помощью математических методов. В самом простейшем случае для заданных чисел m и n он выглядит так:

1) Если m неравно n, перейти к шагу 2, в противном случае вывести m и закончить алгоритм;

2) Если m > n, заменить m на m – n, в противном случае заменить n на n – m;

3) Перейти на шаг 1

Как видим, в шаге 2 большее из двух текущих чисел заменяется разностью большего и меньшего.

Приведем пример для чисел 12 и 8:

a. Так как 12 > 8, заменим 12 на 12 – 8 = 4;

b. Так как 8 > 4, заменим 8 на 8 – 4 = 4;

c. 4 = 4, конец.

Не проводя каких-либо рассуждений над алгоритмом и не доказывая его корректности, переведем его описание в более близкую для языка Pascal форму:

Алгоритм на естественном языке:

1) Ввод m и n;

2) Запуск цикла с предусловием m < > n. В цикле:

1. Если m > n, то переменной m присвоить m – n, иначе переменной n присвоить n – m;

3) Вывод m:

Код:

1. program GreatestCommonDiv; 2. 3. var 4.   m, n: word; 5. 6. begin 7.   readln(m, n); 8.   while m <> n do begin 9.     if m > n then begin 10.       m := m - n 11.     end 12.     else begin 13.       n := n - m 14.     end 15.   end; 16.   writeln(m) 17. end.

Задача № 23. Найти наименьшее общее кратное двух натуральных чисел

Формулировка. Даны два натуральных числа. Найти их наименьшее общее кратное.

Примечание: наименьшим общим кратным двух чисел m и n называется наименьшее натуральное число, которое делится на m и n. Обозначение: НОК(m, n)

Решение. Из теории чисел известно, что НОК(m, n) связан с НОД(m, n) следующим образом:

Следовательно, для нахождения ответа нам нужно лишь использовать предыдущую задачу нахождения НОД двух чисел m и n:

while m <> n do begin

if m > n then begin

m := m - n

end

else begin

n := n - m

end

end;

Так как исходные переменные будут испорчены в процессе работы алгоритма Евклида, нам нужно вычислить их произведение до входа в описанный выше цикл и присвоить это произведение переменной prod (от англ. product – «произведение»):

prod := m * n;

После этого нам остается вывести на экран результат арифметического выражения в правой части нашей формулы. В качестве самого НОД будет использоваться переменная m:

writeln(prod div m);

Кстати, деление в формуле будет целочисленным (через div) именно потому, что если два числа делятся на некоторое число, то и их произведение также делится на него.

Код:

1. program LeastCommonMult; 2. 3. var 4.   m, n, prod: word; 5. 6. begin 7.   readln(m, n); 8.   prod := m * n; 9.   while m <> n do begin 10.     if m > n then begin 11.       m := m - n 12.     end 13.     else begin 14.       n := n - m 15.     end 16.   end; 17.   writeln(prod div m) 18. end.

Задача № 24. Вычислить xn

Формулировка. Даны натуральные числа x и n (которое также может быть равно 0). Вычислить xn.

Решение. Для того чтобы решить эту задачу, вспомним определение степени с натуральным показателем: запись xn означает, что число x умножено само на себя n раз.

Сразу из определения видно, что здесь заранее известно количество повторений при вычислении результата, так что задача легко решается через цикл for. Выходит, мы копируем исходное число x в некоторую переменную res (от англ. result – «результат»), а затем просто умножаем его на x n раз? Не стоит торопиться с ответом.

Рассмотрим пример: 34 = 3 * 3 * 3 * 3 = 81. Если посмотреть на эту запись, то мы видим, что возведение в четвертую степень как выражение содержит четыре слагаемых, но только три операции, так как мы с первого шага домножаем число 3 на три тройки. Тогда реализация идеи из абзаца выше будет давать число в степени на 1 больше, чем требуется.

 Какой можно придумать выход? Например, можно сократить цикл на одну операцию, но что тогда будет при вычислении нулевой степени? Как известно, любое число в нулевой степени дает 1, а здесь при вводе в качестве n нуля приведет к тому, что не будет осуществлен вход в цикл (так как не существует целочисленного отрезка от 1 до 0) и в итоге на выход так и пойдет исходное число x.

А что, если изменить схему умножения так: 34 = 1 * 3 * 3 * 3 * 3 = 81? Так мы можем сравнять показатель степени и число требуемых операций, да и с нулевой степенью все становится просто, так как при вводе в качестве n нуля не будет осуществляться вход в цикл и на выход в программе пойдет число 1!

Теперь алгоритм на естественном языке:

1) Ввод x и n;

2) Присваивание переменной res числа 1;

3) Запуск цикла, при котором i изменяется от 1до n. В цикле:

1. Присваиваем переменной res значение res * x;

4) Вывод переменной res.

Код:

1. program Exponentiation; 2. 3. var 4.   x, n, i, res: word; 5. 6. begin 7.   readln(x, n); 8.   res := 1; 9.   for i := 1 to n do begin 10.     res := res * x 11.   end; 12.   writeln(res) 13. end.

Кстати, стоит понимать, что объявление переменной res при использовании типа word достаточно условно, так как этот тип принимает значения от 0 до 65535, что на единицу меньше числа 2562, хотя вводить в программу можно числа, предполагающие возведение в более высокую степень. Так как в условии задачи не сказано ничего о том, в каком числовом промежутке по x и n она должна выдавать корректный ответ, оставим это в таком виде, достаточном для проверки приложения на работоспособность.

Задача № 25. Вычислить xn по алгоритму быстрого возведения в степень

Формулировка. Даны натуральные числа x и n. Вычислить xn, используя алгоритм быстрого возведения в степень:

Решение. Разберем этот пока неясный алгоритм, представленный в нашей задаче лишь единственной формулой. Легко понять, что указанное равенство имеет место: например, 34 = 34 div 2 = (32)2 = 92 = 81. Другой пример: 45 = 4 * (42)5 div 2 = 4 * (42)2 = 4 * 162 = 4 * 256 = 1024. Причем если выражение со степенью нельзя в один шаг преобразовать таким образом, то необходимо продолжить преобразование выражения вплоть до получения конечного ответа. Однако как теперь реализовать эту схему?

Рассмотрим пример немного сложнее. Вычислим 47 = 4 * (42)3 = 4 * (16)3 = 4 * 16 * (162)1 = 4 * 16 * 256 = 16384. Стоит обратить внимание на то, что на каждом шаге при вычислении изменяется только единственное обрабатывающееся число, а остальные множители выносятся однократно и необходимы только для получения ответа в последнем действии.

Чтобы исключить их из рассмотрения, при нечетном текущем числе n мы будем домножать на них некоторую промежуточную переменную r, поначалу равную 1 (кстати, нечетность числа в Pascal определяет функция odd), затем (уже независимо от условий) возведем в квадрат текущее x и разделим n на 2 с отбрасыванием остатка. При повторении этих действий на некотором шаге n обязательно станет равным 1. Это происходит потому, что деление любого нечетного числа a на 2 с отбрасыванием остатка равносильно делению на двойку числа ( a – 1), которое является четным, и при делении, двигаясь по цепочке четных чисел, мы всегда придем к единице. Условие n = 1 и будет окончанием цикла с предусловием. По выходе из цикла необходимо будет в качестве ответа вывести последнее значение x, умноженное на r.

Теперь алгоритм на естественном языке:

1) Ввод x и n;

2) Присваивание переменной r числа 1;

5) Запуск цикла с предусловием n < > 1. В цикле:

1. Если n нечетно, домножаем r на x;

2. Переменной x присваиваем значение x * x;

3. Переменной n присваиваем результат от деления этой переменной на 2 с отбрасыванием остатка;

3) Вывод выражения x * r.

Код:

1. program FastExponentiation; 2. 3. var 4.   x, n, r: word; 5. 6. begin 7.   readln(x, n); 8.   r := 1; 9.   while n <> 1 do begin 10.     if odd(n) then r := r * x; 11.     x := x * x; 12.     n := n div 2 13.   end; 14.   writeln(x * r) 15. end.

Из анализа данного алгоритма известно, что его асимптотическая сложность порядка O(log2n).


Дата добавления: 2018-10-25; просмотров: 1080; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!