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

Я хочу найти простое число от 0 до длинной переменной, но не могу получить никаких результатов.

Программа

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication16
{
    class Program
    {
        void prime_num(long num)
        {
            bool isPrime = true;
            for (int i = 0; i <= num; i++)
            {
                for (int j = 2; j <= num; j++)
                {
                    if (i != j && i % j == 0)
                    {
                        isPrime = false;
                        break;
                    }
                }
                if (isPrime)
                {
                    Console.WriteLine ( "Prime:" + i );
                }
                isPrime = true;
            }
        }

        static void Main(string[] args)
        {
            Program p = new Program();
            p.prime_num (999999999999999L);
            Console.ReadLine();
        }
    }
}

Может ли кто-нибудь мне помочь и выяснить, в чем заключается возможная ошибка в программе?


person sandy101    schedule 02.10.2009    source источник
comment
Какой шаблон проекта был использован для создания этого проекта.   -  person Alexandre Brisebois    schedule 02.10.2009
comment
Предупреждение о домашнем задании !!   -  person whatnick    schedule 02.10.2009
comment
Получите ли вы какой-нибудь результат, если для начала введете небольшое число, например 10?   -  person Eric J.    schedule 02.10.2009
comment
Вероятно, домашнее задание, ничего плохого в этом, пока спрашивающий попытался ответить на домашнее задание и застрял на каком-то конкретном вопросе (как, кажется, здесь).   -  person Eric J.    schedule 02.10.2009
comment
@ Джордж Стокер: это самый патологический пример этой проблемы, который я видел: S   -  person Esteban Küber    schedule 02.10.2009
comment
Даже если вы придерживаетесь наивного алгоритма, по крайней мере, прекратите поиск по квадратному корню.   -  person Tamas Czinege    schedule 02.10.2009
comment
Как долго это займет на самом деле? 999999999999999L - довольно большое число?   -  person Guillermo Phillips    schedule 02.10.2009
comment
@GuillermoPhillips, это займет время жизни, даже если оно исправлено в соответствии с приведенными ниже ответами, и при этом не объясняется цель создания всех этих простых чисел. Если цель состоит в их суммировании, обнаружении первого появления пробелов, двойников, троек и т. Д., Тогда единственно возможным подходом было бы написать высокоэффективную многопроцессорную версию Сита Эратосфена с сегментацией страниц, которая потребовала бы месяц или два на современном настольном компьютере высокого класса, но его можно масштабировать для работы на сотнях тысяч ядер суперкомпьютера для работы за секунды.   -  person GordonBGood    schedule 16.11.2015


Ответы (24)


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

Enumerable.Range(0, Math.Floor(2.52*Math.Sqrt(num)/Math.Log(num))).Aggregate(
    Enumerable.Range(2, num-1).ToList(), 
    (result, index) => { 
        var bp = result[index]; var sqr = bp * bp;
        result.RemoveAll(i => i >= sqr && i % bp == 0); 
        return result; 
    }
);

Используемая здесь формула аппроксимации числа простых чисел: π(x) < 1.26 x / ln(x) . Нам нужно только проверить простые числа не больше x = sqrt(num).

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

person SLaks    schedule 02.10.2009
comment
Почему это было отклонено? Он отвечает на вопрос (как я могу это улучшить?) - person SLaks; 02.10.2009
comment
Похоже, у ОП есть конкретное домашнее задание. Если он представит ваше решение, инструктор сочтет его обманом. - person Jon B; 02.10.2009
comment
Да, удивительно, что этот принцип был впервые описан более 2000 лет назад. - person UpTheCreek; 02.10.2009
comment
(fyi - я не тот, кто тебя -1) - person Jon B; 02.10.2009
comment
И инструктор будет прав. Использование любого другого ответа также можно назвать обманом. Однако он все же дает ответ на вопрос. - person SLaks; 02.10.2009
comment
Думаю, я мог случайно проголосовать против - на этой странице было много активности. - person Cade Roux; 02.10.2009
comment
Я удалил голосование "против", но теперь оно не позволяет мне проголосовать "за" даже после повторного изменения ответа ... - person Cade Roux; 02.10.2009
comment
Ответ был всегда, он не занимается крупным исследовательским проектом. - person whatnick; 02.10.2009
comment
Технически это возвращает больше ответов, чем число. - person Cameron MacFarland; 02.10.2009
comment
И возвращает не простые числа, кроме числа. - person Cameron MacFarland; 02.10.2009
comment
Чем это отличается от OP google-ing или живого вопроса и получения ответа. Нет причин голосовать против - отличный способ решить проблему, потому что вы счастливы! - person Abhijeet Patel; 02.10.2009
comment
Оно сломано. Например, если n = 3, возвращается 49, что не является простым числом. - person Cameron MacFarland; 03.10.2009
comment
Разве это не близко к квадратичной сложности? Эратосфен - это другой алгоритм, и он намного быстрее. - person Accipitridae; 07.10.2009
comment
Как использовать этот фрагмент кода. Кто-нибудь может дать полный код программы. - person Vinod; 17.05.2011
comment
Ни в коем случае решето Эратосфена не вычисляет остатки для проверки и удаления номеров кандидатов. Это просто не так. - person Will Ness; 19.04.2012
comment
Отличный ответ, но, к сожалению, не совсем правильный. Первый диапазон должен быть от 2 до num и, аналогично, второй диапазон должен быть от 2 до num, а строка RemoveAll должна быть result.RemoveAll(i => i > index && i % index == 0); - person N_A; 23.08.2012
comment
1) ЭТО не SoE, а скорее ограниченная версия Решета Дэвида Тернера (1975) согласно (эта ссылка) [en.literateprograms.org/Sieve_of_Eratosthenes_ (Haskell)], поскольку он отсеивает (не оптимизированный) Пробным отделом, а не простым добавлением смещений на простые числа в соответствии с истинным SoE. 2) Что касается Тернера, это не ограничивает тесты только найденными простыми числами, меньшими квадратного корня из просеянного верхнего числа, и не начинается с квадрата каждого найденного простого числа. Эти оптимизации могут быть добавлены, но программа также сложнее, чем необходимо для использования двух диапазонов и метода агрегирования. - person GordonBGood; 06.12.2013
comment
@mydogisbox Я думаю, вы упустили суть того, чего пытался достичь этот код: result отбирается на каждом шаге; используя result[index], с index = 0,1,2,3..., последовательно проверяет и отбраковывает список result на 2,3,5,7,11, .... Вы правы, что диапазоны, кажется, неправильные. - person Will Ness; 30.12.2013
comment
@WillNess Ах, да, ты прав. Как-то пропустил, что он работал с отфильтрованным списком. - person N_A; 30.12.2013
comment
@mydogisbox или, скорее, он фильтрует список при переходе от одного исходного элемента к другому. Что вы думаете об этом предлагаемом изменении? Это нормально? - person Will Ness; 30.12.2013
comment
@WillNess. Первый диапазон все еще неверен? 0 и 1 не простые числа. - person N_A; 30.12.2013
comment
@WillNess: Кроме того, он по-прежнему каждый раз перебирает весь список. RemoveAll() не может сделать это оптимально. - person SLaks; 30.12.2013
comment
@SLaks прав, но количество запусков RemoveAll будет значительно уменьшено, почти оптимально. На самом деле это должен быть PrimePi [sqrt (num)]. Не могли бы вы отредактировать, чтобы я мог отказаться от голоса? (и, конечно же, для num = 10 ^ 15 он никогда не закончится, но это помимо сути ...) Или мне (отредактировать)? ... - person Will Ness; 30.12.2013
comment
@mydogisbox нет, первый диапазон используется как индексы в result. Списки начинаются с индекса 0. - person Will Ness; 30.12.2013
comment
@WillNess А, вот что я получаю за то, что не трачу достаточно времени на понимание того, что он делает. - person N_A; 30.12.2013
comment
@SLaks Кстати, что он каждый раз начинается с самого начала, не меняет сложности, потому что префикс имеет порядок ~ Sqrt от общей длины - с моим предложенным редактированием, то есть. Тогда можно было бы сказать, что это почти оптимальное сито для пробного деления. Наиболее важно ограничить количество итераций от num до PrimePi[Sqrt[num]] . - person Will Ness; 30.12.2013
comment
@WillNess: Я изначально написал этот код, чтобы найти первые N простых чисел, а не простые числа ниже N. - person SLaks; 30.12.2013
comment
@WillNess: Я не говорю, что вам не следует редактировать; Я объяснял исходный код. Не стесняйтесь редактировать это. - person SLaks; 30.12.2013
comment
@WillNess, производительность пробного деления почти оптимальна, если i ›= sqr, а не i› i, и i% bp вместо i% result [index], где var bp = result [index]; var sqr = bp * bp; определены перед оператором results.RemoveAll (), и в этом случае он будет более чем в два раза быстрее. Нет необходимости проверять меньшие числа, поскольку они уже очищены базовыми простыми числами с меньшим значением. Помимо производительности Оптимизированного пробного отдела, ограничение по сравнению с кодом вопроса - это объем памяти, используемый для Списка, поэтому он ограничен диапазонами примерно до 500000000. - person GordonBGood; 01.01.2014
comment
@GordonBGood был это неправильно? ... Вы видите то же самое коэффициент ускорения для нескольких значений num или он увеличивается? - person Will Ness; 01.01.2014
comment
@WillNess, это не так, просто немного медленнее, чем должно быть. Как вы написали для моей машины, время составляет 0,012, 0,179 и 3,6 секунды для до 100000, до одного миллиона и до 10 миллионов соответственно, где они составляют 0,008, 0,109 и 2,116 секунды с модификацией. . Ускорение в основном связано с доступом к списку / массиву один раз, а не дважды во внутреннем цикле, поскольку DotNet практически не оптимизирует лямбда-выражения. - person GordonBGood; 01.01.2014
comment
@GordonBGood эмпирически, есть очень небольшой штраф в сложности за начало с самого начала, а не с квадраты, ~ N ^ 0,016 (на низких диапазонах, как здесь). Но постоянный коэффициент примерно 1,2 ... 1,25 (с учетом делений); это до эффекта вспомогательных переменных. Ваши данные о времени выполнения более или менее согласуются. Итак, удачный улов! :) - person Will Ness; 02.01.2014
comment
Спасибо за быстрый и простой генератор простых чисел! Я использовал ваш код в этой ветке SO для визуализации простых чисел. - person Idle_Mind; 08.08.2015

Попробуй это:

void prime_num(long num)
{

    // bool isPrime = true;
    for (long i = 0; i <= num; i++)
    {
        bool isPrime = true; // Move initialization to here
        for (long j = 2; j < i; j++) // you actually only need to check up to sqrt(i)
        {
            if (i % j == 0) // you don't need the first condition
            {
                isPrime = false;
                break;
            }
        }
        if (isPrime)
        {
            Console.WriteLine ( "Prime:" + i );
        }
        // isPrime = true;
    }
}
person Cade Roux    schedule 02.10.2009
comment
+1 правильное (почти) исправление проблемы: не было вывода из-за использования верхнего предела num во внутреннем цикле; этот ответ меняет его на неэффективный, но не безумный, i. Очевидно, цель программы - просто напечатать постоянный поток простых чисел, а не печатать их все ... И исходный код не делал этого. 'не печатаю any из-за безумного теста 1%2, 1%3, ... 1%(10^15-1), который, конечно же, был ненулевым, поэтому в конечном итоге он выдаст 1 как простое число. Еще одна вещь: этот код здесь сообщает 0 и 1 как простые числа. :) i тоже должно начинаться с 2. - person Will Ness; 19.04.2012
comment
@WillNess, как вы говорите, была проблема с кодом вопроса, заключающаяся в том, что он выполнял безумное количество нелепых проверок простых чисел, настоящая причина того, что код вопроса не выдал никакого вывода вообще, заключается в на самом деле смешение переменных предела проверки дальнего диапазона и переменных целочисленного цикла (автоматически расширяется до значения long для сравнения), причина которого точно такая же, как и у спрашивающего: внутренний цикл проверки никогда не завершается, потому что диапазон переменной цикла меньше проверяемого диапазона , таким образом, никаких выходных данных не производится. - person GordonBGood; 30.12.2013
comment
@GordonBGood 2 ^ 32 ‹10 ^ 10; если бы диапазон INT был циклическим, j в конечном итоге достигнет 0 и вызовет div на ноль в 1%j. Ядро i7 - это чип на 100 гигафлопс, 100 * 10 ^ 9, так что это должно было произойти менее чем за секунду. Может быть, в C # ints 64-битные? Затем произойдет запуск до 10 ^ 15, что займет ~ 20 минут ... 2 часа для i=1. - person Will Ness; 30.12.2013
comment
@WillNess, ну, сама операция деления занимает около 50 тактов, поэтому время на цикл до деления на нулевое состояние будет составлять несколько десятков секунд; однако на самом деле он никогда не достигает нуля, поскольку каждое протестированное число имеет нулевой модуль при проверке на -1, таким образом, цикл вращается вечно, никогда не находя никаких простых чисел (все они делятся на -1, что занимает 10 секунд на каждую). пробный). - person GordonBGood; 31.12.2013
comment
@GordonBGood ага, -1, хороший улов! :) - person Will Ness; 31.12.2013
comment
@WillNess, я не знаю, видели ли вы это, но я также отправил ответ на старый вопрос как здесь о многопоточности, которая представляет собой максимально оптимизированную версию SoE для C #, которая использует все принципы, которые вы так часто поддерживаете: page сегментация, двухэтапное производство простых чисел, разложение на множители (экстремальное) и даже добавление множественной обработки для каждой страницы сегмента. Это немного долго, но оно того стоит, поскольку на самом деле можно обработать этот огромный диапазон чисел за несколько месяцев на i7 (с интервалом 30 дней). - person GordonBGood; 31.12.2013
comment
@GordonBGood wow, C # всего в несколько раз медленнее, чем primesieve, это достижение! Хотя C # - не мой язык, но будет интересно прочитать ваш код. По поводу всех оптимизаций я всего лишь передатчик. :) Несколько лет назад я нашел исправление отсрочки для сита Тернера, как, я уверен / знаю, сделали (многие) другие, так что это вызвало некоторый интерес. Ваше здоровье! :) - person Will Ness; 31.12.2013
comment
@WillNess, хотя в коде C # используются некоторые функциональные формы кода, я еще не преобразовал алгоритм в полностью функциональную форму F # (полностью функциональную, кроме операций отсечения составного представления чисел изменяемого массива), которая будет немного медленнее, чем C # из-за нескольких сложенных операций вызова / возврата, которых я не смогу избежать. Было бы интересно реализовать на других функциональных языках, таких как Haskell, хотя я не знаю, как определять и использовать изменяемые массивы в Haskell. - person GordonBGood; 01.01.2014

Вам нужно только проверить нечетные делители до квадратного корня из числа. Другими словами, ваш внутренний цикл должен запуститься:

for (int j = 3; j <= Math.Sqrt(i); j+=2) { ... }

Вы также можете выйти из функции, как только обнаружите, что число не является простым, вам не нужно больше проверять делители (я вижу, вы уже это делаете!).

Это будет работать, только если num больше двух.

Без Sqrt

Вы можете вообще избежать Sqrt, сохранив текущую сумму. Например:

int square_sum=1;
for (int j=3; square_sum<i; square_sum+=4*(j++-1)) {...}

Это потому, что сумма чисел 1+ (3 + 5) + (7 + 9) даст вам последовательность нечетных квадратов (1,9,25 и т. Д.). Следовательно, j представляет собой квадратный корень из square_sum. Пока square_sum меньше i, j меньше квадратного корня.

person Guillermo Phillips    schedule 02.10.2009
comment
@GuillermoPhillips, если бы кто-то хотел использовать параметр square_sum, правильный и более эффективный цикл (только деление на нечетные числа; также обратите внимание, что проверка до квадратного корня включительно) будет для (int j = 3, square_sum = 9; square_sum ‹= i; square_sum + = 4 ((j + = 2) -1)) {...} - person GordonBGood; 30.12.2013
comment
@GuillermoPhillips, я не думаю, что идея square_sum действительно так хорошо работает на практике, поскольку, хотя для вычисления (int) Math.Sqrt ((double) i) требуется около 24 часов процессора, это можно сделать только один раз для каждое значение 'i', тогда как вычисление square_sum требует около одной тактовой частоты ЦП на цикл 'j'; это означает, что вычисление квадратного корня на самом деле занимает меньше времени, чем текущее вычисление square_sum, когда 'i' достигает значения тысячи или около того. - person GordonBGood; 31.12.2013

Люди упомянули пару строительных блоков, которые помогут сделать это эффективно, но на самом деле никто не сложил их вместе. сито Эратосфена - хорошее начало, но с ним у вас закончится память < em> long, прежде чем вы достигнете установленного лимита. Это не значит, что это бесполезно - когда вы выполняете свой цикл, вам действительно важны простые делители. Таким образом, вы можете начать с использования сита для создания базы простых делителей, а затем использовать их в цикле для проверки чисел на первичность.

Однако, когда вы пишете цикл, вы действительно НЕ хотите использовать sqrt (i) в условии цикла, как было предложено в нескольких ответах. Мы с вами знаем, что sqrt - это «чистая» функция, которая всегда дает один и тот же ответ, если задан один и тот же входной параметр. К сожалению, компилятор НЕ знает этого, поэтому, если использовать что-то вроде '‹= Math.sqrt (x)' в условии цикла, он будет повторно вычислять sqrt числа на каждой итерации цикла.

Вы можете избежать этого несколькими способами. Вы можете либо предварительно вычислить sqrt перед циклом и использовать предварительно вычисленное значение в условии цикла, либо вы можете работать в другом направлении и изменить i<Math.sqrt(x) на i*i<x. Лично я бы заранее вычислил квадратный корень - я думаю, что это яснее и, вероятно, немного быстрее, - но это зависит от количества итераций цикла (i*i означает, что он все еще выполняет умножение в цикле). Всего за несколько итераций i*i обычно будет быстрее. При достаточном количестве итераций потери от i*i каждой итерации перевешивают время выполнения sqrt один раз вне цикла.

Вероятно, этого достаточно для размера чисел, с которыми вы имеете дело - ограничение в 15 цифр означает, что квадратный корень составляет 7 или 8 цифр, что умещается в довольно разумном объеме памяти. С другой стороны, если вы хотите часто иметь дело с числами в этом диапазоне, вы можете изучить некоторые из более сложных алгоритмов проверки простых чисел, такие как Алгоритмы Полларда или Брента. Они более сложные (мягко говоря), но на много быстрее для больших чисел.

Существуют и другие алгоритмы для еще больших чисел (квадратное сито, сито общего числового поля), но мы не будем сейчас вдаваться в них - они намного сложнее, и на самом деле полезен только для работы с действительно большими числами (GNFS начинает использоваться в диапазоне более 100 цифр).

person Jerry Coffin    schedule 02.10.2009
comment
+1 Интересное резюме основных алгоритмов, сделанное мной на некоторое время википедией и вольфрамом. Хотелось бы, чтобы этот пост был отредактирован, чтобы включить ссылки. - person whatnick; 03.10.2009
comment
Вы не совсем правы, так как НЕ хотите использовать sqrt (i) в условии цикла, поскольку есть способы сделать это понятным как для компилятора, так и для считывателя кода, вычислив предел один раз за пределами фактической проверки диапазона цикла как я сделал в моем ответе. - person GordonBGood; 30.12.2013

Первый шаг: напишите метод расширения, чтобы узнать, является ли ввод простым.

public static bool isPrime(this int number ) {

    for (int i = 2; i < number; i++) { 
        if (number % i == 0) { 
            return false; 
        } 
    }

    return true;   
}

2 шаг: напишите метод, который будет печатать все простые числа от 0 до введенного числа.

public static void getAllPrimes(int number)
{
    for (int i = 0; i < number; i++)
    {
        if (i.isPrime()) Console.WriteLine(i);
    }
}
person Bamara Coulibaly    schedule 17.04.2012
comment
ваш isPrime вернет true для 0, 1 и для любого отрицательного числа. Таким образом, цикл внутри getAllPrimes должен начинаться как минимум с int i = 2;. - person Will Ness; 19.04.2012
comment
нет, нет, return true; должен быть следующим оператором после оператора for, как сейчас (просто отступ текста неправильный). но функция isPrime, как написано, предполагает 2 <= number. В других случаях это не сработает. Поэтому, где бы вы ни использовали его, убедитесь, что вы проверяете с ним число больше 1. Это также разумный выбор, поскольку ни одно число меньше 2 не является простым, поэтому необходимо проверить эти числа. Это означает, что просто измените начальное значение для i в цикле getAllPrimes, чтобы начать не с 0, а с 2. В противном случае ваша программа покажет 0 и 1 как простые числа. - person Will Ness; 04.05.2012
comment
опечатка: поэтому нет нужно проверять эти числа (до 2) на простоту. - person Will Ness; 04.05.2012
comment
Вам нужно только проверить квадратный корень из number в isPrime. - person Wai Ha Lee; 26.01.2016
comment
вы можете запустить цикл до i ‹= number / 2 в функции isPrime (). Потому что в случае number = 19 ваш цикл for будет повторяться до i = 18. - person Naphstor; 22.05.2016

EDIT_ADD: Если Уилл Несс прав, цель вопроса состоит в том, чтобы просто выводить непрерывный поток простых чисел до тех пор, пока программа работает (нажатие Pause / Break для паузы и любой клавиши для повторного запуска) с нет серьезных надежд на достижение этого верхнего предела, тогда код должен быть написан без аргумента верхнего предела и проверки диапазона на «истина» для первого цикла for. С другой стороны, если в вопросе нужно было напечатать простые числа до предела, то следующий код будет выполнять работу намного более эффективно, используя пробное деление только для нечетных чисел, с тем преимуществом, что он вообще не использует память (его также можно преобразовать в непрерывный цикл, как указано выше):

static void primesttt(ulong top_number) {
  Console.WriteLine("Prime:  2");
  for (var i = 3UL; i <= top_number; i += 2) {
    var isPrime = true;
    for (uint j = 3u, lim = (uint)Math.Sqrt((double)i); j <= lim; j += 2) {
      if (i % j == 0)  {
        isPrime = false;
        break;
      }
    }
    if (isPrime) Console.WriteLine("Prime:  {0} ", i);
  }
}

Во-первых, код вопроса не выводит данные, потому что его переменные цикла являются целыми числами, а протестированный предел - огромным длинным целым числом, что означает, что цикл не может достичь предела, создавая внутренний цикл EDITED: посредством чего переменная 'j' возвращается к отрицательным числам; когда переменная 'j' возвращается к -1, проверяемое число не проходит проверку на простоту, потому что все числа делятся на -1 END_EDIT без остатка. Даже если бы это было исправлено, код вопроса выдает очень медленный вывод, потому что он связан с 64-битным делением очень большого количества составных чисел (все четные числа плюс нечетные составные числа) на весь диапазон чисел до этой вершины. число десять в шестнадцатой степени для каждого возможного простого числа. Приведенный выше код работает, потому что он ограничивает вычисления только нечетными числами и делает только деления по модулю вплоть до квадратного корня из текущего проверяемого числа.

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

Хотя однострочный (своего рода) ответ от @SLaks с использованием Linq работает, на самом деле это не Решето Эратосфена, поскольку это просто неоптимизированная версия Trial Division, неоптимизированный тем, что не исключает нечетные простые числа, не начинается с квадрата найденного базового простого числа и не прекращает отбраковку для базовых простых чисел большего размера чем квадратный корень из верхнего числа, чтобы просеять. Это также довольно медленно из-за множественных вложенных операций перечисления.

На самом деле это злоупотребление методом Linq Aggregate и неэффективное использование первого из двух сгенерированных Linq Range. Он может стать оптимизированным пробным отделом с меньшими накладными расходами на перечисление следующим образом:

static IEnumerable<int> primes(uint top_number) {
  var cullbf = Enumerable.Range(2, (int)top_number).ToList();
  for (int i = 0; i < cullbf.Count; i++) {
    var bp = cullbf[i]; var sqr = bp * bp; if (sqr > top_number) break;
    cullbf.RemoveAll(c => c >= sqr && c % bp == 0);
  } return cullbf; }

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

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

static IEnumerable<uint> primes(uint top_number) {
  if (top_number < 2u) yield break;
  yield return 2u; if (top_number < 3u) yield break;
  var BFLMT = (top_number - 3u) / 2u;
  var SQRTLMT = ((uint)(Math.Sqrt((double)top_number)) - 3u) / 2u;
  var buf = new BitArray((int)BFLMT + 1,true);
  for (var i = 0u; i <= BFLMT; ++i) if (buf[(int)i]) {
      var p = 3u + i + i; if (i <= SQRTLMT) {
        for (var j = (p * p - 3u) / 2u; j <= BFLMT; j += p)
          buf[(int)j] = false; } yield return p; } }

Приведенный выше код вычисляет диапазон всех простых чисел до десяти миллионов примерно за 77 миллисекунд на Intel i7-2700K (3,5 ГГц).

Любой из двух статических методов может быть вызван и протестирован с помощью операторов using и статического метода Main следующим образом:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

static void Main(string[] args) {
  Console.WriteLine("This program generates prime sequences.\r\n");

  var n = 10000000u;

  var elpsd = -DateTime.Now.Ticks;

  var count = 0; var lastp = 0u;
  foreach (var p in primes(n)) { if (p > n) break; ++count; lastp = (uint)p; }

  elpsd += DateTime.Now.Ticks;
  Console.WriteLine(
    "{0} primes found <= {1}; the last one is {2} in {3} milliseconds.",
    count, n, lastp,elpsd / 10000);

  Console.Write("\r\nPress any key to exit:");
  Console.ReadKey(true);
  Console.WriteLine();
}

который покажет количество простых чисел в последовательности до предела, последнее найденное простое число и время, затраченное на их перечисление.

EDIT_ADD: однако, чтобы произвести перечисление числа простых чисел меньше десяти тысяч триллионов (десять в шестнадцатой степени), как задается вопрос, требуется сегментированный постраничный подход с использованием многоядерной обработки. но даже с C ++ и очень оптимизированным PrimeSieve для этого потребуется около 400 часов, чтобы просто произвести количество найденных простых чисел, и в десятки раз больше, чтобы перечислить их все, чтобы в течение года сделать то, что задает вопрос. Чтобы сделать это с использованием неоптимизированного алгоритма пробного деления, потребуются суперэоны и очень-очень много времени даже при использовании оптимизированного алгоритма пробного деления, например, от десяти до двух миллионов лет (это два миллиона нулевых лет !! !).

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

Решения, которые я публикую здесь, тоже не помогут, так как даже последнее Сито Эратосфена потребует около 640 Терабайт памяти для этого диапазона.

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

person GordonBGood    schedule 06.12.2013

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

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

Вам нужно научиться разделять свой код на части. Это не то, что вам всегда нужно делать в проекте, но хорошо знать, как это делать.

Ваш метод prime_num (long num) мог бы иметь лучшее, более информативное имя. И если предполагается, что все простые числа меньше заданного числа, он должен вернуть их в виде списка. Это упрощает разделение вашего дисплея и ваших функций.

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

Поэтому моя лучшая рекомендация - сделать что-то вроде этого:

public void main(string args[])
{
    //Get the number you want to use as input
    long x = number;//'number' can be hard coded or retrieved from ReadLine() or from the given arguments

    IList<long> primes = FindSmallerPrimes(number);

    DisplayPrimes(primes);
}

public IList<long> FindSmallerPrimes(long largestNumber)
{
    List<long> returnList = new List<long>();
    //Find the primes, using a method as described by another answer, add them to returnList
    return returnList;
}

public void DisplayPrimes(IList<long> primes)
{
    foreach(long l in primes)
    {
        Console.WriteLine ( "Prime:" + l.ToString() );
    }
}

Даже если вы в конечном итоге окажетесь там, где такая работа не нужна, хорошо знать, как это сделать.

person Jeff    schedule 02.10.2009
comment
Хотя другие люди ответили на этот вопрос, я считаю ваш ответ весьма полезным для ОП в том смысле, что он немного учит его разделению задач в программировании. +1 - person Hallaghan; 25.01.2011

Пахнет домашней работой. В моем очень-очень старом графическом калькуляторе была такая простая программа. Технически цикл проверки внутреннего устройства должен выполняться только до i ^ (1/2). Вам нужно найти «все» простые числа от 0 до L? Другая серьезная проблема заключается в том, что ваши переменные цикла имеют значение «int», в то время как ваши входные данные - «длинные», это вызовет переполнение, из-за которого ваши циклы не выполнятся ни разу. Исправьте переменные цикла.

person whatnick    schedule 02.10.2009
comment
Фактически, смешение длинных предельных переменных и целочисленных переменных цикла приведет именно к тому, что испытал вопрошающий: переменная цикла (автоматически расширенная до long для сравнения) меньше диапазона, проверяемого, как обозначено длинной переменной. Это заставляет внутренний цикл 'j' проверять все числа до int.MaxValue (два миллиарда плюс), затем переходить к началу с int.MinValue (отрицательные два миллиарда минус), и когда эта переменная цикла 'j' возвращается до -1 , цикл прервется, потому что все числа без остатка делятся на -1, поэтому классифицируются как непростые и результаты не выводятся. - person GordonBGood; 31.12.2013

Однострочный код на C #: -

Console.WriteLine(String.Join(Environment.NewLine, 
    Enumerable.Range(2, 300)
        .Where(n => Enumerable.Range(2, (int)Math.Sqrt(n) - 1)
        .All(nn => n % nn != 0)).ToArray()));                                    
person Shivakant Upadhyay    schedule 17.11.2012

Приведенный выше ответ Сито Эратосфена не совсем правильный. Как написано, он найдет все простые числа от 1 до 1000000. Чтобы найти все простые числа от 1 до num, используйте:

private static IEnumerable Primes01(int num)
{
    return Enumerable.Range(1, Convert.ToInt32(Math.Floor(Math.Sqrt(num))))
        .Aggregate(Enumerable.Range(1, num).ToList(),
        (result, index) =>
            {
                result.RemoveAll(i => i > result[index] && i%result[index] == 0);
                return result;
            }
        );
}

Начальное значение Aggregate должно находиться в диапазоне от 1 до num, поскольку этот список будет содержать окончательный список простых чисел. Enumerable.Range(1, Convert.ToInt32(Math.Floor(Math.Sqrt(num)))) - это количество раз, когда семя очищается.

person darren    schedule 19.12.2012

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

Алгоритм на странице также использует несколько сокращений (нечетные числа и только проверка до квадратного корня), что делает его чрезвычайно эффективным и позволяет вычислять длинные числа.

person Joe Meyer    schedule 23.07.2012

так что это в основном всего две опечатки, одна, самая неудачная, for (int j = 2; j <= num; j++), которая является причиной непродуктивного тестирования 1%2,1%3 ... 1%(10^15-1), которое длится очень долго, поэтому OP не получил " любой вывод ». Вместо этого должно было быть j < i;. Другой, второстепенный по сравнению с этим вопрос, i должен начинаться с 2, а не с 0:

for( i=2; i <= num; i++ )
{
    for( j=2; j < i; j++ ) // j <= sqrt(i) is really enough
....

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

Что здесь может сработать, так это оптимизированное пробное деление, которое сохранит простые числа по мере их нахождения и проверяет их с простыми числами, а не только со всеми числами ниже кандидата.

Вторая альтернатива, более сложная (т.е. намного более быстрая), - это использование сегментированного сита Эратосфена . Это происходит постепенно и неограниченно.

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

person Will Ness    schedule 18.04.2012

Откровенно говоря, некоторые из предлагаемых решений действительно медленные и поэтому являются плохими предложениями. Для проверки того, что одно число является простым, вам понадобится оператор деления / по модулю, но для вычисления диапазона вам не нужно.

По сути, вы просто исключаете числа, кратные ранее найденным простым числам, поскольку они (по определению) сами по себе не являются простыми числами.

Я не буду приводить полную реализацию, поскольку это было бы несложно, это подход в псевдокоде. (На моей машине фактическая реализация вычисляет все простые числа в Sytem.Int32 (2 миллиарда) в течение 8 секунд.

public IEnumerable<long> GetPrimes(long max)
{
    // we safe the result set in an array of bytes.
    var buffer = new byte[long >> 4];
    // 1 is not a prime.
    buffer[0] = 1;

    var iMax = (long)Math.Sqrt(max);

    for(long i = 3; i <= iMax; i +=2 )
    {
        // find the index in the buffer
        var index = i >> 4;
        // find the bit of the buffer.
        var bit = (i >> 1) & 7;

        // A not set bit means: prime
        if((buffer[index] & (1 << bit)) == 0)
        {
            var step = i << 2;
            while(step < max)
            {
                // find position in the buffer to write bits that represent number that are not prime.
            }
        }
        // 2 is not in the buffer.
        yield return 2;

        // loop through buffer and yield return odd primes too.
    }
}

Решение требует хорошего понимания побитовых операций. Но это намного быстрее. Вы также можете сохранить результат на диске, если он вам понадобится для дальнейшего использования. Результат из 17 * 10 ^ 9 чисел может быть сохранен с помощью 1 ГБ, и расчет этого набора результатов занимает максимум 2 минуты.

person Corniel Nobel    schedule 10.02.2015

Я знаю, что это довольно старый вопрос, но после прочтения здесь: Сито Эратосфена вики

Я написал это так, исходя из понимания алгоритма:

void SieveOfEratosthenes(int n)
{
    bool[] primes = new bool[n + 1];

    for (int i = 0; i < n; i++)
        primes[i] = true;

    for (int i = 2; i * i <= n; i++)
        if (primes[i])
            for (int j = i * 2; j <= n; j += i)
                primes[j] = false;

    for (int i = 2; i <= n; i++)
        if (primes[i]) Console.Write(i + " ");
}

В первом цикле мы заполняем массив логических значений значением true.

Второй цикл for начнется с 2, поскольку 1 не является простым числом, и проверит, не изменилось ли простое число, а затем присвоит false индексу j.

последний цикл, который мы просто печатаем, когда он прост.

person Community    schedule 11.05.2017

Очень похоже - из упражнения по реализации Решета Эратосфена на C #:

public class PrimeFinder
{
    readonly List<long> _primes = new List<long>();

    public PrimeFinder(long seed)
    {
        CalcPrimes(seed);
    }

    public List<long> Primes { get { return _primes; } }

    private void CalcPrimes(long maxValue)
    {
        for (int checkValue = 3; checkValue <= maxValue; checkValue += 2)
        {
            if (IsPrime(checkValue))
            {
                _primes.Add(checkValue);
            }
        }
    }

    private bool IsPrime(long checkValue)
    {
        bool isPrime = true;

        foreach (long prime in _primes)
        {
            if ((checkValue % prime) == 0 && prime <= Math.Sqrt(checkValue))
            {
                isPrime = false;
                break;
            }
        }
        return isPrime;
    }
}
person Community    schedule 02.10.2009

Prime Helper очень быстрый расчет

public static class PrimeHelper
{

    public static IEnumerable<Int32> FindPrimes(Int32 maxNumber)
    {
        return (new PrimesInt32(maxNumber));
    }

    public static IEnumerable<Int32> FindPrimes(Int32 minNumber, Int32 maxNumber)
    {
        return FindPrimes(maxNumber).Where(pn => pn >= minNumber);
    }

    public static bool IsPrime(this Int64 number)
    {
        if (number < 2)
            return false;
        else if (number < 4 )
            return true;

        var limit = (Int32)System.Math.Sqrt(number) + 1;
        var foundPrimes = new PrimesInt32(limit);

        return !foundPrimes.IsDivisible(number);
    }

    public static bool IsPrime(this Int32 number)
    {
        return IsPrime(Convert.ToInt64(number));
    }

    public static bool IsPrime(this Int16 number)
    {
        return IsPrime(Convert.ToInt64(number));
    }

    public static bool IsPrime(this byte number)
    {
        return IsPrime(Convert.ToInt64(number));
    }
}

public class PrimesInt32 : IEnumerable<Int32>
{
    private Int32 limit;
    private BitArray numbers;

    public PrimesInt32(Int32 limit)
    {
        if (limit < 2)
            throw new Exception("Prime numbers not found.");

        startTime = DateTime.Now;
        calculateTime = startTime - startTime;
        this.limit = limit;
        try { findPrimes(); } catch{/*Overflows or Out of Memory*/}

        calculateTime = DateTime.Now - startTime;
    }

    private void findPrimes()
    {
        /*
        The Sieve Algorithm
        http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
        */
        numbers = new BitArray(limit, true);
        for (Int32 i = 2; i < limit; i++)
            if (numbers[i])
                for (Int32 j = i * 2; j < limit; j += i)
                     numbers[j] = false;
    }

    public IEnumerator<Int32> GetEnumerator()
    {
        for (Int32 i = 2; i < 3; i++)
            if (numbers[i])
                yield return i;
        if (limit > 2)
            for (Int32 i = 3; i < limit; i += 2)
                if (numbers[i])
                    yield return i;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    // Extended for Int64
    public bool IsDivisible(Int64 number)
    {
        var sqrt = System.Math.Sqrt(number);
        foreach (var prime in this)
        {
            if (prime > sqrt)
                break;
            if (number % prime == 0)
            {
                DivisibleBy = prime;
                return true;
            }
        }
        return false;
    }

    private static DateTime startTime;
    private static TimeSpan calculateTime;
    public static TimeSpan CalculateTime { get { return calculateTime; } }
    public Int32 DivisibleBy { get; set; }
}
person Community    schedule 30.08.2011

U может использовать обычное понятие простого числа, нужно только два множителя (один и сам). Так делай вот так, легкий способ

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PrimeNUmber
{
    class Program
    {
        static void FindPrimeNumber(long num)
        {
            for (long i = 1; i <= num; i++)
            {
                int totalFactors = 0;
                for (int j = 1; j <= i; j++)
                {
                    if (i % j == 0)
                    {
                        totalFactors = totalFactors + 1;
                    }
                }
                if (totalFactors == 2)
                {
                    Console.WriteLine(i);
                }
            }
        }

        static void Main(string[] args)
        {
            long num;
            Console.WriteLine("Enter any value");
            num = Convert.ToInt64(Console.ReadLine());
            FindPrimeNumber(num);
            Console.ReadLine();
        }
    }
}
person user1134478    schedule 29.03.2013

Это решение отображает все простые числа от 0 до 100.

        int counter = 0;
        for (int c = 0; c <= 100; c++)
        {
            counter = 0;
            for (int i = 1; i <= c; i++)
            {
                if (c % i == 0)
                { counter++; }
            }
            if (counter == 2)
            { Console.Write(c + " "); }
        }
person Arthur    schedule 21.01.2016
comment
отображает все простые числа от 0 до 100 - person Arthur; 21.01.2016

Это самый быстрый способ вычисления простых чисел в C #.

   void PrimeNumber(long number)
    {
        bool IsprimeNumber = true;
        long  value = Convert.ToInt32(Math.Sqrt(number));
        if (number % 2 == 0)
        {
            IsprimeNumber = false;
        }
        for (long i = 3; i <= value; i=i+2)
        {             
           if (number % i == 0)
            {

               // MessageBox.Show("It is divisible by" + i);
                IsprimeNumber = false;
                break;
            }

        }
        if (IsprimeNumber)
        {
            MessageBox.Show("Yes Prime Number");
        }
        else
        {
            MessageBox.Show("No It is not a Prime NUmber");
        }
    }
person Raj    schedule 24.07.2015
comment
Самый быстрый способ вычисления простых чисел в C # - person Raj; 24.07.2015
comment
Пожалуйста, вставьте текст или комментарии в ответ, чтобы объяснить свой ответ. Добавление простого кода не поможет тому, кто задал вопрос. По крайней мере, объясните свою логику - person Abhinav Singh Maurya; 24.07.2015
comment
Тоже не самый быстрый способ. Взглянув на него, если я позвоню PrimeNumber по четному номеру, например PrimeNumber(1000000000000000000), он вычислит квадратный корень и выполнит цикл, даже если сразу знает, что это не простое число! (nb 1000000000000000000 меньше, чем Int64.MaxValue). Затем он запускает цикл по нечетным числам от 3 до квадратного корня, 1000000000, что очень неэффективно и бессмысленно, поскольку мы уже знаем, что это не простое число. - person Wai Ha Lee; 26.01.2016
comment
Кстати, кто-то пометил ваш вопрос, потому что посчитал, что он низкого качества. Я хорошо выгляжу, потому что это была попытка ответить на вопрос, но это не значит, что это хороший ответ. - person Wai Ha Lee; 26.01.2016

Есть несколько очень оптимальных способов реализации алгоритма. Но если вы мало разбираетесь в математике и просто следуете определению простого числа как требованию: число, которое делится только на 1 и само по себе (и ни на что другое), вот простой для понимания код для положительных чисел.

public bool IsPrime(int candidateNumber)
{
    int fromNumber = 2;
    int toNumber = candidateNumber - 1;

    while(fromNumber <= toNumber)
    {
        bool isDivisible = candidateNumber % fromNumber == 0;
        if (isDivisible)
        {
            return false;
        }

        fromNumber++;
    }
    return true;
}

Поскольку каждое число делится на 1 и само по себе, мы начинаем проверку с 2 и далее до числа, расположенного непосредственно перед ним. Это основная аргументация.

person diegosasw    schedule 28.02.2019

Вы также можете сделать это:

class Program
  {
    static void Main(string[] args)
    {
      long numberToTest = 350124;
      bool isPrime = NumberIsPrime(numberToTest);
      Console.WriteLine(string.Format("Number {0} is prime? {1}", numberToTest, isPrime));
      Console.ReadLine();
    }

    private static bool NumberIsPrime(long n)
    {
      bool retVal = true;

      if (n <= 3)
      {
        retVal = n > 1;
      } else if (n % 2 == 0 || n % 3 == 0)
      {
        retVal = false;
      }

      int i = 5;

      while (i * i <= n)
      {
        if (n % i == 0 || n % (i + 2) == 0)
        {
          retVal = false;
        }
        i += 6;
      }

      return retVal;
    }
  }
person JoKeRxbLaCk    schedule 01.03.2019

Я сделал более простой подход: я проверил, имеет ли число ровно два коэффициента деления, что является сутью простых чисел.

    List<int> factorList = new List<int>();
    int[] numArray = new int[] { 1, 0, 6, 9, 7, 5, 3, 6, 0, 8, 1 };

    foreach (int item in numArray)
    {

        for (int x = 1; x <= item; x++)
        {
          //check for the remainder after dividing for each number less that number
            if (item % x == 0)
            {
                factorList.Add(x);
            }
        }
        if (factorList.Count == 2) // has only 2 division factors ; prime number
        {
            Console.WriteLine(item + " is a prime number ");
        }
        else
        {Console.WriteLine(item + " is not a prime number ");}

        factorList = new List<int>(); // reinitialize list
    }
person Wesam    schedule 29.10.2019

Вот решение с модульным тестом:

Решение:

public class PrimeNumbersKata
    {
        public int CountPrimeNumbers(int n)
        {
            if (n < 0) throw new ArgumentException("Not valide numbre");
            if (n == 0 || n == 1) return 0;
            int cpt = 0;
            for (int i = 2; i <= n; i++)
            {
                if (IsPrimaire(i)) cpt++;
            }
            return cpt;
        }

        private bool IsPrimaire(int number)
        {

            for (int i = 2; i <= number / 2; i++)
            {
                if (number % i == 0) return false;
            }
            return true;
        }
    }

Тесты:

[TestFixture]
    class PrimeNumbersKataTest
    {
        private PrimeNumbersKata primeNumbersKata;
        [SetUp]
        public void Init()
        {
            primeNumbersKata = new PrimeNumbersKata();
        }
        [TestCase(1,0)]
        [TestCase(0,0)]
        [TestCase(2,1)]
        [TestCase(3,2)]
        [TestCase(5,3)]
        [TestCase(7,4)]
        [TestCase(9,4)]
        [TestCase(11,5)]
        [TestCase(13,6)]
        public void CountPrimeNumbers_N_AsArgument_returnCountPrimes(int n, int expected)
        {
            //arrange
            //act
            var actual = primeNumbersKata.CountPrimeNumbers(n);
            //assert
            Assert.AreEqual(expected,actual);
        }

        [Test]
        public void CountPrimairs_N_IsNegative_RaiseAnException()
        {
            var ex = Assert.Throws<ArgumentException>(()=> { primeNumbersKata.CountPrimeNumbers(-1); });
            //Assert.That(ex.Message == "Not valide numbre");
             Assert.That(ex.Message, Is.EqualTo("Not valide numbre"));

        }
    }
person ilyes    schedule 28.02.2020

в вузе надо было считать простые числа до 10 000 сделали так, преподаватель немного удивился, но тест я сдал. Lang c #

void Main()
{
    int number=1;
    for(long i=2;i<10000;i++)
    {
        if(PrimeTest(i))
        {
            Console.WriteLine(number+++" " +i);
        }
    }
}

List<long> KnownPrime = new List<long>();
private bool PrimeTest(long i)
{
    if (i == 1) return false;
    if (i == 2)
    {
        KnownPrime.Add(i);
        return true;
    }
    foreach(int k in KnownPrime)
    {
        if(i%k==0)
            return false;
    }
    KnownPrime.Add(i);
    return true;
}
person Сергей toff    schedule 30.01.2021