Как написать программу вычисления наименьшего общего кратного

Информатика 9 класс Босова ФГОС

§ 2.4. Запись вспомогательных алгоритмов на языке Паскаль ГДЗ по Информатике 9 класс. Босова.


Напишите программу вычисления наименьшего общего крат­ного следующих четырёх чисел: 36, 54, 18 и 15. Исполь­зуйте процедуру вычисления наибольшего общего делителя двух чисел.

Ответ

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

НОД(mn) и НОК(mn) связаны соотношением:
НОД(mn) · НОК(mn) = 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;
}

Понравилась статья? Поделить с друзьями:
  • Как написать программу браузер
  • Как написать программу бота
  • Как написать программу базу данных
  • Как написать программу антивирус
  • Как написать программу jar