Skip to content

Some minor corrections and improvements #332

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions content/russian/cs/factorization/eratosthenes.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ published: true

**Решето Эратосфена** (англ. *sieve of Eratosthenes*) — алгоритм нахождения всех простых чисел от $1$ до $n$.

Основная идея соответствует названию алгоритма: запишем ряд чисел $1, 2,\ldots, n$, а затем будем вычеркивать
Основная идея соответствует названию алгоритма: запишем ряд чисел $1, 2,\ldots, n$, а затем будем вычёркивать:

- сначала числа, делящиеся на $2$, кроме самого числа $2$,
- потом числа, делящиеся на $3$, кроме самого числа $3$,
Expand All @@ -24,7 +24,8 @@ published: true
```c++
vector<bool> sieve(int n) {
vector<bool> is_prime(n + 1, true);
for (int i = 2; i <= n; i++)
is_prime[0] = false, is_prime[1] = false;
for (int i = 2; i <= n; ++i)
if (is_prime[i])
for (int j = 2 * i; j <= n; j += i)
is_prime[j] = false;
Expand All @@ -44,14 +45,14 @@ $$
\sum_k \frac{n}{k} = \frac{n}{2} + \frac{n}{3} + \frac{n}{4} + \ldots + \frac{n}{n} = O(n \log n)
$$

Здесь мы воспользовались асимптотикой [гармонического ряда](https://ru.wikipedia.org/wiki/%D0%93%D0%B0%D1%80%D0%BC%D0%BE%D0%BD%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D1%80%D1%8F%D0%B4).
Здесь мы воспользовались асимптотикой [гармонического ряда](https://ru.wikipedia.org/wiki/Гармонический_ряд).

У исходного алгоритма асимптотика должна быть ещё лучше. Чтобы найти её точнее, нам понадобятся два факта про простые числа:

1. Простых чисел от $1$ до $n$ примерно $\frac{n}{\ln n}$ .
2. Простые числа распределены без больших «разрывов» и «скоплений», то есть $k$-тое простое число примерно равно $k \ln k$.
1. простых чисел от $1$ до $n$ примерно $\frac{n}{\ln n}$;
2. простые числа распределены без больших «разрывов» и «скоплений», то есть $k$-ое простое число примерно равно $k \ln k$.

Мы можем упрощённо считать, что число $k$ является простым с «вероятностью» $\frac{1}{\ln n}$. Тогда, время работы алгоритма можно более точнее оценить как
Можно упрощённо считать, что число $n$ является простым с «вероятностью» $\frac{1}{\ln n}$. Тогда время работы алгоритма можно более точно оценить как

$$
\sum_k \frac{1}{\ln k} \frac{n}{k}
Expand All @@ -66,13 +67,13 @@ $$

Основная проблема решета Эратосфена состоит в том, что некоторые числа мы будем помечать как составные несколько раз — столько, сколько у них различных простых делителей. Чтобы достичь линейного времени работы, нам нужно придумать способ, как рассматривать все составные числа ровно один раз.

Обозначим за $d(k)$ минимальный простой делитель числа $k$ и заметим следующий факт: у составного числа $k$ есть единственное представление $k = d(k) \cdot r$, и при этом у числа $r$ нет простых делителей меньше $d(k)$.
Обозначим за $d(k)$ минимальный простой делитель числа $k$ и заметим следующий факт: любое составное число $k$ можно представить в виде $k = d(k) \cdot r$, причём у числа $r$ все простые нет делители не меньше $d(k)$.

Идея оптимизации состоит в том, чтобы перебирать этот $r$, и для каждого перебирать только нужные множители — а именно, все от $2$ до $d(r)$ включительно.
Идея оптимизации состоит в том, чтобы перебирать это $r$, и для каждого перебирать только нужные множители — а именно, все от $2$ до $d(r)$ включительно.

### Алгоритм

Немного обобщим задачутеперь мы хотим посчитать для каждого числа $k$ на отрезке $[2, n]$ его минимальный простой делитель $d_k$, а не только определить его простоту.
Немного обобщим задачу: теперь мы хотим для каждого числа $k$ на отрезке $[2, n]$ не только определить, простое ли оно, но и найти его наименьший простой делитель $d(k)$.

Изначально массив $d$ заполним нулями, что означает, что все числа предполагаются простыми. В ходе работы алгоритма этот массив будет постепенно заполняться. Помимо этого, будем поддерживать список $p$ всех найденных на текущий момент простых чисел.

Expand Down