<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Тимофей Ижицкий</title><generator>teletype.in</generator><description><![CDATA[Тимофей Ижицкий]]></description><image><url>https://img4.teletype.in/files/7c/ce/7cce39ee-3153-4156-b682-e39f1580c57f.png</url><title>Тимофей Ижицкий</title><link>https://teletype.in/@timofeyizhitskiy</link></image><link>https://teletype.in/@timofeyizhitskiy?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=timofeyizhitskiy</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/timofeyizhitskiy?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/timofeyizhitskiy?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Mon, 08 Jun 2026 04:15:36 GMT</pubDate><lastBuildDate>Mon, 08 Jun 2026 04:15:36 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@timofeyizhitskiy/2XsHxDRVYrn</guid><link>https://teletype.in/@timofeyizhitskiy/2XsHxDRVYrn?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=timofeyizhitskiy</link><comments>https://teletype.in/@timofeyizhitskiy/2XsHxDRVYrn?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=timofeyizhitskiy#comments</comments><dc:creator>timofeyizhitskiy</dc:creator><title>Решето Эратосфена </title><pubDate>Sat, 18 Nov 2023 13:07:55 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/bf/92/bf92227c-3266-45ec-bcb8-0d60f5f292c5.png"></media:content><description><![CDATA[<img src="https://img2.teletype.in/files/90/e1/90e1acf6-7760-4223-a8ce-2a54dd8c1d5f.jpeg"></img>Решето Эратосфена]]></description><content:encoded><![CDATA[
  <p id="O25G">Статья написана для <a href="https://t.me/sbornik_olprog" target="_blank">Сборника Олпрогера</a>.</p>
  <p id="T6Wm">Достаточно часто в задачах бывает полезно - уметь факторизировать числа. В большинстве таких задач как раз используется решето Эратосфена.</p>
  <hr />
  <p id="knMa">Вспомним несколько определений.</p>
  <p id="h6PT"><em>Простое число</em> - число, которое имеет ровно два различных натуральных делителя — единицу и самого себя. </p>
  <p id="bkiW">Если число <strong>не</strong> простое, то оно <em>составное</em>. </p>
  <p id="cRwy"><strong>Единица</strong> не является ни простым, ни составным числом.</p>
  <p id="TT6M">Давайте сначала решим задачу капельку полегче, чем исходная: для каждого числа от <code>1 до n</code> поймем является ли оно простым или нет.</p>
  <p id="mbwm">Список простых чисел: <code>2, 3, 5, 7, 11, 13, 17, ...</code></p>
  <p id="PuZ2">Заметим, что если число <code>x</code> делится на какое-либо из чисел <code>от 2 до x - 1</code>, то оно составное, иначе простое. Сделаем какие-то замечания:</p>
  <ul id="oOm1">
    <li id="twGL">Все числа, которые делятся на <code>2</code>, кроме самой <code>двойки</code> точно не простые: <code>4, 6, 8, 10, 12, ...</code></li>
    <li id="zdpA">Все числа, которые делятся на <code>3</code>, кроме самой <code>тройки</code> точно не простые: <code>6, 9, 12, 15, 18, ...</code></li>
    <li id="QKnb">Все числа, которые делятся на <code>4</code>, кроме самой <code>четверки</code> точно не простые: <code>8, 12, 16, 20, ...</code></li>
    <li id="TEgz">Все числа, которые делятся на <code>5</code>, кроме самой <code>пятерки</code> точно не простые: <code>10, 15, 20, 25, ...</code></li>
    <li id="20rO">И так далее</li>
  </ul>
  <p id="NXxF">Из этого замечания вырисовывается интересный алгоритм: давайте перебирать число <code>x от 2 до n</code>, и помечать, что числа <code>2x, 3x, 4x, ...</code> - составные. Если мы ни разу не пометили число, то оно является простым. Это и есть решето Эратосфена.</p>
  <pre id="5pd7" data-lang="cpp">// isPrime[i] - хранит bool в зависимости от того, что i простое или нет
// Изначального IsPrime - хранит n значений true
isPrime[1] = false; // 1 - не простое число
for (int x = 2; x &lt;= n; x++) {
    // перебираем x
    for (int y = 2 * x; y &lt;= n; y += x) {
        // перебираем числа вида 2x, 3x, 4x
        isPrime[y] = false;
    }
}
// после этого массив isPrime хранит корректные значения</pre>
  <p id="A7cZ">Сперва может показаться, что этот алгоритм работает долго, но это не так. Оценка временной сложности алгоритма:</p>
  <figure id="1762" class="m_column">
    <img src="https://img3.teletype.in/files/68/7b/687bd145-0d51-4875-b1a2-dbe247ae63c2.png" width="1372" />
  </figure>
  <p id="yWXZ">Это так, потому что сумма <a href="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" target="_blank">гармонического ряда</a> сходится к O(log n).</p>
  <hr />
  <p id="Eanz">Давайте сделаем небольшую оптимазацию нашего алгоритма, а именно достаточно перебирать только <strong>простые</strong> <code>x</code>, и для них помечать <code>2x, 3x, 4x, ...</code> </p>
  <pre id="X5Ss" data-lang="cpp">// isPrime[i] - хранит bool в зависимости от того, что i простое или нет
// Изначального IsPrime - хранит n значений true
isPrime[1] = false; // 1 - не простое число
for (int x = 2; x &lt;= n; x++) {
    // перебираем x
    if (isPrime[x]) {
        // берем только простые x
        for (int y = 2 * x; y &lt;= n; y += x) {
            // перебираем числа вида 2x, 3x, 4x
            isPrime[y] = false;
        }
    }
}
// после этого массив isPrime хранит корректные значения</pre>
  <p id="gpZb">Используя математику можно доказать, что это работает за <code>O(n log (logn))</code>.  При желании с доказательсвом можно ознакомиться в статьях, отмеченных внизу.</p>
  <hr />
  <p id="2cEi">Вернемся к исходной задаче. Мне нужно <a href="https://ru.wikipedia.org/wiki/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F_%D1%82%D0%B5%D0%BE%D1%80%D0%B5%D0%BC%D0%B0_%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D0%BA%D0%B8" target="_blank">факторизовать</a> все числа от <code>1 до n</code>.</p>
  <p id="29cm">Пусть я для каждого числа x знаю его минимальный простой делитель. То есть для <code>4 - это 2</code>, <code>21 - это 3</code>, <code>17 - это 17</code>. Единицу проигнорируем. Обозначим минимальный простой делитель <code>x - minPrimeDivisor[x]</code>.</p>
  <p id="BcuF">Тогда, чтобы разложить на простые множители число <code>x</code>, сделаем следующее: добавим в список его простых множителей <code>minPrimeDivisor[x]</code>, и разложим число<code> x / minPrimeDivisor[x]</code>.  Будем повторять этот процесс, пока не получим единицу. Достаточно понятно, что алгоритм корректен. Код:</p>
  <pre id="i9aK" data-lang="cpp">for (int x = 2; x &lt;= n; x++) {
    // перебираем число x, которое хотим разложить
    vector&lt;int&gt; factors; // список простых множителей
    int current = x; // текущее значение x
    while (current != 1) {
         factors.push_back(minPrimeDivisor[current]);
         current /= minPrimeDivisor[current];
    }
    // factor - корректный список множителей
}</pre>
  <p id="dCyt">Тогда, чтобы решить исходную задачу, нужно научиться строить массив <code>minPrimeDivisor</code>.</p>
  <p id="iR1i">Заметим, что этот массив можно поддерживать с помощью решета Эратосфена. Действительно, давайте изначально заполним массив <code>minPrimeDivisor[i] = i</code>. Теперь, когда я перебираю простое <code>x</code> и перебираю<code> y = 2x, 3x, 4x, ...</code>, прорелаксируем <code>minPrimeDivisor[y]</code> с <code>x</code>.  Достаточно понятно, что для числа я переберу все его простые делители, а значит минимум будет посчитан корректно. Код:</p>
  <pre id="PPRO" data-lang="cpp">// isPrime[i] - хранит bool в зависимости от того, что i простое или нет
// Изначального IsPrime - хранит n значений true
// minPrimeDivisor[i] - хранит минимальный простой делитель i
// Изначально minPrimeDivisor[i] = i
isPrime[1] = false; // 1 - не простое число
for (int x = 2; x &lt;= n; x++) {
    // перебираем x
    if (isPrime[x]) {
        // берем только простые x
        for (int y = 2 * x; y &lt;= n; y += x) {
            // перебираем числа вида 2x, 3x, 4x
            isPrime[y] = false;
            // релаксируем minPrimeDivisor[y] с x
            minPrimeDivisor[y] = min(minPrimeDivisor[y], x);
        }
    }
}
// после этого массивы isPrime, minPrimeDivisor хранят корректные значения</pre>
  <p id="LZaW">Также можно сократить код:</p>
  <pre id="RKvP" data-lang="cpp">for (int x = 2; x &lt;= n; x++) {
    if (minPrimeDivisor[x] == x) {
        for (int y = 2 * x; y &lt;= n; y += x) {
            minPrimeDivisor[y] = min(minPrimeDivisor[y], x);
        }
    }
}</pre>
  <p id="JvAI">Итого мы научились решать задачу, которую хотели за <code>O(nlog(logn))</code>.</p>
  <hr />
  <h2 id="p7gJ">Другие статьи:</h2>
  <p id="kL80"><a href="https://e-maxx.ru/algo/eratosthenes_sieve" target="_blank">E-maxx</a> - описание и реализация</p>
  <p id="HGmF"><a href="https://ru.algorithmica.org/cs/factorization/eratosthenes/" target="_blank">Алгоритмика</a> - в статье также содержится оптимазация алгоритма O(n).</p>

]]></content:encoded></item></channel></rss>