C - определя дали числото е просто

Опитвам се да измисля метод, който взема цяло число и връща булево, за да каже дали числото е просто или не, и не знам много C; някой би ли искал да ми даде някои насоки?

По принцип бих направил това в C# така:

static bool IsPrime(int number)
{
    for (int i = 2; i < number; i++)
    {
        if (number % i == 0 && i != number)
            return false;
    }
    return true;
}

person Jimmy    schedule 08.10.2009    source източник
comment
Това е по-скоро въпрос по математика, отколкото въпрос по програмиране, сигурно?   -  person bdonlan    schedule 08.10.2009
comment
Ето някои указатели: int *ptr; int *ptr2; int *ptr3. Съжалявам, не можах да помогна. Колко големи са числата, които ще проверявате? И също така, искате ли евристика или нещо, което винаги работи?   -  person AlbertoPL    schedule 08.10.2009
comment
Измислете своя алгоритъм (начина, по който го тествате без код) и тогава може би ще можем да ви помогнем да го изразите в C.   -  person BobbyShaftoe    schedule 08.10.2009
comment
Помислете за алгоритъм, опитайте се да го приложите и ако имате конкретен проблем с него, ще намерите някой, който да ви помогне. Не можеш да очакваш да напишеш на другите цялата си домашна работа. Както и да е, има лесен начин да намерите решение: просто използвайте функцията за търсене - ако търсите prime, ще намерите много добри подходи.   -  person arno    schedule 08.10.2009
comment
Какъв е смисълът от „i != число“, когато имате „i ‹ число“ като условие за изпълнение на цикъла?   -  person Matthieu M.    schedule 08.10.2009
comment
Разгледайте тази връзка, която съдържа обяснение как работят нещата. cprogramming.language- tutorial.com/2012/01/   -  person    schedule 03.03.2012
comment
Също така имайте предвид, че проверката на i < number е излишна. По дефиниция, ако число x = a * b, a или b е < int(sqrt(x)), а другото е по-голямо. Така че вашият цикъл трябва да стигне само до int(sqrt(x)).   -  person twalberg    schedule 17.05.2013


Отговори (11)


Добре, забравете за C. Да предположим, че ви дам число и ви помоля да определите дали е просто. Как го правиш? Запишете ясно стъпките, след което се погрижете да ги преведете в код.

След като определите алгоритъма, ще бъде много по-лесно за вас да разберете как да напишете програма и за другите да ви помогнат с нея.

редактиране: Ето C# кода, който публикувахте:

static bool IsPrime(int number) {
    for (int i = 2; i < number; i++) {
        if (number % i == 0 && i != number) return false;
    }
    return true;
}

Това е почти валиден C такъв, какъвто е; в C няма тип bool и няма true или false, така че трябва да го модифицирате малко (редактиране: Кристофър Джонсън правилно отбелязва, че C99 е добавил заглавката stdbool.h). Тъй като някои хора нямат достъп до C99 среда (но трябва да използвате такава!), нека направим тази много малка промяна:

int IsPrime(int number) {
    int i;
    for (i=2; i<number; i++) {
        if (number % i == 0 && i != number) return 0;
    }
    return 1;
}

Това е напълно валидна C програма, която прави това, което искате. Можем да го подобрим малко без много усилия. Първо, имайте предвид, че i винаги е по-малко от number, така че проверката, че i != number винаги е успешна; можем да се отървем от него.

Освен това всъщност не е нужно да опитвате делители до number - 1; можете да спрете проверката, когато достигнете sqrt(число). Тъй като sqrt е операция с плаваща запетая и това носи цяла купчина тънкости, ние всъщност няма да изчисляваме sqrt(number). Вместо това можем просто да проверим, че i*i <= number:

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

Едно последно нещо обаче; имаше малка грешка в оригиналния ви алгоритъм! Ако number е отрицателно, или нула, или едно, тази функция ще твърди, че числото е просто. Вероятно искате да се справите с това правилно и може да искате да направите number без знак, тъй като е по-вероятно да се интересувате само от положителните стойности:

int IsPrime(unsigned int number) {
    if (number <= 1) return 0; // zero and one are not prime
    unsigned int i;
    for (i=2; i*i<=number; i++) {
        if (number % i == 0) return 0;
    }
    return 1;
}

Това определено не е най-бързият начин да проверите дали числото е просто, но работи и е доста лесно. Едва ли трябваше изобщо да променяме кода ви!

person Stephen Canon    schedule 08.10.2009
comment
За информация, стандартът C99 дефинира заглавка ‹stdbool.h›, която предоставя bool, true и false. - person Kristopher Johnson; 08.10.2009
comment
също така забравих да спомена, че не е нужно да се тревожа за 0, 1 или отрицателни стойности, това просто преминава през списък от 50 числа, ако си спомням правилно, но благодаря отново - person Jimmy; 08.10.2009
comment
@Kristopher: благодаря, забравих за това. @Jimmy: радвам се да помогна, радвам се, че това беше само грешка в документацията =) - person Stephen Canon; 08.10.2009
comment
Можете също така да подобрите това (с почти 50%), като проверявате само нечетните числа (третирайте 0,1,2 като специални случаи) и започнете от 3 предварително с 2 всеки път. - person Tom; 08.10.2009
comment
Знам, че е по-лесно да се изчисли квадратен корен, отколкото квадратен корен, но изчисляването на квадратен корен на всяка итерация трябва да струва ПОВЕЧЕ от еднократното изчисляване на квадратния корен и да приключи с това :x - person Matthieu M.; 08.10.2009
comment
На модерна машина извън ред латентността на инструкцията mul за квадрат i трябва да бъде изцяло скрита в латентността на модула, така че няма да има осезаема печалба в производителността. На строго подредена машина може да се спечели с помощта на повдигнат квадратен корен, но това потенциално повдига проблеми с неточността на плаваща запетая, ако кодът е компилиран на платформа с голям int тип (64 бита или по-голям) . С всичко това може да се работи, но реших, че е най-добре нещата да са прости и тривиално преносими. В крайна сметка, ако ви е грижа за скоростта, вие изобщо не използвате този алгоритъм. - person Stephen Canon; 08.10.2009
comment
@Tom можеш да подобриш много повече, като спреш на пода(sqrt(номер)). Вземете 11, например, floor(sqrt(11)) = 3. Числото след 3 е 4, 3*4 = 12 › 11. Ако използвате просто сито, за да проверите за първичност, трябва да проверите само нечетното числа до sqrt на оригинала, с изключение на 2. - person Calyth; 09.10.2009
comment
@Matthieu M.: Съгласен. Написах ruby ​​скрипт, за да тествам това: 331: Stephen: 0.000635 Matthieu: 0.000611 68720001023: Stephen: 1.958449 Matthieu: 0.003167 999999000001: Stephen: 7.293912 Matthieu: 0.005871 - person aarona; 30.05.2010
comment
@Calyth добавянето на floor() към функцията не прави нищо различно от sqrt() само по себе си, но отнема повече време за изпълнение на задачата. - person aarona; 30.05.2010
comment
@StephenCanon: Условието за прекратяване на цикъла for е: i ‹ число, тогава как така, ако (число % i == 0 && i != число) е валидно? Имам предвид i != номер - person RajSanpui; 27.05.2013
comment
-1. Последната функция дава неправилния отговор за 4294967291. - person davidg; 07.04.2014
comment
Хареса ми нещо с квадратния корен! - person gab06; 16.10.2014
comment
@StephenCanon: и можете също да замените това увеличение на публикацията i++ с ++i. тъй като това също ще намали една стъпка на работа. - person Abdullah; 05.01.2015
comment
@StephenCanon, бих използвал i<number/i, за да избегна препълване. Още по-добре е да запазите коефициента и остатъка, напр. for(i=2, q=number/i, r=number%i; i<q; i++, q=number/i, r=number%i). - person Z boson; 29.04.2015
comment
@Zboson: Ако наистина искаме да се заяждаме, number/i е строго по-лошо от (предварително изчислено) sqrt(number). number/i извършва едно допълнително ненужно деление. Той също така прави условието на цикъла да зависи от операция с дълга латентност; на архитектура с конвейерно разделение, това лесно може да доведе до спиране поради ограничения на броя на неразрешените прогнози за разклонения в полет наведнъж. - person Stephen Canon; 29.04.2015
comment
@StephenCanon, ти вече правиш number%i, така че получаваш number/i безплатно. То е също толкова бързо, колкото sqrt(number). Прочетохте ли моите условни-тестове-in-primality-by-trial-division? - person Z boson; 29.04.2015
comment
@Zboson: толкова е бърз на архитектура без конвейерно разделение и не е по-ясен (по мое мнение вкусовете може да варират). - person Stephen Canon; 29.04.2015
comment
Кодът, публикуван от OP, вече беше 100% валиден C. Така че голяма част от този отговор е просто глупост, няма нужда да конвертирате нито едно нещо. bool true и false съществуват в C. static функции съществуват в C и декларациите на променливи вътре в циклите for са разрешени. - person Lundin; 26.10.2015
comment
можете също да се отървете от всички четни числа, така че i+=2 - person Kasparov92; 04.04.2016
comment
@Kasparov92 i започва от 2, така че i+=2 не е правилно. Можете да опитате да въведете 31 или 33, за да го тествате. - person CyberMew; 30.04.2016
comment
@CyberMew нека започна от 3 и i+=2, така че да преминем само към нечетните - person Kasparov92; 01.05.2016
comment
Като високо гласуван отговор, ъгловите грешки заслужават внимание - в код. 1) IsPrime(0 or 1) неправилно съобщава вярно. 2) Недефинирано поведение с IsPrime(int number) и голямо number, 3) IsPrime(unsigned number): i*i<=number е проблем, когато число близо до UINT_MAX като i*i препълва. Гранична линия безкраен цикъл, когато number == UINT_MAX. - person chux - Reinstate Monica; 10.02.2019

Учудвам се, че никой не спомена това.

Използвайте Ситето на Ератостен

подробности:

  1. По принцип непростите числа се делят на друго число освен на 1 и себе си
  2. Следователно: непростото число ще бъде произведение на прости числа.

Ситото на Ератостен намира просто число и го запаметява. Когато ново число се проверява за простота, всички предишни прости числа се проверяват спрямо известния списък с прости числа.

Причини:

  1. Този алгоритъм/проблем е известен като „Смущаващо паралелен
  2. Той създава колекция от прости числа
  3. Това е пример за проблем с динамично програмиране
  4. Бързо е!
person monksy    schedule 09.10.2009
comment
Освен това е O(n) в пространството и докато изчислението ви е за една стойност, това е огромна загуба на пространство без печалба в производителността. - person R.. GitHub STOP HELPING ICE; 30.05.2011
comment
(Всъщност O(n log n) или по-голямо, ако поддържате големи числа...) - person R.. GitHub STOP HELPING ICE; 30.05.2011
comment
Кой изчислява само 1 стойност за просто число за продължителността на живота на приложението? Простите числа са добър кандидат за кеширане. - person monksy; 31.05.2011
comment
Програма от команден ред, която се прекратява след една заявка, би била очевиден пример. Във всеки случай поддържането на глобално състояние е грозно и винаги трябва да се счита за компромис. И бих стигнал толкова далеч, че да кажа, че ситото (генерирано по време на изпълнение) е по същество безполезно. Ако вашите основни кандидати са достатъчно малки, за да можете да поберете сито с такъв размер в паметта, трябва просто да имате static const растерно изображение, на което числата са прости, и да го използвате, вместо да го запълвате по време на изпълнение. - person R.. GitHub STOP HELPING ICE; 31.05.2011
comment
Или, като общо правило, запаметяването е почти винаги безполезно, освен ако не можете по някакъв начин да пропуснете големи интервали, тъй като винаги, когато можете да си позволите толкова много памет, бихте могли дори по-лесно да си позволите толкова много дисково пространство/.text размер на сегмента. - person R.. GitHub STOP HELPING ICE; 31.05.2011
comment
Ситото на Ератостен е добър (е, добър) начин за решаване на проблема, генерирайки всички прости числа до n. Това е разточителен начин за решаване на проблема е n първичен? - person hobbs; 10.06.2014
comment
Ситото на Ератостен е само смущаващо паралелно за MIMD, а не за SIMD. Съвременният компютър използва няколко технологии за паралелизиране и MIMD е само една от тях. Ако намерите начин да го направите паралелен за SIMD, моля, уведомете ме! - person Z boson; 29.04.2015

Стивън Кенън го отговори много добре!

Но

  • Алгоритъмът може да бъде подобрен допълнително, като се наблюдава, че всички прости числа са във формата 6k ± 1, с изключение на 2 и 3.
  • Това е така, защото всички цели числа могат да бъдат изразени като (6k + i) за някакво цяло число k и за i = −1, 0, 1, 2, 3 или 4; 2 деления (6k + 0), (6k + 2), (6k + 4); и 3 деления (6k + 3).
  • Така че по-ефективен метод е да се провери дали n се дели на 2 или 3, след което да се проверят всички числа от формата 6k ± 1 ≤ √n.
  • Това е 3 пъти по-бързо от тестването на всички m до √n.

    int IsPrime(unsigned int number) {
        if (number <= 3 && number > 1) 
            return 1;            // as 2 and 3 are prime
        else if (number%2==0 || number%3==0) 
            return 0;     // check if number is divisible by 2 or 3
        else {
            unsigned int i;
            for (i=5; i*i<=number; i+=6) {
                if (number % i == 0 || number%(i + 2) == 0) 
                    return 0;
            }
            return 1; 
        }
    }
    
person Blackhat002    schedule 05.11.2014
comment
трябва да върнете 0, когато (число == 1), тъй като 1 не е просто число. - person Ahmad Ibrahim; 10.05.2016
comment
Този вид оптимизации са IMO неуместни за тази задача: защо да спираме на формата 6k ± 1 с изключение на 2 и 3, която се преобразува в n^2 mod 6 = 1, когато можете да имате n^4 mod 30 = 1 с изключение на 2,3,5 ... всъщност можете да продължите вечно, защото използвате прости числа, за да направите тази оптимизация ... и това Е самият принцип на по-общия алгоритъм Сито на Ератостен :) - person ghilesZ; 09.04.2017
comment
@GhilesZ: Не съм съгласен, това е много подходящо за проблема и с един || позволява на основния цикъл да работи ефективно 3 пъти по-бързо. - person verdy_p; 16.08.2017
comment
Освен това за number==1 той правилно връща 0 (непрост) с тестваното confition (number%2==0), така че изобщо няма грешка - person verdy_p; 16.08.2017
comment
Методът на Ератостен е напълно различен метод, който изисква разпределяне на голям O(n) масив от булеви стойности и не е задължително да е по-бърз поради индексирания достъп. Този код е добър, тъй като оптимизира първо случая на двете първи прости числа 2 и 3 (ето защо цикълът стъпва с 2*3). - person verdy_p; 16.08.2017
comment
Можете да продължите да използвате същата техника, като използвате следващото просто число (5) на стъпки от (30 == 2*3*5) и тествате 9 кандидат стойности (30k+{1;7;11;13;17;19;21;23 ;29}), все още без използване на масив за crible Въпреки това мисля, че е по-бързо да се изчисли предварително sqrt(i) веднъж, вместо да се изчислява продукт i*i при всеки цикъл. - person verdy_p; 16.08.2017
comment
(редактиране: премахнете 30k+21, което се дели на 3). Виждате, че първата оптимизация на двете първи прости числа печели много (6 пъти по-малко цикли, тестващи 2 кандидата, т.е. 3 пъти по-бързо от цикли на всички шансове). Оптимизацията на първите 3 прости числа (30 пъти по-малко цикли, тестващи 8 кандидата, ще бъде т.е. 30/8=3,75 пъти по-бърза от циклите на всички шансове), но печалбата е по-малко впечатляваща. Това е общо взето: няма да бъдете значително по-бързи с обикновена ясла! - person verdy_p; 16.08.2017

  1. Изградете таблица с малки прости числа и проверете дали те разделят вашето въведено число.
  2. Ако числото оцелее до 1, опитайте псевдо тестове за първичност с нарастваща основа. Вижте например тест за първичност на Милър-Рабин.
  3. Ако числото ви е оцеляло до 2, можете да заключите, че е просто, ако е под някои добре известни граници. В противен случай вашият отговор ще бъде само "вероятно прост". Ще намерите някои стойности за тези граници в wiki страницата.
person Eric Bainville    schedule 08.10.2009
comment
+1: пълно прекаляване с това, което питаше, но все пак правилно. - person Stephen Canon; 09.10.2009
comment
Имайте предвид, че Guy L. наскоро предложи използването на Miller-Rabin в отговор също и свързан към rosettacode.org /wiki/Miller-Rabin_primality_test#C — което показва внедряване в C, използвайки GMP. Записът също има редица реализации на голямо разнообразие от други езици. - person Jonathan Leffler; 02.08.2015

тази програма е много ефективна за проверка на едно число за проверка на основността.

bool check(int n){
    if (n <= 3) {
        return n > 1;
    }

    if (n % 2 == 0 || n % 3 == 0) {
        return false;
    }
        int sq=sqrt(n); //include math.h or use i*i<n in for loop
    for (int i = 5; i<=sq; i += 6) {
        if (n % i == 0 || n % (i + 2) == 0) {
            return false;
        }
    }

    return true;
}
person GorvGoyl    schedule 30.04.2015
comment
За да тествате просто число, трябва да преминете целия път от i=2 до i<=ceil(sqrt(n)). Пропуснахте 2 числа във вашия тест: Първо, прехвърлянето към (int) прави sqrt(n) ствол на десетичните знаци. Второ, използвахте i<sq, когато трябва да бъде i<=sq. Сега да предположим, че число отговаря на този проблем. Съставно число n, което има ceil(sqrt(n)) като по-малък множител. Вашият вътрешен цикъл работи за аз като: (5, 7), (11, 13), (17, 19), (23, 25), (29, 31), (35, 37), (41, 43), и така нататък, n%i и n%(i+2). Да предположим, че получаваме sqrt(1763)=41.98. Тъй като 1763=41*43 е съставно число. Вашият цикъл ще работи само до (35, 37) и ще се провали. - person DrBeco; 01.08.2015
comment
@DrBeco хубаво наблюдение! благодаря например. актуализира кода. - person GorvGoyl; 01.08.2015
comment
След като анализирах внимателно проблема ceil(), разбрах, че въпреки че много сайтове го препоръчват, той просто е пресилен. Можете да свържете и тествате само i<=sqrt(n) и всичко ще бъде наред. Тестовите случаи са големи между прости числа. Пример: 86028221*86028223=7400854980481283 и sqrt(7400854980481283)~86028222. И по-малките прости числа между познатите числа, 2 и 3, дават sqrt(6)=2.449, че trunked пак ще напусне 2. (Но по-малкият не е тестов случай, а просто сравнение, за да се подчертае). Така че, да, сега алгоритъмът е правилен. Няма нужда да използвате ceil(). - person DrBeco; 02.08.2015

Проверете модула на всяко цяло число от 2 до корена на числото, което проверявате.

Ако модулът е равен на нула, тогава той не е прост.

псевдо код:

bool IsPrime(int target)
{
  for (i = 2; i <= root(target); i++)
  {
    if ((target mod i) == 0)
    {
      return false;
    }
  }

  return true;
}
person Matt Lacey    schedule 08.10.2009
comment
Разбира се, недостатъкът е, че sqrt се изчислява на всяка итерация, което ще го забави много. - person Rich Bradshaw; 08.10.2009
comment
Всеки разумен компилатор трябва да може да открие, че root(target) е инвариант на цикъл и да го повдигне. - person Stephen Canon; 08.10.2009
comment
(и ако имате компилатор, който не може да направи тази оптимизация, трябва абсолютно да подадете бъг, за да уведомите автора на компилатора, че им липсва тази оптимизация.) - person Stephen Canon; 08.10.2009
comment
заедно с много други потенциални (микро)оптимизации, Ако ръчно получите sqrt преди оператора for, можете да проверите и мода на това (и да върнете false, ако е 0). - person Matt Lacey; 09.10.2009
comment
Ами ако целевата стойност е 1? - person ffffff01; 06.12.2012

След като прочетох този въпрос, бях заинтригуван от факта, че някои отговори предлагат оптимизация чрез изпълнение на цикъл с кратни на 2*3=6.

Така че създавам нова функция със същата идея, но с кратни на 2*3*5=30.

int check235(unsigned long n)
{
    unsigned long sq, i;

    if(n<=3||n==5)
        return n>1;

    if(n%2==0 || n%3==0 || n%5==0)
        return 0;

    if(n<=30)
        return checkprime(n); /* use another simplified function */

    sq=ceil(sqrt(n));
    for(i=7; i<=sq; i+=30)
        if (n%i==0 || n%(i+4)==0 || n%(i+6)==0 || n%(i+10)==0 || n%(i+12)==0 
           || n%(i+16)==0 || n%(i+22)==0 || n%(i+24)==0)
            return 0;

        return 1;
}

Като стартирам и двете функции и проверявам времето, мога да кажа, че тази функция е наистина по-бърза. Да видим 2 теста с 2 различни прости числа:

$ time ./testprimebool.x 18446744069414584321 0
f(2,3)
Yes, its prime.    
real    0m14.090s
user    0m14.096s
sys     0m0.000s

$ time ./testprimebool.x 18446744069414584321 1
f(2,3,5)
Yes, its prime.    
real    0m9.961s
user    0m9.964s
sys     0m0.000s

$ time ./testprimebool.x 18446744065119617029 0
f(2,3)
Yes, its prime.    
real    0m13.990s
user    0m13.996s
sys     0m0.004s

$ time ./testprimebool.x 18446744065119617029 1
f(2,3,5)
Yes, its prime.    
real    0m10.077s
user    0m10.068s
sys     0m0.004s

Така че си помислих дали някой ще спечели твърде много, ако се обобщи? Измислих функция, която първо ще извърши обсада, за да изчисти даден списък от първични прости числа и след това ще използва този списък, за да изчисли по-големия.

int checkn(unsigned long n, unsigned long *p, unsigned long t)
{
    unsigned long sq, i, j, qt=1, rt=0;
    unsigned long *q, *r;

    if(n<2)
        return 0;

    for(i=0; i<t; i++)
    {
        if(n%p[i]==0)
            return 0;
        qt*=p[i];
    }
    qt--;

    if(n<=qt)
        return checkprime(n); /* use another simplified function */

    if((q=calloc(qt, sizeof(unsigned long)))==NULL)
    {
        perror("q=calloc()");
        exit(1);
    }
    for(i=0; i<t; i++)
        for(j=p[i]-2; j<qt; j+=p[i])
            q[j]=1;

    for(j=0; j<qt; j++)
        if(q[j])
            rt++;

    rt=qt-rt;
    if((r=malloc(sizeof(unsigned long)*rt))==NULL)
    {
        perror("r=malloc()");
        exit(1);
    }
    i=0;
    for(j=0; j<qt; j++)
        if(!q[j])
            r[i++]=j+1;

    free(q);

    sq=ceil(sqrt(n));
    for(i=1; i<=sq; i+=qt+1)
    {
        if(i!=1 && n%i==0)
            return 0;
        for(j=0; j<rt; j++)
            if(n%(i+r[j])==0)
                return 0;
    }
    return 1;
}

Предполагам, че не съм оптимизирал кода, но е честно. Сега, тестовете. Поради толкова много динамична памет, очаквах списъкът 2 3 5 да бъде малко по-бавен от твърдо кодирания 2 3 5. Но беше добре, както можете да видите по-долу. След това времето ставаше все по-малко и по-малко, като кулминацията на най-добрия списък беше:

2 3 5 7 11 13 17 19

С 8,6 сек. Така че, ако някой създаде твърдо кодирана програма, която използва такава техника, бих предложил да използвате списъка 2 3 и 5, защото печалбата не е толкова голяма. Но също така, ако искате да кодирате, този списък е добре. Проблемът е, че не можете да посочите всички случаи без цикъл или вашият код ще бъде много голям (ще има 1658879 ORs, което е || в съответния вътрешен if). Следващият списък:

2 3 5 7 11 13 17 19 23

времето започна да се увеличава с 13 секунди. Ето целия тест:

$ time ./testprimebool.x 18446744065119617029 2 3 5
f(2,3,5)
Yes, its prime.
real    0m12.668s
user    0m12.680s
sys     0m0.000s

$ time ./testprimebool.x 18446744065119617029 2 3 5 7
f(2,3,5,7)
Yes, its prime.
real    0m10.889s
user    0m10.900s
sys     0m0.000s

$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11
f(2,3,5,7,11)
Yes, its prime.
real    0m10.021s
user    0m10.028s
sys     0m0.000s

$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13
f(2,3,5,7,11,13)
Yes, its prime.
real    0m9.351s
user    0m9.356s
sys     0m0.004s

$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17
f(2,3,5,7,11,13,17)
Yes, its prime.
real    0m8.802s
user    0m8.800s
sys     0m0.008s

$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17 19
f(2,3,5,7,11,13,17,19)
Yes, its prime.
real    0m8.614s
user    0m8.564s
sys     0m0.052s

$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17 19 23
f(2,3,5,7,11,13,17,19,23)
Yes, its prime.
real    0m13.013s
user    0m12.520s
sys     0m0.504s

$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17 19 23 29
f(2,3,5,7,11,13,17,19,23,29)                                                                                                                         
q=calloc(): Cannot allocate memory

PS. Не освободих(r) умишлено, давайки тази задача на операционната система, тъй като паметта ще бъде освободена веднага щом програмата излезе, за да спечеля малко време. Но би било разумно да го освободите, ако възнамерявате да продължите да изпълнявате кода си след изчислението.


БОНУС

int check2357(unsigned long n)
{
    unsigned long sq, i;

    if(n<=3||n==5||n==7)
        return n>1;

    if(n%2==0 || n%3==0 || n%5==0 || n%7==0)
        return 0;

    if(n<=210)
        return checkprime(n); /* use another simplified function */

    sq=ceil(sqrt(n));
    for(i=11; i<=sq; i+=210)
    {    
        if(n%i==0 || n%(i+2)==0 || n%(i+6)==0 || n%(i+8)==0 || n%(i+12)==0 || 
   n%(i+18)==0 || n%(i+20)==0 || n%(i+26)==0 || n%(i+30)==0 || n%(i+32)==0 || 
   n%(i+36)==0 || n%(i+42)==0 || n%(i+48)==0 || n%(i+50)==0 || n%(i+56)==0 || 
   n%(i+60)==0 || n%(i+62)==0 || n%(i+68)==0 || n%(i+72)==0 || n%(i+78)==0 || 
   n%(i+86)==0 || n%(i+90)==0 || n%(i+92)==0 || n%(i+96)==0 || n%(i+98)==0 || 
   n%(i+102)==0 || n%(i+110)==0 || n%(i+116)==0 || n%(i+120)==0 || n%(i+126)==0 || 
   n%(i+128)==0 || n%(i+132)==0 || n%(i+138)==0 || n%(i+140)==0 || n%(i+146)==0 || 
   n%(i+152)==0 || n%(i+156)==0 || n%(i+158)==0 || n%(i+162)==0 || n%(i+168)==0 || 
   n%(i+170)==0 || n%(i+176)==0 || n%(i+180)==0 || n%(i+182)==0 || n%(i+186)==0 || 
   n%(i+188)==0 || n%(i+198)==0)
            return 0;
    }
    return 1;
}

Време:

$ time ./testprimebool.x 18446744065119617029 7
h(2,3,5,7)
Yes, its prime.
real    0m9.123s
user    0m9.132s
sys     0m0.000s
person DrBeco    schedule 01.08.2015
comment
Бонус: 101-199 първични всички се провалят тук, защото 101 % (11+90). - person vp_arth; 31.08.2016
comment
трябва да спрете на n%(i+86) или да проверите n > i+k - person vp_arth; 31.08.2016
comment
Браво господине ще погледна Благодаря ти. Същият проблем се случва с функция check235() за прости числа 7, 11, 13, 17, 19, 23 и 29 - person DrBeco; 05.09.2016
comment
Решение: трябва да преместите тези напомняния в масив, итерация и прекъсване на итерацията, когато i+arr[k] >= n - person vp_arth; 05.09.2016
comment
Мислех за това, но не искам масив, тъй като if с константи може да бъде по-добре оптимизиран от компилатора. Редактирах, за да добавя изключение и да запазя текущата структура непокътната. Но съм съгласен, че с масив може да бъде по-добре за човешките очи. - person DrBeco; 05.09.2016

Бих добавил само, че никое четно число (такт 2) не може да бъде просто число. Това води до друго условие преди for цикъла. Така че крайният код трябва да изглежда така:

int IsPrime(unsigned int number) {
    if (number <= 1) return 0; // zero and one are not prime
    if ((number > 2) && ((number % 2) == 0)) return 0; //no even number is prime number (bar 2)
    unsigned int i;
    for (i=2; i*i<=number; i++) {
        if (number % i == 0) return 0;
    }
    return 1;
}
person Vladimir Kocjancic    schedule 11.02.2010

int is_prime(int val)
{
   int div,square;

   if (val==2) return TRUE;    /* 2 is prime */
   if ((val&1)==0) return FALSE;    /* any other even number is not */

   div=3;
   square=9;    /* 3*3 */
   while (square<val)
   {
     if (val % div == 0) return FALSE;    /* evenly divisible */
     div+=2;
     square=div*div;
   }
   if (square==val) return FALSE;
   return TRUE;
}

Обработката на 2 и четните числа се държи извън основния цикъл, който обработва само нечетни числа, разделени на нечетни числа. Това е така, защото нечетно число по модул четно число винаги ще даде ненулев отговор, което прави тези тестове излишни. Или, казано по друг начин, нечетно число може да се дели равномерно на друго нечетно число, но никога на четно число (E*E=>E, E*O =>E, O*E=>E и O*O=>O).

Деление/модул е ​​наистина скъпо за x86 архитектурата, въпреки че колко скъпо варира (вижте http://gmplib.org/~tege/x86-timing.pdf). Умноженията от друга страна са доста евтини.

person Olof Forshell    schedule 30.05.2011

Избягвайте грешка при препълване

unsigned i, number;
...
for (i=2; i*i<=number; i++) {  // Buggy
for (i=2; i*i<=number; i += 2) {  // Buggy
// or
for (i=5; i*i<=number; i+=6) { // Buggy

Тези форми са неправилни, когато number е просто число и i*i е близо до максималната стойност на типа.

Проблемът съществува при всички цели числа, signed, unsigned и по-широки.

Пример:

Нека UINT_MAX_SQRT като под на корен квадратен от максималната цяло число. напр. 65535, когато unsigned е 32-битов.

При for (i=2; i*i<=number; i++) този 10-годишен неизправност възниква, защото когато UINT_MAX_SQRT*UINT_MAX_SQRT <= number и number е просто число, следващата итерация води до препълване на умножението. Ако типът е подписан тип, препълването е UB. С неподписани типове, това само по себе си не е UB, но логиката се е развалила. Взаимодействията продължават, докато скъсен продукт надвиши number. Може да се получи неправилен резултат. С 32-битов unsigned опитайте 4,294,967,291, което е просто число.

Ако some_integer_type_MAX е било просто число на Мерсен, i*i<=number никога не е вярно.


За да избегнете този бъг, имайте предвид, че number%i, number/i е ефективен при много компилатори, тъй като изчисленията на коефициента и остатъка се правят заедно, като по този начин не се налагат допълнителни разходи, за да се направят и двете вместо само 1.

Просто цялостно решение:

bool IsPrime(unsigned number) {
    for(unsigned i = 2; i <= number/i; i++){
        if(number % i == 0){
            return false;
        }
    }
    return number >= 2;
}
person chux - Reinstate Monica    schedule 10.02.2019

Използвайки Ситото на Ератостен, изчислението е доста по-бързо в сравнение с „широкоизвестния“ алгоритъм за прости числа.

Чрез използване на псевдокод от неговото wiki (https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) , мога да имам решението на C#.

public bool IsPrimeNumber(int val) {
    // Using Sieve of Eratosthenes.
    if (val < 2)
    {
        return false;
    }

    // Reserve place for val + 1 and set with true.
    var mark = new bool[val + 1];
    for(var i = 2; i <= val; i++)
    {
        mark[i] = true;
    }

    // Iterate from 2 ... sqrt(val).
    for (var i = 2; i <= Math.Sqrt(val); i++)
    {
        if (mark[i])
        {
            // Cross out every i-th number in the places after i (all the multiples of i).
            for (var j = (i * i); j <= val; j += i)
            {
                mark[j] = false;
            }
        }
    }

    return mark[val];
}

IsPrimeNumber(1000000000) отнема 21s 758ms.

ЗАБЕЛЕЖКА: Стойността може да варира в зависимост от хардуерните спецификации.

person S_R    schedule 12.05.2016