Дан фрагмент Python для поиска простых чисел до n: def primes(n): res=[]; for i in range(2,n+1): isprime=True; for j in range(2,i): if i%j==0: isprime=False; break; if isprime: res.append(i); return res; Предложите оптимизированную реализацию, объясните её теоретическую сложность и почему выбранный подход быстрее в практике (например, решето Эратосфена, проверка до sqrt(n), фракционирование по нечётности)?

10 Окт в 04:18
5 +5
0
Ответы
1
Ниже — кратко про улучшения, их сложность и код.
1) Простой оптимизированный вариант: проверять делители только до i\sqrt{i}i и пропускать чётные числа.
- Сложность: суммарно O(nn)\displaystyle O\bigl(n\sqrt{n}\bigr)O(nn ) (то же, что O(n3/2)O(n^{3/2})O(n3/2)), по сравнению с исходным O(n2)\displaystyle O(n^2)O(n2).
- Почему быстрее: меньше проверок на делимость (до i\sqrt{i}i вместо до i−1i-1i−1), и проверяем примерно вдвое меньше кандидатов, пропуская чётные.
Код:
def primes_trial(n):
if n < 2:
return []
res = [2]
for i in range(3, n+1, 2):
isprime = True
r = int(i**0.5)
for j in range(3, r+1, 2):
if i % j == 0:
isprime = False
break
if isprime:
res.append(i)
return res
2) Сито Эратосфена (рекомендую для большинства задач до больших n).
- Сложность по времени: O(nlog⁡log⁡n)\displaystyle O\bigl(n\log\log n\bigr)O(nloglogn).
- Память: O(n)\displaystyle O(n)O(n).
- Почему быстрее на практике: алгоритм помечает кратные простых массовыми операциями (меньше операций деления), хорошая локальность данных и возможность оптимизаций (байтовый массив, срезы), что в Python дает большой выигрыш.
Код (байтовое сито, эффективнее чем списковые проверки):
def primes_sieve(n):
if n < 2:
return []
sieve = bytearray(b'\x01') * (n + 1)
sieve[0:2] = b'\x00\x00'
r = int(n**0.5)
for p in range(2, r+1):
if sieve[p]:
start = p*p
sieve[start:n+1:p] = b'\x00' * ((n - start)//p + 1)
return [i for i, isprime in enumerate(sieve) if isprime]
3) Для очень больших nnn: сегментированное сито — делает те же пометки по блокам, использует память O(n)O(\sqrt{n})O(n ) и сохраняет время примерно O(nlog⁡log⁡n)\displaystyle O(n\log\log n)O(nloglogn).
Краткое резюме:
- Если nnn невелико (например, до нескольких сотен тысяч), trial-division с проверкой до i\sqrt{i}i + пропуск чётных простых может быть простым и достаточным.
- Для больших nnn лучше использовать сито Эратосфена (или сегментированное сито) — асимптотически и практически быстрее.
10 Окт в 04:50
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир