Как найти количество простых чисел от 1 до n

от admin

алгебра — Определить кол-во простых чисел от 1 до n с математической точки зрения

Здравствуйте. Как определить кол-во простых чисел от 1 до n с математической точки зрения, например от 1 до 10^9.

задан 18 Янв ’14 0:26

Чтобы найти точно это количество, нужно всё «честно» подсчитать — «обходных» способов тут нет. Но для приблизительной оценки существует асимптотический закон распределения простых чисел. В отрезке от $%1$% до $%n$% таких чисел имеется порядка $%\frac<\ln n>$%. При $%x\to\infty$% отношение числа $%\pi(x)$% (так стандартно обозначается количество простых чисел, не превосходящих $%x$%) к величине $%x/\ln x$% стремится к единице.

Я не до конца понимаю постановку задачи. В частности, мне не ясна роль параметра $%k$%. Что он означает? Хотелось бы «чистого» описания на математическом языке, без «занимательного» сюжета и персонажей (мне трудно отсеивать ненужную информацию). Скорее всего, тут должно работать решето Эратосфена. Более сложных средств в такого рода задачах, как правило, не используется.

Число является простым если оно не делится от 2 до k+1,решето Эратосфена здесь не подходит

Falcao,отвечаю про k. В условии задачи сказано, что год не должен быть простым, а он должен казаться главному герою простым, то есть не иметь простых делителей меньших k+2.Решето Эратосфена в этой задаче здесь не подходит,а вот как в этой задаче можно использовать метод включений-исключений на отрезке с учетом k

@ivan145: роль числа $%k$% я теперь понял. Решето Эратосфена, если я правильно понимаю, не подходит по той причине, что в худшем случае оно требует прохождения массива длиной $%10^9$%, а это считается много. Если это в самом деле так, то напрашивается идея использования формулы включений и исключений. Реализовать её, судя по всему, можно. Я попробую тогда изложить свои соображения.

Отвечаю здесь, так как снизу всё исчерпано. Конечно, никаких произведений порядка $%10^<18>$% здесь в принципе возникнуть не может. Ведь рассматриваются только числа до $%10^9$%, и у них не бывает столь огромных делителей. Я уже говорил, что задача подсчёта для $%[A;B]$% сводится к нахождению разности $%f(B)-f(A-1)$%, то есть параметр в задаче фактически один: число $%n$% от $%1$% до $%10^9$%, для которого мы ищем $%f(n)$%.

Falcao,я все реализовал,не считал те произведения которые больше 2*10^9,на тестах где k маленькое все проходит довольно неплохо,но в худшем случае при k=300,считает 3 секунды.Не может ли у этой задачи быть другого решения?

2 ответа

Попробую изложить реализацию одного из алгоритмов. Задачу я понимаю так. Нам даны три числа: $%A$%, $%B$% и $%k$%. Требуется найти количество чисел отрезка $%[A;B]$%, которые не делятся ни на одно простое число из отрезка $%[2;k+1]$%. Поскольку $%k$% сравнительно небольшое, мы можем быстро выписать все такие числа, пользуясь решетом Эратосфена. В худшем случае возникает список 2, 3, 5, . 293, в который входят 62 простых числа. Теперь надо реализовать функцию $%f(n)$%, которая подсчитывает количество чисел отрезка $%[1;n]$%, не делящихся ни на одно из указанных. Это будет некая процедура-функция, и далее она дважды вызывается, и вычисляется разность $%f(B)-f(A-1)$%. Число $%n$% может иметь порядок $%10^9$%.

Для подсчёта $%f(n)$% нужно узнать количество чисел, делящихся хотя бы на одно из наших простых. Пусть эти простые числа суть $%p_1 < \cdots < p_m$%. Количество чисел отрезка $%[1;n]$%, делящихся на $%p_i$%, нам известно, и оно вычисляется по формуле $%[n/p_i]$%, где квадратные скобки обозначают целую часть. В соответствии с формулой включений и исключений, все эти числа складываются. Далее из них надо вычесть величины, соответствующие попарным пересечениям, то есть $%[n/(p_ip_j)]$% при $%i < j$%. Потом прибавляются числа для тройных пересечений, и так далее.

Проблема в том, что пересечений, вообще говоря, имеется очень много, и в общем случае это будет величина порядка $%2^<62>$% (каждое простое число можно брать или не брать, составляя произведение различных простых). Однако у нас тут имеется ограничение чисел по величине, поэтому брать те произведения простых, которые превышают $%10^9$%, нам уже не надо. Соответственно, задача сводится к нахождению эффективного принципа перебора всех произведений различных простых из списка $%p_1,\ldots p_m$%, с условием, что произведение не превышает $%10^9$%.

Сделаем ещё вот какое замечание. Если нам надо подсчитать число $%[n/(st)]$%, то можно это сделать по формуле $%[[n/s]/t]$%, то есть округлять можно после каждого деления. Тогда составляем список чисел $%[n/p_1],[n/p_2],\ldots,[n/p_m]$%. Все эти числа складываем. Про каждое число мы запоминаем, на какое $%p_i$% мы его последний раз делили. Далее $%[n/p_1]$% делим с округлением на числа $%p_2,\ldots,p_m$%; число $%[n/p_2]$% делим с округлением на более «поздние» числа $%p_3,\ldots,p_m$% и так далее; $%[n/p_]$% делим с округлением на $%p_m$%. Все полученные числа вычитаются. И далее повторяем эту процедуру, на каждом шаге меняя вычитание на сложение и наоборот, пока не произойдёт обнуление. Надо заметить, что количество действий тут не должно быть слишком большим: произведение первых 10 простых чисел уже превышает $%10^9$%, то есть делить слишком много раз будет не нужно. У самых больших простых чисел списка вообще берутся только три сомножителя. Судя по всему, этот план должен быть реализуем за предлагаемое время.

отвечен 18 Янв ’14 22:47

@falcao Думаю, что предложенное Вами решение действительно может уложится в ограничение времени. Но «Это будет некая процедура-функция, и далее она дважды вызывается, и вычисляется разность f(B)−f(A−1). Число n может иметь порядок $$10^9$$.» В случае если А и В будут достаточно большими числами, а их разность небольшой, например, А=999999995 и В=999999999, то вместо проверки 4 чисел будут необоснованно дважды вычисляться значения функции для достаточно больших чисел. Уверен, что несколько тестов будут именно такого плана.

Falcao,понял все до замечания.Является ли замечание еще одним способом решения задачи?или это продолжение того,что вы написали ранее?можно поконкретней о чем говорится в замечании.

@aid78: с Вашим замечанием я согласен. Но дело в том, что такого рода соображений может быть много, и они играют роль уже на стадии реализации программы. Она может себя вести как-то по-особому при «малых» значениях $%B$%, или $%B-A$%. У меня в первую очередь речь шла о преодолении чисто математических трудностей для «худшего» случая.

@ivan145: замечание касалось способа вычисления целой части. Вместо $%[n/6]$% можно брать подсчитанное ранее значение $%[n/2]$%, которое далее делим на 3 с округлением вниз и получаем то же самое. Так удобнее реализовать алгоритм. Дальше у меня шло описание основной части, с формулой включений и исключений. Его было бы уместно начать с новой строки, так как это отдельная мысль, хотя и связанная с предыдущей. Фактически, это основной момент в вычислениях. Представьте себе формулу типа $%[n/p_1]+\ldots+[n/p_m]-([n/(p_1p_2]+[n/(p_1p_3)]+\ldots)+([n/(p_1p_2p_3)+\ldots)-$% и далее по тексту.

Ага,это и есть формула включений и исключений,и вы говорите,что здесь можно сделать отсечение,что если произведение превысит 10^9,то уже это необязательно рассматривать.А почему это необязательно рассматривать?и не нарушит ли это итоговый ответ?

@ivan145: представьте себе формулу включений и исключений для этого случая в её полном виде. Если мы поделили $%n$% на очень большое число, то целая часть такого частного равна нулю. Поэтому слагаемые этого вида можно не рассматривать. Мы ведь по формуле $%[n/s]$% подсчитываем количество чисел от 1 до $%n$%, кратных $%s$%, но в случае $%s > 10^9$% таких чисел просто нет.

Да с таким отсечением согласен,но есть проблема.Допустим нам достался худший случай и чисел простых будет 62.И когда (n/(p1p2p3p4p5p6p7p8p9p10)+n/(p1p2p3p4p5p6p7p8p9p11))и т.д. в фор-ле включений и исключений то этот перебор комбинаций займет слишком много времени,если составлять комбинации по 2,3,4,5 то это еще проходит,возможно пройдет и по 6,но когда составляем комбинации из 62 чисел по семь,то тут уже начинаются проблемы.Возможно ли это как-то избежать?

@ivan145: здесь не надо составлять все комбинации (сочетания) из 62 по 7. Учитывать и рассматривать надо только те произведения простых, которые не превосходят $%10^9$%. Их относительно немного, и «отсев» происходит на достаточно ранней стадии. Я показал, как можно осуществить перебор при помощи составления списков. Об этом в последнем абзаце говорится. Суть в том, что «длинных» произведений в списке будет очень мало.

Ага,понял,и еще кое-что по условию задачи a<=10^9 и b<=10^9 а это означает рассматривать надо только те произведения простых которые не превосходят 10^18 или я не прав?

В условии задачи сказано, что год не должен быть простым, а он должен казаться главному герою простым, то есть не иметь простых делителей меньших k+2. Решето Эратосфена от 2 до k+1 здесь должно подойти. Его реализацию на любом распространенном алгоритмическом языке не сложно найти с сети. А если будет лимит времени, то сделайте отдельную программу, которая будет заполнять массив логических переменных от 1 до 10^9 по принципу true если число не имеет делителей меньших k+2 и false в противном случае. В ту программу, что будете тестировать внесите этот массив как массив констант и в самой программе считайте сколько true попало от A до B.

отвечен 18 Янв ’14 10:00

Извините,но я уже это пробывал,слишком много памяти выделяется,поэтому этот вариант отпадает

@ivan145: приходится отвечать здесь. Я посмотрел в условие, и там, насколько я понимаю, $%B$% играет роль количества чисел отрезка, то есть найти надо то, что попадает в $%[A;A+B]$%. Сути дела это не меняет, так как слегка изменяется ограничение: получается $%2\cdot10^9$%, то есть величина того же порядка, что и была. Разумеется, никакого $%10^<18>$% тут нет и быть не может: ведь числа $%A$% и $%B$% всего лишь складываются, а не перемножаются.

@ivan145: я говорил, что не надо считать через $%C_<62>^7$%. Ясно, что это очень большое число (порядка половины миллиарда). У меня был описан другой алгоритм, основанный на составлении списков. Я могу попробовать сегодня вечером его реализовать в Maple чисто ради спортивного интереса. Вариантов там не должно быть слишком много.

@ivan145: я пришёл к этому выводу на основании того, какие именно члены надо учитывать в формуле включений и исключений. Они именно такие, как я описал. Понятно, что каждое из них учитывать нужно в силу вида формулы. То есть надо работать со списками, учитывая то, на что мы уже делили, чтобы не повторить дважды то же самое. А привлекать сочетания тут незачем, потому что очень многие произведения 7 сомножителей слишком велики. Кроме того, сам алгоритм перебора сочетаний, если его реализовать, превратится примерно в то же самое.

Насколько я понимаю,это почти тоже самое,что и перебор сочетаний,но убыстренный в силу того,что мы запоминаем предущие вычисления

@ivan145: я делаю похоже, но немного не так. На каждом шаге мы храним списки чисел. На нулевом шаге есть список из числа n. Скажем, что оно имеет ранг 0. Это число идёт с плюсом при вычислении ответа. Далее возникает m списков из одного числа каждый: $%n/p_1$%; . ; $%n/p_m$%. Этим числам присваиваем ранги от 1 до m. Это всё идёт с минусом. На следующем шаге возникают списки: $%n/(p_1p_2)$% (ранг 2), $%n/(p_1p_3)$%, $%n/(p_2p_3)$% (ранг 3), . $%n/(p_1p_m)$%, . $%n/(p_p_m)$% (ранг m). Знаки всё время меняются; рангом считается наибольший номер i, если делили на $%p_i$%.

для n/(pm-1pm) понятно как будет выглядеть алгоритм,а как он будет выглядеть когда делим n на три элемента?И как мы эти числа все время будем запоминать с этим тоже проблема,ведь их становится все больше и больше

Научный форум dxdy

Если Вы хотите задать новый вопрос, то не дописывайте его в существующую тему, а создайте новую в корневом разделе "Помогите решить/разобраться (М)".

Если Вы зададите новый вопрос в существующей теме, то в случае нарушения оформления или других правил форума Ваше сообщение и все ответы на него могут быть удалены без предупреждения.

Не ищите на этом форуме халяву , правила запрещают участникам публиковать готовые решения стандартных учебных задач. Автор вопроса обязан привести свои попытки решения и указать конкретные затруднения.

Обязательно просмотрите тему Правила данного раздела, иначе Ваша тема может быть удалена или перемещена в Карантин, а Вы так и не узнаете, почему.

Существует ли формула для определения кол-ва простых чисел?

формулы не встречал, но при не очень больших N , можно применить решето эратосфена! конечно не очень удобно.

— Вс авг 16, 2009 02:04:08 —

формулы не встречал, но при не очень больших N , можно применить решето эратосфена! конечно не очень удобно.

Если через $» /> обозначить количество натуральных чисел, взаимнопростых с числом $ s $, непревышающих $ m $, то количество простых чисел, непревышающих $ N $, можно точно подсчитать по бесконечной формуле:

$ \pi(N) = \dfrac<N> <2>+ (i — 1) — \varphi(p_1; \dfrac<N><p_2>) — \varphi((p_1\cdot p_2); \dfrac<N><p_3>) — . — \varphi((p_1\cdot p_2\cdot. \cdot p_<i-1>); \dfrac<N><p_i>) $» />,</p>
<p>где <img decoding=— простые числа 2, 3, 5, 7. непревышающие $ \sqrt <N>$» />.</p>
<p>— Вс авг 16, 2009 12:58:00 —</p>
<p>Бесконечных формул не бывает</p>
<p>Думаю, для начала стоит определиться с тем, что такое «формула». Вот если я напишу, что количество простых чисел в промежутке <img decoding=равно $\pi(N)$, это будет «формулой» или нет?

Просто я пытался вывести формулу и она у меня получилась. Стал искать подобную формулу в интернете, нашёл различные формулы, но точно такую же, как и у меня не нашёл.
Вот она:$\pi(N) = [N]-[\dfrac<N-2><2>]-[\dfrac<N-3><2*3>]-[\dfrac<N-5><2*3*5>]-[\dfrac<N-7><2*3*5*7>]. $» /></p>
<p>Где <img decoding=— это количество простых чисел от 1 до N.

Что это за формула? Наверняка, её вывели лет 150-200 назад. Дайте, пожалуйста, ссылки на сайты, где она упомянута.
Можно ли считать эту формулу тривиальной?

И ещё вопрос, как поставить знак праймориал «#» в коде math?

— Пн авг 17, 2009 16:42:24 —

Батороев
Мне кажется, что формула, написанная мной выше, куда более проста и эффективна,чем упомянутая вами Или же я всё-таки ошибаюсь?

Профессор Снэйп
«Бесконечная формула» — это я сказал детским, «школьным» языком, дабы сам в этом году только окончил 11 класс

Вот она:$\pi(N) = [N]-[\dfrac<N-2><2>]-[\dfrac<N-3><2*3>]-[\dfrac<N-5><2*3*5>]-[\dfrac<N-7><2*3*5*7>]. $» /><br />Где <img decoding=— это количество простых чисел от 1 до N.
Что это за формула? Наверняка, её вывели лет 150-200 назад. Дайте, пожалуйста, ссылки на сайты, где она упомянута.
В очень старом «Кванте»(60е-70е гг)была такая:
$\pi(N) = N-[\dfrac<N><2>]-[\dfrac<N><3>]+[\dfrac<N><2*3>]-[\dfrac<N><5>]+[\dfrac<N><2*5>]+[\dfrac<N><3*5>]-[\dfrac<N><2*3*5>]. $» /><br />Только статья даже не на тему»К-во простых» а про «Формулу включений и исключений»<br />
Просто я пытался вывести формулу и она у меня получилась. Стал искать подобную формулу в интернете, нашёл различные формулы, но точно такую же, как и у меня не нашёл.<br />Вот она:<img decoding=Ваша формула даст $4$( $ 4 - 1 - 0 + 1 $), если я правильно понял Ваши обозначения, конечно.

$ \sharp,\ \# $

P.P.S. То есть у Вас в знаменателе праймориал? А для его вычисления Вам же нужно будет знать те же N простых чисел, что является более сложной задачей, разве нет?

Формулу необходимо подкорректировать.
Правильнее будет так:
$\pi(N) = [N]-[\dfrac<N-2><2\#>]-[\dfrac<N-3><3\#>]-[\dfrac<N-5><5\#>]-[\dfrac<N-7><7\#>]. -1$» /></p>
<p>То есть в конце необходимо вычесть 1, так как 1 — это не простое число. Поэтому в конце в формулу нужно добавить «-1». <br />Обозначения:<br /> <img decoding=— количество простых чисел от 1 до N;
$[x]$— целая часть числа $x$;
$p\#$— праймориал, то есть произведение простых чисел от 1 до $p$(https://dxdy-01.korotkov.co.uk/f/8/a/3/8a3750bd85b655e1b7136e775fe25dbd82.png*3*5*7*. *p$)

Важное замечание.
$[x]$— это именно целая часть числа $x$, а не округление числа $x$.
То есть $[\dfrac<1><2>]=0;$» /> <img decoding=— наибольшее простое, не превышающее $\sqrt N$(если речь идет о точной формуле расчета количества простых).
Заодно спрошу, какой ответ получается по Вашей формуле для $N=100$?
Я тут проверил, формула начинает превышать правильное количество простых чисел при $N=25$, и это расхождение постепенно увеличивается, при $N=100$расхождение составляет $6$, при $N=1000$$129$.
Cкачки происходят при $N=25(5^2),49(7^2),55(5\cdot 11),77(7\cdot 11),85(5\cdot 17),91(7\cdot 13)$. Видимо, Вы в доказательстве где-то не учитываете степени простых чисел и произведения простых чисел, идущих не подряд.

Теория чисел

Простым называется натуральное число, которое делится только на единицу и на себя. Единица при этом простым числом не считается. Составным числом называют непростое число, которое еще и не единица.

Примеры простых чисел: \(2\) , \(3\) , \(5\) , \(179\) , \(10^9+7\) , \(10^9+9\) .

Примеры составных чисел: \(4\) , \(15\) , \(2^<30>\) .

Еще одно определение простого числа: \(N\) — простое, если у \(N\) ровно два делителя. Эти делители при этом равны \(1\) и \(N\) .

Проверка на простоту за линию

С точки зрения программирования интересно научиться проверять, является ли число \(N\) простым. Это очень легко сделать за \(O(N)\) — нужно просто проверить, делится ли оно хотя бы на одно из чисел \(2, 3, 4, \ldots, N-1\) . \(N > 1\) является простым только в случае, если оно не делится на на одно из этих чисел.

Проверка на простоту за корень

Алгоритм можно ускорить с \(O(N)\) до \(O(\sqrt)\) .

Пусть \(N = a \times b\) , причем \(a \leq b\) . Тогда заметим, что \(a \leq \sqrt N \leq b\) .

Почему? Потому что если \(a \leq b < \sqrt\) , то \(ab \leq b^2 < N\) , но \(ab = N\) . А если \(\sqrt < a \leq b\) , то \(N < a^2 \leq ab\) , но \(ab = N\) .

Иными словами, если число \(N\) равно произведению двух других, то одно из них не больше корня из \(N\) , а другое не меньше корня из \(N\) .

Из этого следует, что если число \(N\) не делится ни на одно из чисел \(2, 3, 4, \ldots, \lfloor\sqrt\rfloor\) , то оно не делится и ни на одно из чисел \(\lceil\sqrt\rceil + 1, \ldots, N-2, N-1\) , так как если есть делитель больше корня (не равный \(N\) ), то есть делитель и меньше корня (не равный 1). Поэтому в цикле for достаточно проверять числа не до \(N\) , а до корня.

Разложение на простые множители

Любое натуральное число можно разложить на произведение простых, и с такой записью очень легко работать при решении задач. Разложение на простые множители еще называют факторизацией.

\[11 = 11 = 11^1\] \[100 = 2 \times 2 \times 5 \times 5 = 2^2 \times 5^2\] \[126 = 2 \times 3 \times 3 \times 7 = 2^1 \times 3^2 \times 7^1\]

Рассмотрим, например, такую задачу:

Условие: Нужно разбить \(N\) людей на группы равного размера. Нам интересно, какие размеры это могут быть.

Решение: По сути нас просят найти число делителей \(N\) . Нужно посмотреть на разложение числа \(N\) на простые множители, в общем виде оно выглядит так:

\[N= p_1^ \times p_2^ \times \ldots \times p_k^\]

Теперь подумаем над этим выражением с точки зрения комбинаторики. Чтобы «сгенерировать» какой-нибудь делитель, нужно подставить в степень \(i\) -го простого число от 0 до \(a_i\) (то есть \(a_i+1\) различное значение), и так для каждого. То есть делитель \(N\) выглядит ровно так: \[M= p_1^ \times p_2^ \times \ldots \times p_k^, 0 \leq b_i \leq a_i\] Значит, ответом будет произведение \((a_1+1) \times (a_2+1) \times \ldots \times (a_k + 1)\) .

Алгоритм разложения на простые множители

Применяя алгоритм проверки числа на простоту, мы умеем легко находить минимальный простой делитель числа N. Ясно, что как только мы нашли простой делитель числа \(N\) , мы можем число \(N\) на него поделить и продолжить искать новый минимальный простой делитель.

Будем перебирать простой делитель от \(2\) до корня из \(N\) (как и раньше), но в случае, если \(N\) делится на этот делитель, будем просто на него делить. Причем, возможно, нам понадобится делить несколько раз ( \(N\) может делиться на большую степень этого простого делителя). Так мы будем набирать простые делители и остановимся в тот момент, когда \(N\) стало либо \(1\) , либо простым (и мы остановились, так как дошли до корня из него). Во втором случае надо еще само \(N\) добавить в ответ.

Напишем алгоритм факторизации:

Задание

За сколько работает этот алгоритм?

Решение

За те же самые \(O(\sqrt)\) . Итераций цикла while с перебором делителя будет не больше, чем \(\sqrt\) . Причем ровно \(\sqrt\) операций будет только в том случае, если \(N\) — простое.

А итераций деления \(N\) на делители будет столько, сколько всего простых чисел в факторизации числа \(N\) . Понятно, что это не больше, чем \(O(\log)\) .

Задание

Докажите, что число \(N\) имеет не больше, чем \(O(\log)\) простых множителей в факторизации.

Разные свойства простых чисел*

Вообще, про простые числа известно много свойств, но почти все из них очень трудно доказать. Вот еще некоторые из них:

  • Простых чисел, меньших \(N\) , примерно \(\frac<\ln N>\) .
  • N-ое простое число равно примерно \(N\ln N\) .
  • Простые числа распределены более-менее равномерно. Например, если вам нужно найти какое-то простое число в промежутке, то можно их просто перебрать и проверить — через несколько сотен какое-нибудь найдется.
  • Для любого \(N \ge 2\) на интервале \((N, 2N)\) всегда найдется простое число (Постулат Бертрана)
  • Впрочем, существуют сколь угодно длинные отрезки, на которых простых чисел нет. Самый простой способ такой построить — это начать с \(N! + 2\) .
  • Есть алгоритмы, проверяющие число на простоту намного быстрее, чем за корень. равно примерно \(O(\sqrt[3])\) . Это не математический результат, а чисто эмпирический — не пишите его в асимптотиках.
  • Максимальное число делителей у числа на отрезке \([1, 10^5]\) — 128
  • Максимальное число делителей у числа на отрекзке \([1, 10^9]\) — 1344
  • Максимальное число делителей у числа на отрезке \([1, 10^<18>]\) — 103680
  • Наука умеет факторизовать числа за \(O(\sqrt[4])\) , но об этом как-нибудь в другой раз.
  • Любое число больше трёх можно представить в виде суммы двух простых (гипотеза Гольдбаха), но это не доказано.

Решето Эратосфена

Часто нужно не проверять на простоту одно число, а найти все простые числа до \(N\) . В этом случае наивный алгоритм будет работать за \(O(N\sqrt N)\) , так как нужно проверить на простоту каждое число от 1 до \(N\) .

Но древний грек Эратосфен предложил делать так:

Запишем ряд чисел от 1 до \(N\) и будем вычеркивать числа: * делящиеся на 2, кроме самого числа 2 * затем деляющиеся на 3, кроме самого числа 3 * затем на 5, затем на 7, и так далее и все остальные простые до n. Таким образом, все незачеркнутые числа будут простыми — «решето» оставит только их.

Задание

Найдите этим способом на бумажке все простые числа до 50, потом проверьте с программой:

У этого алгоритма можно сразу заметить несколько ускорений.

Во-первых, число \(i\) имеет смысл перебирать только до корня из \(N\) , потому что при зачеркивании составных чисел, делящихся на простое \(i > \sqrt N\) , мы ничего не зачеркнем. Почему? Пусть существует составное \(M \leq N\) , которое делится на %i%, и мы его не зачеркнули. Но тогда \(i > \sqrt N \geq \sqrt M\) , а значит по ранее нами доказанному утверждению \(M\) должно делиться и на простое число, которое меньше корня. Но это значит, что мы его уже вычеркнули.

Во-вторых, по этой же самое причине \(j\) имеет смысл перебирать только начиная с \(i^2\) . Зачем вычеркивать \(2i\) , \(3i\) , \(4i\) , …, \((i-1)i\) , если они все уже вычеркнуты, так как мы уже вычеркивали всё, что делится на \(2\) , \(3\) , \(4\) , …, \((i-1)\) .

Асимптотика

Такой код будет работать за \(O(N \log \log N)\) по причинам, которые мы пока не хотим объяснять формально.

Гармонический ряд

Научимся оценивать асимптотику величины \(1 + \frac<1> <2>+ \ldots + \frac<1>\) , которая нередко встречается в задачах, где фигурирует делимость.

Возьмем \(N\) равное \(2^i — 1\) и запишем нашу сумму следующим образом: \[\left(\frac<1><1>\right) + \left(\frac<1> <2>+ \frac<1><3>\right) + \left(\frac<1> <4>+ \ldots + \frac<1><7>\right) + \ldots + \left(\frac<1><2^> + \ldots + \frac<1><2^i - 1>\right)\]

Каждое из этих слагаемых имеет вид \[\frac<1> <2^j>+ \ldots + \frac<1> <2^— 1> \le \frac<1> <2^j>+ \ldots + \frac<1> <2^j>= 2^j \frac<1> <2^j>= 1\]

Таким образом, наша сумма не превосходит \(1 + 1 + \ldots + 1 = i \le 2\log_2(2^i — 1)\) . Тем самым, взяв любое \(N\) и дополнив до степени двойки, мы получили асимптотику \(O(\log N)\) .

Оценку снизу можно получить аналогичным образом, оценив каждое такое слагаемое снизу значением \(\frac<1><2>\) .

Попытка объяснения асимптотики** (для старших классов)

Мы знаем, что гармонический ряд \(1 + \frac<1> <2>+ \frac<1> <3>+ \ldots + \frac<1>\) это примерно \(\log N\) , а значит \[N + \frac <2>+ \frac <3>+ \ldots + \frac \sim N \log N\]

А что такое асимптотика решета Эратосфена? Мы как раз ровно \(\frac

\) раз зачеркиваем числа делящиеся на простое число \(p\) . Если бы все числа были простыми, то мы бы как раз получили \(N \log N\) из формули выше. Но у нас будут не все слагаемые оттуда, только с простым \(p\) , поэтому посмотрим чуть более точно.

Известно, что простых чисел до \(N\) примерно \(\frac<\log N>\) , а значит допустим, что k-ое простое число примерно равно \(k ln k\) . Тогда

Но вообще-то решето можно сделать и линейным.

Задание

Решите 5 первых задач из этого контеста:

Линейное решето Эратосфена*

Наша цель — для каждого числа до \(N\) посчитать его минимальный простой делитель. Будем хранить его в массиве min_d. Параллельно будем хранить и список всех найденных простых чисел primes — это ровно те числа \(x\) , у которых \(min\_d[x] = x\) .

Основное утверждение такое:

Пусть у числа \(M\) минимальный делитель равен \(a\) . Тогда, если \(M\) составное, мы хотим вычеркнуть его ровно один раз при обработке числа \(\frac\) .

Мы также перебираем число \(i\) от \(2\) до \(N\) . Если \(min\_d[i]\) равно 0 (то есть мы не нашли ни один делитель у этого числа еще), значит оно простое — добавим в primes и сделаем \(min\_d[i] = i\) .

Далее мы хотим вычеркнуть все числа \(i \times k\) такие, что \(k\) — это минимальный простой делитель этого числа. Из этого следует, что необходимо и достаточно перебрать \(k\) в массиве primes, и только до тех пор, пока \(k < min\_d[i]\) . Ну и перестать перебирать, если \(i \times k > N\) .

Алгоритм пометит все числа по одному разу, поэтому он корректен и работает за \(O(N)\) .

Этот алгоритм работает асимптотически быстрее, чем обычное решето. Но на практике, если писать обычное решето Эратсфена с оптимизациями, то оно оказывается быстрее линейнего. Также линейное решето занимает гораздо больше памяти — ведь в обычном решете можно хранить просто \(N\) бит, а здесь нам нужно \(N\) чисел и еще массив primes.

Зато один из «побочных эффектов» алгоритма — он неявно вычисляет факторизацию всех чисел от \(1\) до \(N\) . Ведь зная минимальный простой делитель любого числа от \(1\) до \(N\) можно легко поделить на это число, посмотреть на новый минимальный простой делитель и так далее.

НОД и НОК

Введем два определения.

Наибольший общий делитель (НОД) чисел \(a_1, a_2, \ldots, a_n\) — это максимальное такое число \(x\) , что все \(a_i\) делятся на \(x\) .

Наименьшее общее кратное (НОК) чисел \(a_1, a_2, \ldots, a_n\) — это минимальное такое число \(x\) , что \(x\) делится на все \(a_i\) .

Например, * НОД(18, 30) = 6 * НОД(60, 180, 315) = 15 * НОД(1, N) = 1 * НОК(12, 30) = 6 * НОК(1, 2, 3, 4) = 12 * НОК(1, \(N\) ) = \(N\)

Зачем они нужны? Например, они часто возникают в задачах.

Условие: Есть \(N\) шестеренок, каждая \(i\) -ая зацеплена с \((i-1)\) -ой. \(i\) -ая шестеренка имеет \(a_i\) зубчиков. Сколько раз нужно повернуть полносьтю первую шестеренку, чтобы все остальные шестеренки тоже вернулись на изначальное место?

Решение: Когда одна шестеренка крутится на 1 зубчик, все остальные тоже крутятся на один зубчик. Нужно найти минимальное такое число зубчиков \(x\) , что при повороте на него все шестеренки вернутся в изначальное положение, то есть \(x\) делится на все \(a_i\) , то есть это НОК( \(a_1, a_2, \ldots, a_N\) ). Ответом будет \(\frac\) .

Еще пример задачи на применение НОД и НОК:

Условие: Город — это прямоугольник \(n\) на \(m\) , разделенный на квадраты единичного размера. Вертолет летит из нижнего левого угла в верхний правый по прямой. Вертолет будит людей в квартале, когда он пролетает строго над его внутренностью (границы не считаются). Сколько кварталов разбудит вертолёт?

Решение: Вертолет пересечет по вертикали \((m-1)\) границу. С этим ничего не поделать — каждое считается как новое посещение какого-то квартала. По горизонтали то же самое — \((n-1)\) переход в новую ячейку будет сделан.

Однако еще есть случай, когда он пересекает одновременно обе границы (то есть пролетает над каким-нибудь углом) — ровно тот случай, когда нового посещения квартала не происходит. Сколько таких будет? Ровно столько, сколько есть целых решений уравнения \(\frac = \frac\) . Мы как бы составили уравнение движения вертолёта и ищем, в скольки целых точках оно выполняется.

Пусть \(t = НОД(n, m)\) , тогда \(n = at, m = bt\) .

Значит, итоговый ответ: \((n-1) + (m-1) — (t-1)\) .

Кстати, когда \(НОД(a, b) = 1\) , говорят, что \(a\) и \(b\) взаимно просты.

Алгоритм Евклида

Осталось придумать, как искать НОД и НОК. Понятно, что их можно искать перебором, но мы хотим хороший быстрый способ.

Давайте для начала научимся искать \(НОД(a, b)\) .

Мы можем воспользоваться следующим равенством: \[НОД(a, b) = НОД(a, b — a), b > a\]

Оно доказывается очень просто: надо заметить, что множества общих делителей у пар \((a, b)\) и \((a, b — a)\) совпадают. Почему? Потому что если \(a\) и \(b\) делятся на \(x\) , то и \(b-a\) делится на \(x\) . И наоборот, если \(a\) и \(b-a\) делятся на \(x\) , то и \(b\) делится на \(x\) . Раз множства общих делитей совпадают, то и максимальный делитель совпадает.

Из этого равенства сразу следует следующее равенство: \[НОД(a, b) = НОД(a, b \operatorname <\%>a), b > a\]

(так как \(НОД(a, b) = НОД(a, b — a) = НОД(a, b — 2a) = НОД(a, b — 3a) = \ldots = НОД(a, b \operatorname <\%>a)\) )

Это равенство дает идею следующего рекурсивного алгоритма:

\[НОД(a, b) = НОД(b \operatorname <\%>a, a) = НОД(a \operatorname <\%>\, (b \operatorname <\%>a), b \operatorname <\%>a) = \ldots\]

Например: \[НОД(93, 36) = \] \[= НОД(36, 93\space\operatorname<\%>36) = НОД(36, 21) = \] \[= НОД(21, 15) = \] \[= НОД(15, 6) = \] \[= НОД(6, 3) = \] \[= НОД(3, 0) = 3\]

Задание:

Примените алгоритм Евклида и найдите НОД чисел: * 1 и 500000 * 10, 20 * 18, 60 * 55, 34 * 100, 250

По-английски наибольший общий делительgreatest common divisor. Поэтому вместо НОД будем в коде писать gcd.

Вообще, в C++ такая функция уже есть в компиляторе g++ — называется __gcd . Если у вас не Visual Studio, то, скорее всего, у вас g++ . Вообще, там много всего интересного.

А за сколько оно вообще работает?

Задание

Докажите, что алгоритм Евклида для чисел \(N\) , \(M\) работает за \(O(\log(N+M))\) .

Кстати, интересный факт: самыми плохими входными данными для алгоритма Евклида являются числа Фибоначчи. Именно там и достигается логарифм.

Как выразить НОК через НОД

По этой формуле можно легко найти НОК двух чисел через их произведение и НОД. Почему она верна?

Посмотрим на разложения на простые множители чисел a, b, НОК(a, b), НОД(a, b).

\[ a = p_1^\times p_2^\times\ldots\times p_n^ \] \[ b = p_1^\times p_2^\times\ldots\times p_n^ \] \[ ab = p_1^\times p_2^\times\ldots\times p_n^ \]

Из определений НОД и НОК следует, что их факторизации выглядят так: \[ НОД(a, b) = p_1^\times p_2^\times\ldots\times p_n^ \] \[ НОК(a, b) = p_1^\times p_2^\times\ldots\times p_n^ \]

Как посчитать НОД/НОК от более чем 2 чисел

Для того, чтобы искать НОД или НОК у более чем двух чисел, достаточно считать их по цепочке:

Почему это верно?

Ну просто множество общих делителей \(a\) и \(b\) совпадает с множеством делителей \(НОД(a, b)\) . Из этого следует, что и множество общих делителей \(a\) , \(b\) и еще каких-то чисел совпадает с множеством общих делителей \(НОД(a, b)\) и этих же чисел. И раз совпадают множества общих делителей, то и наибольший из них совпадает.

С НОК то же самое, только фразу “множество общих делителей” надо заменить на “множество общих кратных”.

Задание

Решите задачи F, G, H, I из этого контеста:

Расширенный алгоритм Евклида*

Очень важным для математики свойством наибольшего общего делителя является следующий факт:

Для любых целых \(a, b\) найдутся такие целые \(x, y\) , что \(ax + by = d\) , где \(d = \gcd(a, b)\) .

Из этого следует, что существует решение в целых числах, например, у таких уравнений: * \(8x + 6y = 2\) * \(4x — 5y = 1\) * \(116x + 44y = 4\) * \(3x + 11y = -1\)

Мы сейчас не только докажем, что решения у таких уравнений существуют, но и приведем быстрый алгоритм нахождения этих решений. Здесь нам вновь пригодится алгоритм Евклида.

Рассмотрим один шаг алгоритма Евклида, преобразующий пару \((a, b)\) в пару \((b, a \operatorname <\%>b)\) . Обозначим \(r = a \operatorname <\%>b\) , то есть запишем деление с остатком в виде \(a = bq + r\) .

Предположим, что у нас есть решение данного уравнения для чисел \(b\) и \(r\) (их наибольший общий делитель, как известно, тоже равен \(d\) ): \[bx_0 + ry_0 = d\]

Теперь сделаем в этом выражении замену \(r = a — bq\) :

\[bx_0 + ry_0 = bx_0 + (a — bq)y_0 = ay_0 + b(x_0 — qy_0)\]

Tаким образом, можно взять \(x = y_0\) , а \(y = (x_0 — qy_0) = (x_0 — (a \operatorname b)y_0)\) (здесь \(/\) обозначает целочисленное деление).

В конце алгоритма Евклида мы всегда получаем пару \((d, 0)\) . Для нее решение требуемого уравнения легко подбирается — \(d * 1 + 0 * 0 = d\) . Теперь, используя вышесказанное, мы можем идти обратно, при вычислении заменяя пару \((x, y)\) (решение для чисел \(b\) и \(a \operatorname <\%>b\) ) на пару \((y, x — (a / b)y)\) (решение для чисел \(a\) и \(b\) ).

Это удобно реализовывать рекурсивно:

Но также полезно и посмотреть, как будет работать расширенный алгоритм Евклида и на каком-нибудь конкретном примере. Пусть мы, например, хотим найти целочисленное решение такого уравнения: \[116x + 44y = 4\] \[(2\times44+28)x + 44y = 4\] \[44(2x+y) + 28x = 4\] \[44x_0 + 28y_0 = 4\] Следовательно, \[x = y_0, y = x_0 — 2y_0\] Будем повторять такой шаг несколько раз, получим такие уравнения: \[116x + 44y = 4\] \[44x_0 + 28y_0 = 4, x = y_0, y = x_0 — 2y_0\] \[28x_1 + 16y_1 = 4, x_0 = y_1, y_0 = x_1 — y_1\] \[16x_2 + 12y_2 = 4, x_1 = y_2, y_1 = x_2 — y_2\] \[12x_3 + 4y_3 = 4, x_2 = y_3, y_2 = x_3 — y_3\] \[4x_4 + 0y_4 = 4, x_3 = y_4, y_3 = x_4 — 3 y_4\] А теперь свернем обратно: \[x_4 = 1, y_4 = 0\] \[x_3 = 0, y_3 =1\] \[x_2 = 1, y_2 =-1\] \[x_1 = -1, y_1 =2\] \[x_0 = 2, y_0 =-3\] \[x = -3, y =8\]

Действительно, \(116\times(-3) + 44\times8 = 4\)

Задание

Решите задачу J из этого контеста:

Операции по модулю

Выражение \(a \equiv b \pmod m\) означает, что остатки от деления \(a\) на \(m\) и \(b\) на \(m\) равны. Это выражение читается как « \(a\) сравнимо \(b\) по модулю \(m\) ».

Еще это можно опрделить так: \(a\) сравнимо c \(b\) по модулю \(m\) , если \((a — b)\) делится на \(m\) .

Все целые числа можно разделить на классы эквивалентности — два числа лежат в одном классе, если они сравнимы по модулю \(m\) . Говорят, что мы работаем в «кольце остатков по модулю \(m\) », и в нем ровно \(m\) элементов: \(0, 1, 2, \cdots, m-1\) .

Сложение, вычитение и умножение по модулю определяются довольно интуитивно — нужно выполнить соответствующую операцию и взять остаток от деления.

С делением намного сложнее — поделить и взять по модулю не работает. Об этом подробнее поговорим чуть дальше.

Задание

Посчитайте: * \(2 + 3 \pmod 5\) * \(2 * 3 \pmod 5\) * \(2 ^ 3 \pmod 5\) * \(2 — 4 \pmod 5\) * \(5 + 5 \pmod 6\) * \(2 * 3 \pmod 6\) * \(3 * 3 \pmod 6\)

Для умножения (в C++) нужно ещё учитывать следующий факт: при переполнении типа всё ломается (разве что если вы используете в качестве модуля степень двойки).

  • int вмещает до \(2^ <31>— 1 \approx 2 \cdot 10^9\) .
  • long long вмещает до \(2^ <63>— 1 \approx 8 \cdot 10^<18>\) .
  • long long long в плюсах нет, при попытке заиспользовать выдает ошибку long long long is too long .
  • Под некоторыми компиляторами и архитектурами доступен int128 , но не везде и не все функции его поддерживают (например, его нельзя вывести обычными методами).

Зачем нужно считать ответ по модулю

Очень часто в задаче нужно научиться считать число, которое в худшем случае гораздо больше, чем \(10^<18>\) . Тогда, чтобы не заставлять вас писать длинную арифметику, автор задачи часто просит найти ответ по модулю большого числа, обычно \(10^9 + 7\)

Кстати, вместо того, чтобы писать \(1000000007\) удобно просто написать \(1e9 + 7\) . \(1e9\) означает \(1 \times 10^9\)

Быстрое возведение в степень

Задача: > Даны натуральные числа \(a, b, c < 10^9\) . Найдите \(a^b\) (mod \(c\) ).

Мы хотим научиться возводить число в большую степень быстро, не просто умножая \(a\) на себя \(b\) раз. Требование на модуль здесь дано только для того, чтобы иметь возможность проверить правильность алгоритма для чисел, которые не влезают в int и long long.

Сам алгоритм довольно простой и рекурсивный, постарайтесь его придумать, решая вот такие примеры (прямо решать необязательно, но можно придумать, как посчитать значение этих чисел очень быстро):

  • \(3^2\)
  • \(3^4\)
  • \(3^8\)
  • \(3^<16>\)
  • \(3^<32>\)
  • \(3^<33>\)
  • \(3^<66>\)
  • \(3^<132>\)
  • \(3^<133>\)
  • \(3^<266>\)
  • \(3^<532>\)
  • \(3^<533>\)
  • \(3^<1066>\)

Да, здесь специально приведена такая последовательность, в которой каждое следующее число легко считается через предыдущее: его либо нужно умножить на \(a=3\) , либо возвести в квадрат. Так и получается рекурсивный алгоритм:

  • \(a^0 = 1\)
  • \(a^<2k>=(a^)^2\)
  • \(a^<2k+1>=a^<2k>\times a\)

Нужно только после каждой операции делать mod: * \(a^0 \pmod c = 1\) * \(a^ <2k>\pmod c = (a^ \pmod c)^2 \pmod c\) * \(a^ <2k+1>\pmod c = ((a^<2k>\pmod c) \times a) \pmod c\)

Этот алгоритм называется быстрое возведение в степень. Он имеет много применений: * в криптографии очень часто надо возводить число в большую степень по модулю * используется для деления по простому модулю (см. далее) * можно быстро перемножать не только числа, но еще и матрицы (используется для динамики, например)

Асимптотика этого алгоритма, очевидно, \(O(\log c)\) — за каждые две итерации число уменьшается хотя бы в 2 раза.

Задание

Решите задачу K из этого контеста:

Задание

Решите как можно больше задач из практического контеста:

Деление по модулю*

Давайте все-таки научимся не только умножать, но и делить по простому модулю. Вот только что это значит?

\(a / b\) = \(a \times b^<-1>\) , где \(b^<-1>\) — это обратный элемент к \(b\) .

Определение: \(b^<-1>\) — это такое число, что \(bb^ <-1>= 1\)

Утверждение: в кольце остатков по простому модулю \(p\) у каждого остатка (кроме 0) существует ровно один обратный элемент.

Например, обратный к \(2\) по модулю \(5\) это \(3\) ( \(2 \times 3 = 1 \pmod 5\) ))

Задание

Найдите обратный элемент к: * числу \(3\) по модулю \(5\) * числу \(3\) по модулю \(7\) * числу \(1\) по модулю \(7\) * числу \(2\) по модулю \(3\) * числу \(9\) по модулю \(31\)

Давайте докажем это утверждение: надо заметить, что если каждый ненулевой остаток \(1, 2, \ldots, (p-1)\) умножить на ненулевой остаток \(a\) , то получатся числа \(a, 2a, \ldots, (p-1)a\) — и они все разные! Они разные, потому что если \(xa = ya\) , то \((x-y)a = 0\) , а значит \((x — y) a\) делится на \(p\) , \(a\) — ненулевой остаток, а значит \(x = y\) , и это не разные числа. И из того, что все числа получились разными, это все ненулевые, и их столько же, следует, что это ровно тот же набор чисел, просто в другом порядке!

Из этого следует, что среди этих чисел есть \(1\) , причем ровно один раз. А значит существует ровно один обратный элемент \(a^<-1>\) . Доказательство закончено.

Это здорово, но этот обратный элемент еще хочется быстро находить. Быстрее, чем за \(O(p)\) .

Есть несколько способов это сделать.

Через малую теорему Ферма

Малая теорема Ферма: > \(a^ = 1 \pmod p\) , если \(p\) — простое, \(a \neq 0 \pmod p\) ).

Доказательство: В предыдущем пункте мы выяснили, что множества чисел \(1, 2, \ldots, (p-1)\) и \(a, 2a, \ldots, (p-1)a\) совпадают. Из этого следует, что их произведения тоже совпадают по модулю: \((p-1)! = a^ (p-1)! \pmod p\) .

\((p-1)!\neq 0 \pmod p\) а значит на него можно поделить (это мы кстати только в предыдущем пункте доказали, поделить на число — значит умножить на обратный к нему, который существует).

А значит, \(a^

= 1 \pmod p\) .

Как это применить Осталось заметить, что из малой теоремы Ферма сразу следует, что \(a^\) — это обратный элемент к \(a\) , а значит мы свели задачу к возведению \(a\) в степень \(p-2\) , что благодаря быстрому возведению в степень мы умеем делать за \(O(\log p)\) .

Обобщение У малой теоремы Ферма есть обобщение для составных \(p\) :

Теорема Эйлера: > \(a^ <\varphi(p)>= 1 \pmod p\) , \(a\) — взаимно просто с \(p\) , а \(\varphi(p)\) — это функция Эйлера (количество чисел, меньших \(p\) и взаимно простых с \(p\) ).

Доказывается теорема очень похоже, только вместо ненулевых остатков \(1, 2, \ldots, p-1\) нужно брать остатки, взаимно простые с \(p\) . Их как раз не \(p-1\) , а \(\varphi(p)\) .

Для нахождения обратного по этой теореме достаточно посчитать функцию Эйлера \(\varphi(p)\) и найти \(a^ <-1>= a^<\varphi(p) - 1>\) .

Но с этим возникают большие проблемы: посчитать функцию Эйлера сложно. Более того, на предполагаемой невозможности быстро ее посчитать построены некоторые криптографические алгоритм типа RSA. Поэтому быстро делить по составному модулю этим способом не получится.

Через расширенный алгоритм Евклида

Этим способом легко получится делить по любому модулю! Рекомендую.

Пусть мы хотим найти \(a^ <-1>\pmod p\) , \(a\) и \(p\) взаимно простые (а иначе обратного и не будет существовать).

Давайте найдем корни уравнения

Они есть и находятся расширенным алгоритмом Евклида за \(O(\log p)\) , так как \(НОД(a, p) = 1\) , ведь они взаимно простые.

Тогда если взять остаток по модулю \(p\) :

А значит, найденный \(x\) и будет обратным элементом к \(a\) .

То есть надо просто найти \(x\) из решения того уравнения по модулю \(p\) . Можно брать по модулю прямо походу решения уравнения, чтобы случайно не переполниться.

Алгоритмы поиска простых чисел в C#

Простое число — это целое положительное число, имеющее ровно два различных натуральных делителя — единицу и самого себя. Определение того является ли число простым или нет — это одна из самых распространенных задач, решаемых в рамках курса лабораторных работ по информатике. Ниже будет представлена реализация алгоритма поиска простых чисел в C#, а также использование различных циклов для вывода простых чисел.

Задача №1

Проверить является ли число простым и вывести результат вычисления в консоль.

Алгоритм определения простого числа

Для того, чтобы проверить является ли число N простым необходимо:

  1. Задаем значение N
  2. Задаем цикл for от 2 до N-1 (счётчик цикла обозначим, например, как i )
    1. Если остаток от деление N на i равен нулю, то число не является простым — выходим из цикла
    2. Если остаток от деления N на i не равен нулю, то переходим к следующей итерации цикла

    Реализация алгоритма определения простого числа в C#

    Метод определения простого числа в C#, реализующий представленный выше алгоритм может быть представлен следующим образом:

    Пример определения простых чисел из диапазона от 1 до N

    Если по условиям задачи конечное значение диапазона задается в виде числа N , то здесь удобно использовать цикл for или while. Пример программы с использованием цикла for представлен ниже:

    Строго говоря, число 1 не является ни простым не составным, поэтому его можно было бы исключить из цикла, что мы и сделаем в следующем примере.

    Пример определения заданного количества простых чисел

    Задача поиска простых чисел может быть сформулирована и по другому, например, так: найти первые N простых чисел. В этом случае нам будет выгодно использовать цикл while:

    Результатом работы программы будет вывод в консоль первых N простых чисел.

    Итого

    Сегодня мы рассмотрели алгоритм поиска простых чисел и реализовали этот алгоритм в C#. В зависимости от условий задачи, мы использовали различные виды циклов для поиска набора простых чисел.

    Читать:
    Как скопировать ссылку в фигме

Похожие статьи