Задачи с простыми числами.
Представлен ряд задач, связанных с простыми числами - натуральными (целыми положительными) числами, большими 1, не имеющими других делителей, кроме самих себя и единицы.
Рассмотрим сначала такую задачу:
Задача 1. <Дано натуральное число n. Проверить, является ли оно простым>.
Метод решения этой задачи очевиден. Если n - четное, то оно не простое (составное), иначе следует проверить, являются ли делителем n числа 3, 5, 7,...,n/3 (проверять до n-1 и даже до n/2 нет необходимости), и если хотя бы для одного числа ответ будет положительным, то число n - составное. Заметим при этом, что число 2 - простое.
Программа на школьном алгоритмическом языке имеет вид:
алг Проверка нач цел n,i, лог simp | вывод нс, "n=" | ввод n | если n=2 | |то вывод нс, "Это число простое" | |иначе | | если mod(n,2) = 0 |n - четное число | | |то вывод нс, "Это число составное" | | |иначе | | | simp:=да | | | нц для i от 3 до div(n,3) шаг 2 | | | | если mod(n,i)=0 |встретился делитель числа n | | | | |то simp:=нет | | | | все | | | кц | | | если simp | | | |то вывод нс, "Это число простое" | | | |иначе вывод нс, "Это число составное" | | | все | | все | все кон
Можно ускорить вычисления и проверять делимость n на еще меньшее количество чисел - до квадратного корня из n. Если среди проверенных чисел нет делителей числа n, то их нет и среди остальных нечетных чисел. Можно также прекратить вычисления после того, как только встре-тился делитель числа n. С учетом этого приведенная выше программа мо-жет быть модифицирована путем замены имеющегося в ней цикла на следую-щий:
... simp:=да i:=3 нц пока i<=int(sqrt(n)) и simp | если mod(n,i)=0 |встретился делитель числа n | |то simp:=нет | |иначе i:=i+2 | все кц ...
а функция, проверяющая, является ли число n простым, и возвращаю-щая результат логического типа, имеет вид:
алг лог Simple(арг цел n) нач цел i, лог simp |simp=нет, если встретится делитель числа n | если n=2 | |то знач:=да |значение функции | |иначе | | если mod(n,2) = 0 | | |то знач:=нет | | |иначе | | | simp:=да | | | i:=3 | | | нц пока i<=int(sqrt(n)) и simp | | | | если mod(n,i)=0 | | | | |то simp:=нет | | | | |иначе i:=i+2 | | | | все | | | кц | | | если simp | | | |то знач:=да | | | |иначе знач:=нет | | | все | | все | все кон
Язык Паскаль:
Uses CRT;
Var n: longint;
Function Simple(n:longint):boolean;
Var i:longint;simp : boolean { simp=false, если встретился делитель числа n} ;
begin
if n=2 then Simple:=true
else if n mod 2 = 0 then Simple:=false
else begin
simp:=true;
i:=3;
while (i<=trunc(sqrt(n))) and simp do
if n mod i =0 then simp:=false else i:=i+2;
if simp then Simple:=true else Simple:=false
end
end;
Основная часть программы:
BEGIN
clrscr;
readln(n);
if Simple(n) then writeln('Yes') else writeln('No')
END.
Задача 2. Даны натуральные числа a,b (a<=b). Получить все простые числа p, удовлетворяющие неравенствам a<=p<=b.
Сделаем несколько предварительных замечаний.
Очевидно, что при решении задачи в качестве возможных "кандида-тов" в простые числа можно рассматривать только нечетные числа из ин-тервала a<=n<=b, а также число 2, если оно входит в этот интервал. За-метим также, что возможен случай a=1 и что число 1 простым не счита-ется (по определению).
Основные величины, используемые в программах (кроме величин a и b):
- n - число, проверяемое на "простоту";
- first - начальное значение этого числа;
- simp - величина логического типа, в зависимости от которой дается ответ на вопрос, явля-ется ли число n простым.
- k - количество найденных простых чисел в исследуемом интервале.
Основные этапы решения задачи:
- Ввод значений a и b.
- Проверка принадлежности числа 2 интервалу a<=2<=b (что возмож-но при a=1 или a=2).
- Определение значения first. Если a - четное число, то first=a+1, если нечетное - то first=3, если a=1 и first=a, если a>=3.
- Проверка всех нечетных чисел n от first до b и вывод на экран простых из них (с подсчетом значения k).
Если величина k=0, то это значит, что в указанном интервале простых чисел нет.
Приведем сначала программу с вложенными циклами.
Var a, b, n, first, k, i: integer; simp:boolean;
BEGIN
a:=2; b:=1; {условно для выполнения цикла}
while a>b do begin
write('a='); readln (a);
write('b='); readln (b);
if a>b then writeln('Число a не должно быть больше числа b!')
end;
if (a=1) or (a=2) then begin
write(2,' ');
k:=1
end;
if a mod 2 =0 then first:=a+1 {поиск начинаем с нечетного числа}
else if a=1 then first:=3
else first:=a;
n:=first;
while n<= b do begin
simp:=True;
i:=3; {первый возможный делитель числа n}
while (i<=trunc(sqrt(n))) and simp do
if n mod i=0 then simp:=False
else i:=i+2;
if simp then begin
write(n,' ');
k:=k+1
end;
n:=n+2 {поиск ведем только среди нечетных чисел}
end;
if k=0 then writeln('В этом диапазоне простых чисел нет')
END.
Читаемость приведенной программы значительно улучшается, если про-верку числа n на "простоту" проводить с помощью функции (процедуры), возвращающей результат логического типа. Эта функция отличается от описанной в Задаче 1, тем, что в ней иссле-дуются только нечетные числа.
Школьный алгоритмический язык:
алг лог Simple(арг цел n) нач цел i, лог simp |simp=нет, если встретится делитель числа n | simp:=да | i:=3 | нц пока i<=int(sqrt(n)) и simp | | если mod(n,i)=0 | | |то simp:=нет | | |иначе i:=i+2 | | все | кц | если simp | |то знач:=да | |иначе знач:=нет | все кон
Язык Паскаль:
Var a,b,n,first,k:integer;
Function Simple(n:integer):boolean;
Var i:integer;
simp:boolean; {simp=False, если встретится делитель числа n }
begin
simp:=True;
i:=3;
while (i<=trunc(sqrt(n))) and simp do
if n mod i=0 then simp:=False
else i:=i+2;
if simp then Simple:=True else Simple:=False;
end;
BEGIN
a:=2; b:=1;
while a>b do begin
write('a='); readln (a);
write('b='); readln (b);
if a>b then writeln('Число a не должно быть больше числа b!') end;
if (a=1) or (a=2) then begin
write(2,' ');
k:=1
end;
if a mod 2 =0 then first:=a+1
else if a=1 then first:=3
else first:=a;
n:=first;
while n< b do begin
if Simple(n) then begin
write(n,' ');
k:=k+1
end;
n:=n+2
end;
if k=0 then writeln('В этом диапазоне простых чисел нет')
END.
Примечание. Во всех представленных программах случаи a=1,b=1 и a=1,b=2 не рассматривались.
Задача 3. Найти 100 первых простых чисел.
Как и в задаче 2, приведем сначала программы с вложенными циклами. Основные величины, используемые в программах:
- n - число, проверяемое на "простоту";
- simp - величина, назначение которой описано при разборе задачи 2;
- k - количество найденных простых чисел.
Школьный алгоритмический язык:
алг Задача 3 нач цел n,i,k,лог simp | вывод 2 | k:=1 | n:=3 | нц пока k<100 | | simp:=да | | i:=3 | | нц пока i<=int(sqrt(n)) и simp | | | если mod(n,i)=0 | | | |то simp:=нет | | | |иначе i:=i+2 | | | все | | кц | | если simp | | |то вывод n | | | k:=k+1 | | все | | n:=n+2 | кц кон
Язык Паскаль:
Var n,k,i:integer; simp:boolean; BEGIN write(2,' '); k:=1; n:=3; while k<100 do begin simp:=True; i:=3; while (i<=trunc(sqrt(n))) and simp do if n mod i=0 then simp:=False else i:=i+2; if simp then begin write(n,' '); k:=k+1 end; n:=n+2 end; END.
Программа, использующая функцию:
Var n,k:integer;
Function Simple(n:integer):boolean;
... см. задачу 2
end;
BEGIN {Основной части программы}
write(2,' '); k:=1;
n:=3;
while k< 100 do begin
if Simple(n) then begin
write(n,' ');
k:=k+1
end;
n:=n+2
end;
END.
Имеется еще один метод решения рассматриваемой задачи. Он заключается в том, что все найденные простые числа хранятся в некотором массиве, а делимость очередного кандидата проверяется только на числа из этого массива. При этом уменьшается количество возможных делителей, что приводит к ускорению работы программы. Причем и эту проверку можно ускорить, если проверять делимость очередного числа n только на те элементы массива, значение которых не больше квадратного корня из n. Данный метод особенно эффективен в тех случаях, когда необходимо найти большое количество простых чисел.
Приведем программы, в которых применяется этот метод.
Назначение величин k, n и simp в программах аналогично описанному выше. Дополнительно используемые величины:
- find - массив найденных простых чисел (из 100 элементов);
- limit - величина, равная квадратному корню из n.
Школьный алгоритмический язык:
алг Задача 3 нач цел n,i,k, цел таб find[1:100], лог simp, цел limit | find[1]:=2; |записываем в массив простых чисел одно известное | вывод 2 | k:=1 |число найденных простых чисел | n:=3; |первый "кандидат" в простые числа | нц пока k<100 | | limit:=int(sqrt(n)) | | simp:=да | | i:=1 | | нц пока i<=k и simp и find[1]<=limit |условие проверки делимости | | | если mod(n,find[i])=0 | | | |то simp:=нет | | | |иначе i:=i+1 | | | все | | кц | | если simp | | |то | | | вывод n |n - простое число | | | k:=k+1; |добавляем найденное n | | | find[k]:=n |в массив простых чисел | | все | | n:=n+2 |следующий кандидат в простые числа | кц кон
Язык Паскаль:
Var n,i,k:integer; find:array[1..100] of integer; simp:boolean; limit:integer;
BEGIN
find[1]:=2; {записываем в массив простых чисел одно известное}
write(2,' ');
k:=1; {число найденных простых чисел}
n:=3; {первый "кандидат" в простые числа}
while k<100 do
begin
limit:=trunc(sqrt(n));
simp:=true;
i:=1;
while (i<=k) and simp=true and (find[i]<=limit)
{условие проверки делимости числа n}
do if n mod find[i]=0 then simp:=false else i:=i+1;
if simp then begin
write(n,' '); {n - простое число}
k:=k+1; {добавляем найденное n}
find[k]:=n {в массив простых чисел}
end;
n:=n+2 {следующий кандидат в простые числа}
end
END.
Задача 4. Дано четное число n>2; проверить для этого числа гипотезу Гольдбаха. Эта гипотеза (по сегодняшний день не опровергнутая и пол-ностью не доказанная) заключается в том, что каждое четное n, большее двух, представляется в виде суммы двух простых чисел. Определить про-цедуру, позволяющую распознавать простые числа).
Для проверки гипотезы следует определить, имеются ли среди пар чисел 2 и n-2, 3 и n-3,..., n-2 и 2 такие, что числа, их составляющие, - простые.
Приведем сначала программу на школьном алгоритмическом языке. Ве-личины, используемые в ней:
- n - число, применительно к которому проверяется гипотеза;
- item1 - первое число в перечисленных парах чисел;
- k - число пар с простыми составляющими (очевидно, что это число может быть больше 1, например, для n=10: 3 и 7, 5 и 5, 7 и 3).
В программе используется функция Simple, "распознающая" простые числа, описанная в Задаче 1.
алг Задача 4 нач цел n,item1, k | n:=1 |условно для выполнения цикла | нц пока mod(n,2)=1 | | вывод нс,"n=" ; ввод n | | если mod(n,2)=1 | | |то | | | вывод нс,"Число n должно быть четным!" | | все | кц | item1:=3; k:=0 | нц для item1 от 2 до n-2 | | если Simple(item1) и Simple(n-item1) | | |то k:=k+1 | | все | кц | если k>0 | |то вывод нс,"Для данного n гипотеза справедлива" | |иначе вывод нс,"Для данного n гипотеза не справедлива" | все кон
Существенного сокращения числа вычислений можно достичь, если учитывать, что:
- для n>4 возможные значения п р о с т ы х составляющих пар - нечетные числа, т.к. единственным простым четным числом является 2;
- можно рассматривать значения item1 только до n/2 (если в этом интервале нет пар чисел с простыми item1 и n-item1, то их нет и среди остальных значений item1).
- можно прекратить вычисления как только будет найдена первая пара простых чисел.
В программах использована величина hypot логического типа, от ко-торой зависит ответ на вопрос о справедливости гипотезы Гольдбаха. В случае положительного ответа на экран выводится также первая найденная пара простых слагаемых item1 и n-item1.
Школьный алгоритмическй язык:
алг лог Simple(арг цел n) нач ... см. выше кон алг Задача 4 нач цел n,item1, лог hypot | n:=1 | нц пока mod(n,2)=1 или n<4 | | вывод нс,"n=" ; ввод n | | если mod(n,2)=1 | | |то | | | вывод нс,"Число n должно быть четным!" | | все | | если n<4 | | |то | | | вывод нс,"Число n должно быть >2!" | | все | кц | hypot:=нет | если n=4 | |то |проверим и число 4 | | item1:=2 | | hypot:=да | |иначе |n>4 | | item1:=3 | | нц пока item1<=div(n,2) и не hypot | | | если Simple(item1) и Simple(n-item1) | | | |то hypot:=да | | | |иначе item1:=item1+2 | | | все | | кц | все | если hypot | |то вывод нс,"Для данного n гипотеза справедлива" | | вывод нс,"Простыми слагаемыми являются числа", item1,"и",n-item1 | |иначе вывод нс,"Для данного n гипотеза несправедлива"" | все кон
Язык Паскаль:
Var n,item1:integer; hypot:boolean;
Function Simple(n:integer):boolean;
Var i:integer;
simp:boolean; {simp=False, если встретится делитель числа n}
begin
if n=2 then Simple:=True
else if n mod 2=0 then Simple:=False
else begin
simp:=True;
i:=3;
while (i<=trunc(sqrt(n))) and simp do
if n mod i=0 then simp:=False
else i:=i+2;
if simp then Simple:=True else Simple:=False;
end;
end;
BEGIN
n:=1; {условно для выполнения цикла}
while n mod 2 = 1 do
begin
write('n='); readln (n);
if n mod 2 = 1 then writeln('Число n должно быть четным!')
end;
hypot:=False;
if n=4 then begin {проверяем и число 4}
item1:=2;
hypot:=True
end
else begin {n>4}
item1:=3;
while (item1<=n div 2) and not hypot do
if Simple(item1) and Simple(n-item1) then hypot:=True
else item1:=item1+2
end;
if hypot then
begin
writeln('Для данного n гипотеза справедлива');
writeln('Простыми слагаемыми являются числа ',item1,' и ',n-item1) end
else writeln('Для данного n гипотеза не справедлива');
END.
Задача 5. Дано натуральное число n. Выяснить, имеются ли среди n, n+1, ..., 2n близнецы, т.е. простые числа, разность между которыми равна двум. (Определить процедуру, позволяющую распознавать простые числа).
Величины, используемые в программах (кроме величины n): m - число, проверяемое на "простоту";
- first - начальное значение этого числа;
- k - число пар "близнецов", найденных в рассматриваемом интервале.
Основные этапы решения задачи:
- Ввод значения n.
- Определение значения first (см. задачу 1).
- Поиск чисел-близнецов среди нечетных чисел m от first до 2n и вывод их на экран (с подсчетом количества).
Школьный алгоритмический язык:
алг лог Simple(арг цел n) нач ... см. задачу 1 кон алг Задача 5 нач цел n, first, m, k | вывод нс,"n=" ; ввод n | вывод нс, "Числа-близнецы" | если mod(n,2)=0 | |то first:=n+1 |поиск начинаем с нечетного числа | |иначе first:=n | все | k:=0 | нц для m от first до 2*n-2 шаг 2 |и ведем среди таких же чисел | | если Simple(m) и Simple(m+2) | | |то вывод m,"и",m+2 | | | k:=k+1 | | все | кц | если k=0 |например, при n=6 | |то вывод "в рассматриваемом диапазоне отсутствуют" | все кон
Язык Паскаль:
Var n,m,first,k:integer;
Function Simple(n:integer):boolean;
... (см. задачу 2)
end;
BEGIN
write('n='); readln (n);
write('Числа-близнецы ');
if n mod 2 =0 then first:=n+1
else first:=n; {поиск начинаем с нечетного числа}
m:=first;
while m<2*n-2 do
begin
if Simple(m) and Simple(m+2) then
begin
write(m,' и ',m+2,' ');
k:=k+1
end;
m:=m+2 {и ведем среди таких же чисел}
end;
if k=0 {например, при n=6}
then write('в рассматриваемом диапазоне отсутствуют')
END.
Примечание. В условии задачи требовалось только ответить на вопрос, имеются ли близнецы в указанном диапазоне чисел. При необходи-мости строгого соблюдения условий задачи можно не выводить найденные значения близнецов на экран, а выводить только ответ на вопрос в за-висимости от значения k. При этом можно также прекратить поиск после нахождения первой пары чисел-близнецов.
Нетрудно заметить, что в представленных программах значение функции (или величины) Simple для каждого значения m определяется дважды, что нерационально. Этот недостаток можно устранить, если использовать величину was_simple логического, определяющую, являлось ли простым число, пред-шествовавшее расcматриваемому числу m. Естественно, что в этом случае следует проверять значения m до 2n, а не до 2n-2, как это делалось ра-нее.
Школьный алгоритмический язык:
алг лог Simple(арг цел n) нач ... см. выше кон алг Задача 5 нач цел n,first,m,k, лог was_simple | вывод нс,"n=" ; ввод n | вывод нс, "Числа-близнецы" | если mod(n,2)=0 | |то first:=n+1 | |иначе first:=n | все | k:=0 | was_simple:=нет | нц для m от first до 2*n шаг 2 | | если Simple(m) | | |то | | | если was_simple | | | |то | | | | вывод m-2,"и",m | | | | k:=k+1 | | | все | | | was_simple:=да | | |иначе | | | was_simple:=нет | | все | кц | если k=0 | |то вывод "в рассматриваемом диапазоне отсутствуют" | все кон
Язык Паскаль:
Var n,m,first,k:integer; was_simple:boolean;
Function Simple(n:integer):boolean;
... см. выше
end;
BEGIN
write('n='); readln (n);
write('Числа-близнецы ');
if n mod 2 =0 then first:=n+1
else first:=n;
m:=first;
while m<2*n do
begin
if Simple(m) then begin
if was_simple then
begin
write(m-2,' и ',m,' '); k:=k+1
end;
was_simple:=True
end
else was_simple:=False;
m:=m+2
end;
if k=0 then write('в рассматриваемом диапазоне отсутствуют')
end.
mailto:zlato@orc.ru
Оставить комментарий
Комментарии


Попробую восполнить для простых чисел.
Итак имеем по определению: простое число -- целое число большее 1 и которое делится без остатка только на 1 и на себя. Т.е. простые числа: 2, 3, 5, 7 и т.д.
Для оптимизации алгоритмов проверки простых чисел можно использовать несколько соображений (применяются в статье, здесь привожу с доказательством):
1)Простое число -- нечетное число (за исключением числа 2, которое простое).
Доказательство очевидно, поскольку все четные кратны двум, а значит уже делимы
без остатка хотя бы на двушку.
2)Если число составное, то его можно представить в виде произведения целых a=m*M;
значит корень(a)=корень(m*M), т.е. сред. геометрическому; значит корень(а) лежит
между m и M. Поэтому, "забраковать" число как не простое можно выполнив проверки
лишь для чисел <= корня от него.
3)Нечетное число делится на четное с остатком. Действительно, предположим,
что верно обратное, тогда нечетное (2m+1) при делении на четное (2n) должно
дать целое (k), т.е. (2m+1)/(2n) = k. Это равносильно тому, что (2m+1)=2nk.
Но 2nk четное. Получили противоречие: нечетное равно четному.
Ну и пример от себя на C++ в обобщение сказанного:
Функция проверки простых чисел.
[code/]
bool prime(int c) {
if (c < 2) return false;
if (c == 2) return true;
if (!(c % 2)) return false;
//цикл по нечетным от 3 до i=корень(с);
//c=3, 5 и 7 этот цикл пропустят, но им
//можно - они простые :)
for (int i = 3; i*i <= c; i+=2)
if (!(c % i))
return false;
return true;
}
[/code]
Здесь сравнение с корнем заменено на более быструю эквивалентную конструкцию
i*i <= c (равносильно i<=корень(с))


bool prime(int c) {
if (c < 2) return false;
if (c == 2) return true;
if (!(c % 2)) return false;
//один раз корень посчитать быстрее, чем i*i сравнивать с c в цикле
int im = (int)sqrt(c) + 1; //+1 чтобы не писать <= im
for (int i = 3; i < im; i+=2)
if (!(c % i)) return false;
return true;
}


bool prime(int c) {
if (c < 2) return false;
if (c == 2) return true;
if (!(c % 2)) return false;
int im = (int)ceil(sqrt(c)) + 1; //+1 чтобы не писать <= im;
//c=3 этот цикл пропустит, но ей можно (3-простое)
for (int i = 3; i < im; i+=2)
if (!(c % i)) return false;
return true;
}




