Делитель целого числа a — это целое число d, не равное нулю, на которое a делится без остатка: a = d * k для некоторого целого k. Кратное числа d — любое число вида d * k. Это одна связь, увиденная с двух сторон: «3 — делитель 12» и «12 кратно 3» значат одно и то же. Ниже — как найти все делители числа, признаки делимости, НОД и НОК и алгоритм Евклида в коде (примеры проверены на Python 3.14.7, сентябрь 2026).
Содержание
Мини-словарь: делитель, кратное, НОД, НОК
| Термин | Что это | Пример для 12 и 18 |
|---|---|---|
| Делитель | d, на которое число делится без остатка | делители 12: 1, 2, 3, 4, 6, 12 |
| Кратное | число вида d * k | кратные 12: 12, 24, 36, 48, … |
| Общий делитель | делит оба числа | 1, 2, 3, 6 |
| НОД | наибольший общий делитель | НОД(12, 18) = 6 |
| Общее кратное | делится на оба числа | 36, 72, 108, … |
| НОК | наименьшее положительное общее кратное | НОК(12, 18) = 36 |
Граничные случаи, на которых путаются:
- у ненулевого числа конечное число делителей, но бесконечно много кратных; у нуля наоборот: его делит любое ненулевое число;
- 1 и само число всегда входят в делители (для n >= 1);
- 0 кратен любому числу (0 = d * 0), но делителем не бывает: на ноль не делят;
- в школе делители считают среди натуральных чисел; в теории чисел у 12 есть и делители -1, -2, …, -12. Дальше, если не сказано иное, речь о натуральных.
Частая ошибка в школьных пересказах — «у 21 два делителя: 3 и 7». На самом деле делителей четыре: 1, 3, 7, 21. Числа 3 и 7 — это нетривиальные делители (все, кроме 1 и самого числа). Собственными обычно называют все делители, кроме самого числа: у 21 это 1, 3 и 7.
Как найти все делители числа
Делители идут парами: если d делит n, то делит и n / d. Для 36 это пары 1-36, 2-18, 3-12, 4-9 и 6-6. Меньший элемент пары не больше корня из n, поэтому перебирать все числа до n не нужно — хватит проверить d от 1 до √n.
Прогоним руками для 36 (√36 = 6):
d = 1 -> 36 % 1 = 0 -> пара (1, 36)
d = 2 -> 36 % 2 = 0 -> пара (2, 18)
d = 3 -> 36 % 3 = 0 -> пара (3, 12)
d = 4 -> 36 % 4 = 0 -> пара (4, 9)
d = 5 -> 36 % 5 = 1 -> не делитель
d = 6 -> 36 % 6 = 0 -> пара (6, 6), это один делитель
Тот же алгоритм в коде:
import math
def divisors(n: int) -> list[int]:
"""Все натуральные делители n >= 1 за O(sqrt(n)) проверок."""
if n < 1:
raise ValueError("нужно натуральное n >= 1")
small, large = [], []
for d in range(1, math.isqrt(n) + 1):
if n % d == 0:
small.append(d)
if d != n // d: # у квадрата пара d*d - один делитель
large.append(n // d)
return small + large[::-1]
print(divisors(36))
print(divisors(21))
print(divisors(97))
print(len(divisors(720720)))
Вывод:
[1, 2, 3, 4, 6, 9, 12, 18, 36]
[1, 3, 7, 21]
[1, 97]
240
math.isqrt дает целый корень без погрешности float. Для 720 720 это 848 проверок вместо 720 720.
Ошибка: двойной счет у точного квадрата. Если добавлять оба элемента пары без проверки, делитель 6 у числа 36 попадет в список дважды:
import math
def divisors_bad(n):
res = []
for d in range(1, math.isqrt(n) + 1):
if n % d == 0:
res += [d, n // d]
return sorted(res)
print(divisors_bad(36), len(divisors_bad(36)))
[1, 2, 3, 4, 6, 6, 9, 12, 18, 36] 10
Исправление — условие if d != n // d, как в рабочей версии. Ошибка видна только на точных квадратах: тест на 12 или 21 ее не поймает.
Простые и составные числа через делители
Простое число — натуральное число больше 1, у которого ровно два натуральных делителя: 1 и оно само (divisors(97) выше). Составное — натуральное число больше 1, у которого делителей больше двух. Единица не относится ни к тем, ни к другим: у нее один делитель.
Проверка на простоту перебором стоит O(√n), а не O(n): у делителя больше √n пара меньше √n и найдется раньше. Для чисел в сотни цифр применяют вероятностные тесты (например, Миллера — Рабина).
Если известно разложение n на простые множители n = p1^a1 * p2^a2 * …, то число делителей равно (a1 + 1)(a2 + 1)… Для 720 720 = 2^4 * 3^2 * 5 * 7 * 11 * 13 это 5 * 3 * 2 * 2 * 2 * 2 = 240, что совпадает с выводом кода. Само разложение на множители — отдельная тема.
Признаки делимости
Признаки делимости позволяют проверить делимость в уме по цифрам числа. В коде достаточно n % d == 0.
| Делитель | Признак | Пример |
|---|---|---|
| 2 | последняя цифра четная (0, 2, 4, 6, 8) | 1 358 |
| 3 | сумма цифр делится на 3 | 471: 4+7+1 = 12 |
| 4 | число из двух последних цифр делится на 4 | 1 316: 16 |
| 5 | последняя цифра 0 или 5 | 2 045 |
| 6 | делится и на 2, и на 3 | 474 |
| 8 | число из трех последних цифр делится на 8 | 5 120: 120 |
| 9 | сумма цифр делится на 9 | 4 752: 4+7+5+2 = 18 |
| 10 | последняя цифра 0 | 930 |
| 11 | знакопеременная сумма цифр делится на 11 | 4 752: 2-5+7-4 = 0 |
| 25 | две последние цифры 00, 25, 50 или 75 | 3 175 |
Почему они работают: 10 при делении на 9 (и на 3) дает остаток 1, поэтому любое число имеет тот же остаток, что и сумма его цифр. А 10 при делении на 11 дает остаток -1, отсюда знакопеременная сумма. Признак для 6 — частный случай общего правила: если число делится на два взаимно простых числа (НОД = 1), оно делится и на их произведение. Для 4 и 6 это не так: 12 делится на оба, но не на 24.
Проверим признаки кодом и сравним с прямой проверкой остатка на первом миллионе чисел:
def digit_sum(n):
return sum(int(c) for c in str(n))
def alt_sum(n):
# знакопеременная сумма цифр справа налево: + - + - ...
return sum(int(c) * (-1) ** i for i, c in enumerate(reversed(str(n))))
for n in (4752, 4753):
print(n, "сумма цифр", digit_sum(n), "-> на 3:", n % 3 == 0, "на 9:", n % 9 == 0)
print(n, "знакоперем. сумма", alt_sum(n), "-> на 11:", n % 11 == 0)
# проверим признаки на первом миллионе чисел
ok = all((digit_sum(k) % 3 == 0) == (k % 3 == 0) and
(digit_sum(k) % 9 == 0) == (k % 9 == 0) and
(alt_sum(k) % 11 == 0) == (k % 11 == 0)
for k in range(1, 1_000_001))
print(ok)
4752 сумма цифр 18 -> на 3: True на 9: True
4752 знакоперем. сумма 0 -> на 11: True
4753 сумма цифр 19 -> на 3: False на 9: False
4753 знакоперем. сумма 1 -> на 11: False
True
Прогон на миллионе чисел — проверка, а не доказательство; доказательство — рассуждение про остатки выше.
НОД и НОК
НОД (наибольший общий делитель) двух чисел — самое большое число, которое делит оба. НОК (наименьшее общее кратное) — самое маленькое положительное число, которое делится на оба. Для натуральных a и b они связаны формулой НОД(a, b) * НОК(a, b) = a * b. Для 84 и 36: НОД = 12, НОК = 252, и 12 * 252 = 3024 = 84 * 36.
Где что нужно:
| Задача | Что считать | Пример |
|---|---|---|
| сократить дробь | НОД числителя и знаменателя | 84/36 -> 7/3 |
| привести дроби к общему знаменателю | НОК знаменателей | 5/12 + 7/18 -> знаменатель 36 |
| когда совпадут два периодических события | НОК периодов | задачи раз в 12 и 18 минут совпадут через 36 минут |
| нарезать без остатка на равные части | НОД размеров | лист 84 x 36 -> квадраты 12 x 12 |
Если НОД(a, b) = 1, числа называют взаимно простыми: 8 и 15 взаимно простые, хотя ни одно из них не простое.
Алгоритм Евклида: как быстро найти НОД
Алгоритм Евклида опирается на свойство НОД(a, b) = НОД(b, a mod b) и НОД(a, 0) = a. Делим с остатком, пока остаток не станет нулем; последний ненулевой остаток — НОД.
Для 84 и 36:
84 = 2*36 + 12
36 = 3*12 + 0
Остаток стал нулем, последний ненулевой — 12, значит НОД(84, 36) = 12. Реализация на Python:
import math
def gcd(a: int, b: int) -> int:
"""НОД по алгоритму Евклида (с делением с остатком)."""
a, b = abs(a), abs(b)
while b:
a, b = b, a % b
return a
def lcm(a: int, b: int) -> int:
if a == 0 or b == 0:
return 0
return abs(a // gcd(a, b) * b)
print(gcd(84, 36), lcm(84, 36))
print(gcd(-12, 18), gcd(0, 5), gcd(0, 0))
print(math.gcd(84, 36), math.lcm(84, 36), math.gcd(12, 18, 30), math.lcm(4, 6, 10))
12 252
6 5 0
12 252 6 60
abs делает НОД положительным для отрицательных чисел. gcd(0, 0) = 0 — соглашение, принятое и в стандартной библиотеке. В рабочем коде берите math.gcd и math.lcm: с Python 3.9 они принимают любое число аргументов.
Алгоритм быстрый: число шагов растет как логарифм от меньшего числа. Худший случай — соседние числа Фибоначчи, где все частные, кроме последнего, равны 1. Для 89 и 55 нужно 9 делений с остатком, а для 1 000 000 и 999 999 — всего 2.
Ошибка: НОК через / на больших числах. Формула a * b / НОД верна математически, но оператор / в Python возвращает float, у которого 53 бита мантиссы:
import math
a, b = 2**61 - 1, 2**59 - 1 # два больших взаимно простых числа
bad = int(a * b / math.gcd(a, b)) # "/" дает float
good = a // math.gcd(a, b) * b
print(bad == good, good - bad)
print(math.lcm(a, b) == good)
False -2882303761517117439
True
Исправление — целочисленное деление //, причем до умножения: a // gcd(a, b) * b. В Python это только экономит память, а в C, C++ и Java с типами фиксированной ширины деление первым еще и уменьшает риск переполнения при умножении.
Остаток от отрицательных чисел: зависит от языка
Проверка n % d == 0 работает во всех популярных языках, а сравнение остатка с ненулевым значением — нет. В Python знак остатка совпадает со знаком делителя, в JavaScript, Java, C и C++ — со знаком делимого:
const isOddBad = n => n % 2 === 1;
const isOdd = n => n % 2 !== 0;
console.log(-7 % 3, -3 % 2);
console.log(isOddBad(-3), isOdd(-3));
-1 -1
false true
В Node.js 24 -3 % 2 дает -1, поэтому isOddBad(-3) ошибочно возвращает false. В Python то же выражение -7 % 3 дает 2, а -3 % 2 — 1. Исправление — проверять остаток на неравенство нулю (n % 2 !== 0), а не на равенство 1. То же касается индекса в кольцевом буфере при отрицательном сдвиге.
Выводы
- Делитель и кратное — одна связь с двух сторон: d делит n тогда и только тогда, когда n кратно d. У ненулевого числа конечное число делителей и бесконечно много кратных; 0 кратен любому числу, но делителем не бывает.
- Все делители n находятся перебором до √n парами (d, n / d); на точных квадратах не забывайте про двойной счет.
- Признаки делимости нужны для счета в уме и объясняются остатками 10 при делении на 3, 9 и 11; в коде достаточно
n % d == 0. - НОД сокращает дроби и режет на равные части, НОК дает общий знаменатель и период совпадения; НОД * НОК = a * b для натуральных чисел.
- Алгоритм Евклида находит НОД за логарифмическое число шагов; в Python берите
math.gcdиmath.lcm, а НОК вручную считайте через//, не через/.
Где применяется / связь с практикой
Делители, НОД и НОК встречаются в коде чаще, чем кажется: сокращение дробей в fractions.Fraction, расчет периодов у расписаний и анимаций, шаг обхода массива, который должен быть взаимно простым с его длиной, чтобы посетить все ячейки. В криптографии расширенный алгоритм Евклида находит обратный элемент по модулю — на этом строится вычисление ключа в RSA.
Освойте тему на практике
Если хочется системно пройти дискретную математику, линейную алгебру и теорию вероятностей с упором на применение в коде, посмотрите курс «Базовая математика для цифровых профессий». Попробовать формат и разобрать отдельные темы можно на бесплатных открытых уроках Otus.
FAQ
Как найти НОД трех и более чисел?
Последовательно: НОД(a, b, c) = НОД(НОД(a, b), c). Так же считается НОК. В Python math.gcd(12, 18, 30) сразу вернет 6.
Чем делитель отличается от множителя?
Множитель — роль числа в произведении: в 12 = 3 * 4 оба числа множители. Любой натуральный множитель в таком разложении — делитель результата, но слово «делитель» описывает отношение между числами, а не конкретную запись.
Что такое расширенный алгоритм Евклида?
Он находит не только НОД(a, b), но и целые x и y, для которых a * x + b * y = НОД(a, b). Когда числа взаимно простые, x дает обратный к a элемент по модулю b; с Python 3.8 то же самое вычисляет pow(a, -1, b).



