§ 2.4. Запись вспомогательных алгоритмов на языке Паскаль ГДЗ по Информатике 9 класс. Босова.
Напишите программу вычисления наименьшего общего кратного следующих четырёх чисел: 36, 54, 18 и 15. Используйте процедуру вычисления наибольшего общего делителя двух чисел.
Ответ
Наименьшее общее кратное (НОК) двух целых чисел m и n есть наименьшее натуральное число, которое делится на m и n без остатка.
НОД(m, n) и НОК(m, n) связаны соотношением:
НОД(m, n) · НОК(m, n) = m · n.
program nok;
var i, х, у, z, t1, t2: integer;
procedure nod (а, b: integer; var с: integer);
begin
while а<>b do
if а>b then а:=а-b else b:=b-а; с:=а
end;
begin
х:=36; у:=54;
nod (х, у, z);
t1:=x*y div z;
х:=18; у:=15;
nod (х, у, z);
t2:=x*y div z;
x:=t1; y:=t2;
nod (х, у, z); writeln (‘НОК = ‘ , x*y/z);
end.
Примеры различных способов вычисления НОК (наименьшего общего кратного) двух целых чисел с использованием циклов и операторов принятия решений.
НОК в C++ двух целых чисел a и b – это наименьшее положительное целое число, которое делится как на a, так и на b .
#include <iostream> using namespace std; int main() { int n1, n2, max; cout << "Enter two numbers: "; cin >> n1 >> n2; // maximum value between n1 and n2 is stored in max max = (n1 > n2) ? n1 : n2; do { if (max % n1 == 0 max % n2 == 0) { cout << "LCM = " << max; break; } else ++max; } while (true); return 0; }
Enter two numbers: 12 18 LCM = 36
В приведенной выше программе пользователя просят ввести два целых числа n1 и n2, и наибольшее из этих двух чисел сохраняется в max .
Проверяется, делится ли max на n1 и n2 , если он делится на оба числа, печатается max и цикл завершается.
Если нет, значение max увеличивается на 1, и тот же процесс продолжается до тех пор, пока max не станет делиться как на n1, так и на n2 .
Пример 2
LCM = (n1 * n2) / HCF
#include <iostream> using namespace std; int main() { int n1, n2, hcf, temp, lcm; cout << "Enter two numbers: "; cin >> n1 >> n2; hcf = n1; temp = n2; while(hcf != temp) { if(hcf > temp) hcf -= temp; else temp -= hcf; } lcm = (n1 * n2) / hcf; cout << "LCM = " << lcm; return 0; }
Читайте также
- 👉 Преобразование восьмеричного числа в десятичное и наоборот в C++
- 👉 Преобразование двоичного числа в восьмеричное и наоборот в C++
- 👉 Как перевернуть строку в C++
Вариант 1
var a,b:longint; function NOD(x,y:longint):longint; { функция поиска наиб. общ. делителя } begin if x<>0 then NOD:=NOD(y mod x,x) else NOD:=y; end; function NOK(x,y:longint):longint; { функция поиска наим. общ. кратного } begin NOK:=( x div NOD(x,y) ) * y; end; begin { основная программа } readln(a,b); writeln( 'НОД этих чисел = ', NOD(a,b) ); writeln( 'НОК этих чисел = ', NOK(a,b) ); end.
Вариант 2 Переборный алгоритм
var a, b, d: integer; begin write('Введите два числа: '); readln(a, b); if a < b then d := a + 1 else d := b + 1; {так как мы используем цикл с постусловием, необходимо минимальное значение увеличить на один, иначе цикл repeat, в силу своих конструктивных особенностей, не учтет это минимальное число и не сделает его кандидатом в НОД. Например, 5 и 25.} repeat d := d - 1 until (a mod d = 0) and (b mod d = 0); write('NOD = ', d) end.
Вариант 3
var m,n,r:integer; label lb; begin write('Введите первое число:');readln(m); write('Введите второе число:');readln(n); lb:r:=m mod n; if r=0 then writeln('НОД = ',n) else begin m:=n; n:=r; goto lb; end; end.
Вариант 4 Алгоритм Евклида с вычитанием
Пусть a и b — целые числа, тогда верны следующие утверждения:
Все общие делители пары a и b являются также общими делителями пары a — b, b;
И наоборот, все общие делители пары a — b и b являются также общими делителями пары a и b; НОД(A, B) = НОД(A — B, B), если A > B; НОД(A, 0) = A.
Доказательство:
Если t — произвольный общий делитель a и b, то он делит и разность a — b. Действительно, из a = t * u и b = t * v следует, что a — b = t * u — t * v = t * (u — v). То есть t — также общий делитель а — b и b. Обратно, если t — произвольный делитель общий делитель a — b и b, то он делит и их сумму a — b + b = a. Это можно доказать аналогично предыдущему. Поэтому t — также общий делитель a и b. Делаем вывод, что множество общих делителей a и b совпадает с множеством делителей a — b и b. В частности, совпадают и наибольшие общие делители этих пар. Наибольшее целое, на которое делится число a, есть само число а. Число 0 делится на любое число. Отсюда наибольший общий делитель а и 0 равен а. Доказанная формула(3) позволяет свести вычисление наибольшего делителя одной пары к вычислению наибольшего общего делителя другой пары, в которой числа уже меньше. Очевидная же формула (4) дает нам понять, когда надо остановиться.
var a, b: integer; begin write('a = '); readln(a); write('b = '); readln(b); while a <> b do if a > b then a := a - b else b := b - a; writeln('NOD = ', a); end.
Вариант 5 Алгоритм Евклида с делением
Пусть a и b — целые числа, а r — остаток от деления a на b. Тогда НОД(a, b) = НОД(b, r). Эта формула также позволяет свести вычисление наибольшего общего делителя одной пары чисел к вычислению наибольшего обшего делителя другой пары чисел.
var a, b: integer; begin write('a = '); readln(a); write('b = '); readln(b); while (a <> 0) and (b <> 0) do if a >= b then a := a mod b else b := b mod a; write(a + b) end.
Вариант № 6
Program test2(input,output); Const N = 5; Var С: array[1..5] of integer; A,B:integer; function HOК (A, В:integer):integer; begin HOK:=A*B/ HOD(A,B); end; function НОD(А, В:integer):integer; var X,Y:integer; begin X:= A; Y: = В; 1:IF X = Y THEN HOD:=X; IF X > Y THEN begin X:= X – Y;goto 1; end; IF Y > X THEN begin Y:= Y – X;goto 1; end; end; Begin FOR i= 1 ТО N READ (C[i]); A:= С ([l]) FOR i = 1 TO N–1 begin B:=С[i + 1]; A:= HOK(A,B); end; writeln ("HOK="; A); end.
Вариант 7
Program N_O_D (Input, Output); Var A, B: LongInt; NOD : LongInt; Begin WriteLn ('PASCAL: Нахождение Н.О.Д. двух заданных чисел.'); Writeln ('Введите числа, для которых ищется НОД:'); Write('Первое число: ');ReadLn (A); Write('Второе число: ');ReadLn (B); If (A < B)ThenNOD := A Else NOD := B; While Not( (A mod NOD = 0) and (B mod NOD = 0) ) do NOD := NOD - 1; WriteLn ('НОД = ',NOD); ReadLn; End.
Program N_O_D (Input, Output); Var A, B: LongInt; NOK, NOD : LongInt; Begin WriteLn ('PASCAL: Нахождение Н.О.К. двух заданных чисел.'); WriteLn ('Введите числа, для которых ищется НОК:'); Write ('Первое число: ');ReadLn (A); Write ('Второе число: ');ReadLn (B); If (A < B)ThenNOD := A Else NOD := B; While Not ( (A Mod NOD = 0) And (B Mod NOD = 0) ) Do NOD := NOD - 1; A := A Div NOD; B := B Div NOD; NOK := A * B * NOD; WriteLn ('НОК = ', NOK); ReadLn; End.
Наибольший общий делитель
Как несложно догадаться, наибольший общий делитель (англ. greatest
common divisor) двух целых чисел — наибольшее число, на которое делится
каждое из них. Например:
[gcd(15, 20) = 5]
[gcd(12, 30) = 6]
Сразу заметим важное свойство:
[gcd(a, b) = gcd(b, a)]
НОД играет большую роль как в математике, так и в программировании, и часто
встречается в задачах на различные темы.
Алгоритм Евклида
Алгоритм Евклида — один из первых алгоритмов в истории, использовался
ещё в Древней Греции, и дошёл до наших дней. В изначальном виде он назывался
“взаимным вычитанием”, так как заключался в поочерёдном вычитании меньшего
числа из большего, пока одно из них не станет равным 0. Сегодня чаще всего
вместо вычитания используется взятие остатка от деления, но суть алгоритма
сохранилась.
Алгоритм заключается в построении ряда чисел следующего вида ((a > b)):
[a, b, r_1, r_2, ldots, r_n]
Где каждое последующее число является остатком от деления предпредыдущего
на предыдущее:
[r_1 = a bmod b \
r_2 = b bmod r_1 \
ldots \
r_n = r_{n — 2} bmod r_{n — 1}]
Ряд заканчивается, когда остаток от деления предпоследнего числа на
последнее становится равным 0:
[r_{n — 1} bmod r_n = 0]
В таком случае утверждается, что:
[gcd(a, b) = r_n]
Для доказательства этого утверждения сначала докажем следующее:
наборы общих делителей пары ((a, b)) и пары ((b, r_1)) полностью совпадают.
Рассмотрим произвольный (не обязательно наибольший) общий делитель (a) и (b):
(t) — общий делитель (a) и (b).
(r_1 = a bmod b), или (a = bq + r_1).
Докажем, что (t) также является общим делителем (b) и (r_1).
(b) делится на (t) по определению.
(r_1 = a — bq = t * ({a over t} — {b over t} * q)), где ({a over t}) и ({b over t}) целые по определению.
Значит, (r_1) также делится на (t), что и требовалось доказать.
Из того, что все общие делители пар ((a, b)) и ((b, r_1)) совпадают,
в частности следует, что (gcd(a, b) = gcd(b, r_1)).
Далее по индукции можно доказать следующее:
[gcd(a, b) = gcd(b, r_1) = gcd(r_1, r_2) = ldots = gcd(r_{n — 1}, r_n) = gcd(r_n, 0)]
(Нуль в последнем выражении появился из условия (r_{n — 1} bmod r_n = 0)).
Нуль делится на все числа, отличные от нуля, поэтому справедливо следующее
свойство:
[gcd(x, 0) = x, для любого x in mathbb{N}.]
Следовательно,
[gcd(a, b) = r_n,]
что и требовалось доказать.
Варианты реализации алгоритма Евклида на C++
Существует несколько вариантов реализации алгоритма Евклида, как итеративных
так и рекурсивных. Классическая итеративная реализация (работающая быстрее всех
рекурсивных) выглядит так:
1 2 3 4 5 6 7 8 9 10 11 12 int gcd(int a, int b) { if (a < b) { swap(a, b); } while (b) { a %= b; swap(a, b); } return a; }Рекурсивно это же можно реализовать так:
1 2 3 4 5 6 7 8 9 10 11 int gcd(int a, int b) { if (a < b) { swap(a, b); } if (b) { return gcd(b, a % b); } else { return a; } }Преимущество рекурсивной реализации заключается в возможности записать её в
очень кратком виде (предполагающим, что (a > b)):
1 2 3 int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }На практике разница во времени работы итеративного и рекурсивного вариантов
не столь значительна, так что вы можете использовать любой из них.Время работы алгоритма Евклида
Согласно некоторым исследованиям, время работы алгоритма Евклида тесно
связано с числами Фибоначчи. Это выражается, в частности, в том, что два
последовательных числа Фибоначчи — наихудшие входные данные для алгоритма
Евклида. При (a = F_n, b = F_{n — 1}), алгоритм Евклида совершит ровно
(n — 2) итерации. Отсюда можно выразить асимптотическую сложность алгоритма:
последовательность Фибоначчи возрастает с экспоненциальной скоростью, поэтому
алгоритм Евклида работает за (O(log min(a, b))).Наименьшее общее кратное
С понятием НОД связано также понятия наименьшего общего кратного (англ.
least common multiple). Наименьшее общее кратное двух натуральных чисел —
наименьшее натуральное число, которое делится на каждое из них. Оно обозначается
следующим образом:[lcm(a, b)]
и связано с НОД формулой:
[lcm(a, b) = {a * b over gcd(a, b)}]
Реализация на C++:
1 2 3 4 5 int lcm(int a, int b) { return a / gcd(a, b) * b; //используя форму a * b / gcd(a, b), //можно получить переполнение на этапе a * b, //поэтому следует выполнять деление до умножения }Взаимнопростые числа
Числа (a) и (b) называются взаимнопростыми тогда и только тогда, когда они
не имеют общих делителей отличных от (1). То есть в их отношении должно
выполняться условие (gcd(a, b) = 1).НОД и НОК для произвольного количества чисел
Обе функции легко обобщаются для произвольного числа аргументов
последовательным применением:[gcd(a, b, c, d) = gcd(gcd(gcd(a, b), c), d)]
[lcm(a, b, c, d) = lcm(lcm(lcm(a, b), c), d)]
Задача:
Найти наименьшее общее кратное для всех элементов массива — минимальное число, которое делится на все элементы массива без остатка. Также, найти НОД всех элементов массива.
Решение:
Вот тут приведены алгоритмы расчета НОК и НОД для двух чисел. Ясно, что наиболее эффективный алгоритм расчета НОК двух чисел — это произведение чисел поделить на их НОД. По содержимому статьи ясно что НОК(а1, а2, а3, ... аN)
равен НОК(НОК(НОК(А1, А2), А3)..., АN)
. Таким образом, для расчета НОК массива чисел надо многократно расчитывать НОД двух чисел, реализация этой функции на С++ взята тут.
Реализация на Си (функции чуть-чуть изменены, так как добавлена самописная функция swap):
#include <stdio.h> #include <stdlib.h> void read_array(int n, int** values) { for (int i = 0; i < n; ++i) { printf("values[%d] = ", i); scanf("%d", &((*values)[i])); } } void print_array(int n, int* values) { for (int i = 0; i < n; ++i) { printf("values[%d] = %dn", i, values[i]); } } void swap(int* a, int* b) { int tmp = *a; *a = *b; *b = tmp; } int gcd(int a, int b) { if (a < b) { swap(&a, &b); } while (a % b != 0) { a = a % b; swap(&a, &b); } return b; } int gcd_n(int n, int* values) { if (n == 0) return -1; if (n == 1) return values[0]; int gcd_value = gcd(values[0], values[1]); for (int i = 2; i < n; ++i) { gcd_value = gcd(gcd_value, values[i]); } return gcd_value; } int lcm(int a, int b) { return (a*b)/gcd(a, b); } int lcm_n(int n, int* values) { if (n == 0) return -1; if (n == 1) return values[0]; int lcm_value = lcm(values[0], values[1]); for (int i = 2; i < n; ++i) { lcm_value = lcm(lcm_value, values[i]); } return lcm_value; } int main() { int n; int *values; printf("n: "); scanf("%d", &n); values = malloc(sizeof(int) * n); read_array(n, &values); printf("lcm: %d", lcm_n(n, values)); free(values); return 0; }