Задачи на числа: практикум по Python
Основные алгоритмические задачи на числа
Как проверить, является ли число простым, используя самый быстрый алгоритм для целых чисел до 109?
Наиболее эффективный способ - проверка делителей до квадратного корня из числа с шагом 2 и дополнительной проверкой на делимость на 2 и 3. Метод основан на том, что все простые числа больше 3 имеют вид 6k±1.
def is_prime(n):
if n < 2:
return False
if n % 2 == 0:
return n == 2
if n % 3 == 0:
return n == 3
i = 5
while i * i <= n:
if n % i == 0 or n % (i + 2) == 0:
return False
i += 6
return TruePython задачи на числа (задачи на числа в python)
Пояснение:
- Числа меньше 2 не являются простыми.
- Отдельные проверки на 2 и 3 позволяют далее использовать шаг 6.
- Цикл идёт только до sqrt(n), что даёт сложность O(√n).
- Проверяются только делители вида 6k-1 и 6k+1, что в два раза быстрее обычного перебора.
Какой самый простой, но медленный способ проверки на простоту?
Самый очевидный метод - перебор всех чисел от 2 до n-1.
def is_prime_slow(n):
if n < 2:
return False
for i in range(2, n):
if n % i == 0:
return False
return TruePython простые числа (простые числа в python)
Этот вариант подходит только для обучения или проверки очень маленьких чисел (n < 106). Сложность O(n).
Типичные ошибки и проблемы:
- Забывание проверки чисел 0, 1 и отрицательных - они не являются простыми.
- Неправильная обработка двойки (единственное чётное простое).
- Переполнение при i*i <= n для очень больших чисел (в Python это не проблема из-за длинных целых, но может быть медленно).
- В медленном варианте при n = 10^7 цикл займёт неприемлемо много времени.
Решение: использовать оптимизированный метод rbase или решето Эратосфена для диапазона чисел.
Как определить, является ли число числом Армстронга (самовлюблённым числом)?
Число Армстронга - число, равное сумме своих цифр, возведённых в степень, равную количеству цифр. Самый простой способ - преобразовать число в строку.
def is_armstrong(n):
s = str(n)
p = len(s)
return n == sum(int(d) ** p for d in s)
Python задания на алгоритмы (задания на алгоритмы в python)
Пояснение:
- Получаем строковое представление числа и определяем длину p.
- Генератор списка возводит каждый символ-цифру в степень p.
- Сравниваем сумму с исходным числом.
Как реализовать проверку без преобразования в строку, используя арифметические операции?
Метод с выделением цифр через деление на 10.
def is_armstrong_math(n):
if n < 0:
return False
temp = n
p = len(str(n))
total = 0
while temp:
total += (temp % 10) ** p
temp //= 10
return total == n
Этот способ полезен, когда запрещены строковые операции, или для встраивания в арифметические цепочки.
Типичные ошибки и проблемы:
- Отрицательные числа - не являются числами Армстронга (в классическом определении).
- Однозначные числа (0-9) всегда являются числами Армстронга, это нужно учитывать.
- При больших p (например, 10-значные числа) возведение в степень даёт огромные значения, что может замедлить работу, но в Python это допустимо.
Решение: предварительно проверять диапазон, использовать встроенные функции.
Как вычислить факториал числа итеративно?
Итеративный метод - самый эффективный и безопасный.
def factorial_iter(n):
result = 1
for i in range(2, n + 1):
result *= i
return result
Пояснение:
- Для n = 0 и n = 1 результат равен 1.
- Цикл начинается с 2, чтобы не делать лишних умножений на 1.
- Сложность O(n), память O(1).
Как вычислить факториал рекурсивно?
Рекурсивное определение факториала: n! = n * (n-1)!.
def factorial_rec(n):
return 1 if n <= 1 else n * factorial_rec(n - 1)
Этот вариант нагляден, но имеет ограничение по глубине рекурсии (обычно 1000).
Типичные ошибки и проблемы:
- Отрицательные числа - факториал не определён.
- Слишком большое n приводит к переполнению стека при рекурсии (RecursionError).
- Неявное переполнение целого типа в Python не происходит, но для огромных n (например, 100000) вычисление может занять много времени.
Решение: использовать итеративный метод или встроенную функцию math.factorial.
Как найти наибольший общий делитель двух чисел с помощью алгоритма Евклида?
Алгоритм Евклида с помощью деления - самый быстрый и распространённый.
def gcd(a, b):
while b:
a, b = b, a % b
return abs(a)
Пояснение:
- Пока b не станет нулём, заменяем пару (a, b) на (b, a % b).
- Результат берётся по модулю для корректной работы с отрицательными числами.
- Сложность O(log min(a,b)).
Как вычислить НОД через вычитание?
Алгоритм, основанный на последовательном вычитании меньше.
def gcd_sub(a, b):
a, b = abs(a), abs(b)
while a != b:
if a > b:
a -= b
else:
b -= a
return a
Этот вариант работает медленнее для больших чисел, но может быть понятнее для новичков.
Типичные ошибки и проблемы:
- Забывание взять модуль для отрицательных чисел - результат станет отрицательным или бесконечным.
- При алгоритме вычитания для больших чисел (например, 10^9 и 1) потребуется много итераций.
- Необработка случая, когда оба числа равны нулю - gcd(0,0) не определён.
Решение: использовать алгоритм деления или встроенную функцию math.gcd.
Расширенные примеры и редкие подходы
Ниже представлены более сложные реализации и неочевидные решения для тех же задач.
1. Проверка простоты: вероятностный тест Миллера - Рабина
Для больших чисел (сотни цифр) детерминированный перебор неприменим. Тест Миллера - Рабина даёт высокую вероятность правильного ответа.
import random
def miller_rabin(n, k=5):
if n < 2:
return False
if n in (2, 3):
return True
if n % 2 == 0:
return False
r, d = 0, n - 1
while d % 2 == 0:
r += 1
d //= 2
for _ in range(k):
a = random.randint(2, n - 2)
x = pow(a, d, n)
if x == 1 or x == n - 1:
continue
for _ in range(r - 1):
x = pow(x, 2, n)
if x == n - 1:
break
else:
return False
return True
# Пример использования
print(miller_rabin(1000000007)) # True (простое)
print(miller_rabin(1000000009)) # True
print(miller_rabin(1000000011)) # False
True True False
2. Числа Армстронга произвольной длины с использованием pow для больших степеней
Для чисел с большим количеством цифр можно использовать функцию pow с модулем, чтобы избежать гигантских чисел, если нужно только сравнение.
def is_armstrong_large(n):
s = str(n)
p = len(s)
# Если p > 60, то даже pow без модуля приведёт к огромному значению, но Python справляется.
# Для ускорения можно использовать pow(d, p, 10**p) - но это меняет логику.
return n == sum(pow(int(d), p) for d in s)
# Пример: 10-значное число Армстронга (их всего 4)
print(is_armstrong_large(4679307774)) # True
True
3. Факториал с мемоизацией (кэширование)
Если требуется многократно вычислять факториалы для разных n, эффективно использовать декоратор lru_cache.
from functools import lru_cache
@lru_cache(maxsize=None)
def factorial_memo(n):
if n < 0:
raise ValueError("Factorial not defined for negative numbers")
return 1 if n <= 1 else n * factorial_memo(n - 1)
# Примеры
print(factorial_memo(10))
print(factorial_memo(20))
print(factorial_memo.cache_info())
3628800 2432902008176640000 CacheInfo(hits=1, misses=21, maxsize=None, currsize=21)
4. НОД: бинарный алгоритм (алгоритм Стейна)
Этот алгоритм использует только сдвиги и вычитания, что может быть быстрее на очень больших числах.
def gcd_binary(a, b):
a, b = abs(a), abs(b)
if a == 0:
return b
if b == 0:
return a
shift = 0
while ((a | b) & 1) == 0:
a >>= 1
b >>= 1
shift += 1
while (a & 1) == 0:
a >>= 1
while b != 0:
while (b & 1) == 0:
b >>= 1
if a > b:
a, b = b, a
b -= a
return a << shift
# Пример
print(gcd_binary(123456789, 987654321)) # 9
print(gcd_binary(-121, 11)) # 11
9 11
5. НОК через НОД
Стандартная формула: lcm(a,b) = abs(a*b) // gcd(a,b).
def lcm(a, b):
return abs(a * b) // gcd(a, b) # gcd из предыдущего примера
print(lcm(12, 18)) # 36
print(lcm(7, 13)) # 91
36 91
6. Решето Эратосфена для генерации списка простых чисел до N
def sieve_of_eratosthenes(limit):
is_prime = [True] * (limit + 1)
is_prime[0] = is_prime[1] = False
for i in range(2, int(limit ** 0.5) + 1):
if is_prime[i]:
for j in range(i * i, limit + 1, i):
is_prime[j] = False
return [num for num, is_p in enumerate(is_prime) if is_p]
primes = sieve_of_eratosthenes(50)
print(primes)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]