Задачи на числа: практикум по Python

Раздел: 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 True

Python задачи на числа (задачи на числа в 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 True

Python простые числа (простые числа в 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]

Задачи на числа в Python - comments

En
Python задачи на числа (python)