Найелегантніший спосіб генерації простих чисел [закрито]


84

Який найелегантніший спосіб реалізувати цю функцію:

ArrayList generatePrimes(int n)

Ця функція генерує перші nпрості числа (edit: where n>1), тому generatePrimes(5)поверне ArrayListс {2, 3, 5, 7, 11}. (Я роблю це на C #, але я задоволений реалізацією Java - або будь-якою іншою подібною мовою з цього приводу (тому не Haskell)).

Я знаю, як написати цю функцію, але коли я зробив це вчора ввечері, це закінчилося не так добре, як я сподівався. Ось що я придумав:

ArrayList generatePrimes(int toGenerate)
{
    ArrayList primes = new ArrayList();
    primes.Add(2);
    primes.Add(3);
    while (primes.Count < toGenerate)
    {
        int nextPrime = (int)(primes[primes.Count - 1]) + 2;
        while (true)
        {
            bool isPrime = true;
            foreach (int n in primes)
            {
                if (nextPrime % n == 0)
                {
                    isPrime = false;
                    break;
                }
            }
            if (isPrime)
            {
                break;
            }
            else
            {
                nextPrime += 2;
            }
        }
        primes.Add(nextPrime);
    }
    return primes;
}

Мене не надто турбує швидкість, хоча я не хочу, щоб вона була очевидно неефективною. Я не проти, який метод використовується (наївний чи ситовий чи що-небудь ще), але я хочу, щоб він був досить коротким і очевидним, як він працює.

Редагувати : Дякую всім, хто відповів, хоча багато хто не відповів на моє фактичне запитання. Щоб повторити, я хотів мати хороший чистий фрагмент коду, який генерував список простих чисел. Я вже знаю, як це зробити, купу різних способів, але я схильний писати код, який не настільки зрозумілий, як може бути. У цій темі запропоновано кілька хороших варіантів:

  • Приємніша версія того, що я мав спочатку (Пітер Сміт, jmservera та Rekreativc)
  • Дуже чисте втілення сита Ератосфена (зірковий)
  • Використовуйте Java BigIntegerі nextProbablePrimeдля дуже простого коду, хоча я не можу уявити, щоб він був особливо ефективним (dfa)
  • Використовуйте LINQ для лінивого створення списку простих чисел (Магіс)
  • Помістіть багато простих чисел у текстовий файл і зачитайте їх, коли це необхідно (Дарін)

Редагування 2 : Я застосував у C # кілька методів, наведених тут, і ще один метод, про який тут не згадується. Всі вони ефективно знаходять перші n простих чисел (і я маю гідний метод пошуку межі, яку можна забезпечити ситам).


12
ні, і це також не для проекту Ейлера :-)
Девід Джонстон,

1
було б краще, щоб я повернув незліченну кількість <int> і поступився по одному
Феліче Поллано,

4
Я хотів би знати, що є найменш елегантним способом генерації простих чисел. Я думаю про щось, що стосується бази даних Access?
j_random_hacker

1
для порівняння, 2008 Haskell код по BMeph : nubBy (((>1).).gcd) [2..]. Він залишає лише недублікати серед натуральних чисел, починаючи з 2, при цьому розглядає як дублікат будь-яке число, яке gcdз будь-яким із раніше знайдених чисел більше 1. Це дуже неефективне, квадратичне за кількістю вироблених простих чисел. Але це елегантно .
Уілл Несс

самий елегантний , ІМО, є Хаскеля , import Data.List.Ordered ; let { _Y g = g (_Y g) ; primes = 2 : _Y( (3:) . minus [5,7..] . unionAll . map (\p-> [p*p, p*p+p*2..]) ) }але це, звичайно , цілком думки , заснованого .
Уілл Несс

Відповіді:


48

Використовуйте кошторис

pi(n) = n / log(n)

для кількості простих чисел до n, щоб знайти межу, а потім використовувати сито. Оцінка дещо занижує кількість простих речовин до n, тому сито буде трохи більшим, ніж потрібно, що нормально.

Це моє стандартне сито Java, обчислює перший мільйон простих чисел приблизно за секунду на звичайному ноутбуці:

public static BitSet computePrimes(int limit)
{
    final BitSet primes = new BitSet();
    primes.set(0, false);
    primes.set(1, false);
    primes.set(2, limit, true);
    for (int i = 0; i * i < limit; i++)
    {
        if (primes.get(i))
        {
            for (int j = i * i; j < limit; j += i)
            {
                primes.clear(j);
            }
        }
    }
    return primes;
}

3
Це дуже гарна реалізація сита Ератосфена
Девід Джонстон

1
чи не буде достатньо петлі, перебуваючи i <= Math.sqrt(limit)у зовнішній петлі?
Крістоф

1
@David Johnstone Ні, pi (n) = n / log (n) занижує кількість простих чисел до n, яке йде в протилежному напрямку. Мені приємно, що ви знайшли набагато приємніше наближення.
starblue

1
якщо ви бажаєте видалити всі кратні 2 у власному циклі, ви можете використовувати j + = 2 * i як приріст циклу, щоб заощадити додатковий час роботи, і ви можете обчислити, що один раз, використовуючи зсув біта
Нік Ларсен

5
Замінивши BitSetна клас, що реалізує факторизацію коліс на 2, 3 і 5, він стає майже в 3 рази швидшим.
starblue

37

Велике спасибі всім, хто дав корисні відповіді. Ось мої реалізації декількох різних методів пошуку перших n простих чисел у C #. Перші два методи - це майже те, що було опубліковано тут. (Назви плакатів знаходяться поруч із заголовком.) Я планую колись зробити сито Аткіна, хоча я підозрюю, що це буде не так просто, як методи, які зараз тут. Якщо хтось може побачити будь-який спосіб вдосконалення будь-якого з цих методів, я хотів би знати :-)

Стандартний метод ( Пітер Сміт , jmservera , Rekreativc )

Перше просте число - 2. Додайте це до списку простих чисел. Наступне просте - це наступне число, яке не рівномірно ділиться на жодне число в цьому списку.

public static List<int> GeneratePrimesNaive(int n)
{
    List<int> primes = new List<int>();
    primes.Add(2);
    int nextPrime = 3;
    while (primes.Count < n)
    {
        int sqrt = (int)Math.Sqrt(nextPrime);
        bool isPrime = true;
        for (int i = 0; (int)primes[i] <= sqrt; i++)
        {
            if (nextPrime % primes[i] == 0)
            {
                isPrime = false;
                break;
            }
        }
        if (isPrime)
        {
            primes.Add(nextPrime);
        }
        nextPrime += 2;
    }
    return primes;
}

Це було оптимізовано шляхом лише тестування на подільність до квадратного кореня числа, що тестується; і лише перевіряючи непарні числа. Це може бути додатково оптимізована шляхом тестування тільки числа виду 6k+[1, 5], або , 30k+[1, 7, 11, 13, 17, 19, 23, 29]або так далі .

Решето Ератосфена ( зірковий )

Це знаходить усі прості числа до k . Щоб скласти список перших n простих чисел, нам спочатку потрібно наблизити значення n- го простого числа. Наступний метод, як описано тут , робить це.

public static int ApproximateNthPrime(int nn)
{
    double n = (double)nn;
    double p;
    if (nn >= 7022)
    {
        p = n * Math.Log(n) + n * (Math.Log(Math.Log(n)) - 0.9385);
    }
    else if (nn >= 6)
    {
        p = n * Math.Log(n) + n * Math.Log(Math.Log(n));
    }
    else if (nn > 0)
    {
        p = new int[] { 2, 3, 5, 7, 11 }[nn - 1];
    }
    else
    {
        p = 0;
    }
    return (int)p;
}

// Find all primes up to and including the limit
public static BitArray SieveOfEratosthenes(int limit)
{
    BitArray bits = new BitArray(limit + 1, true);
    bits[0] = false;
    bits[1] = false;
    for (int i = 0; i * i <= limit; i++)
    {
        if (bits[i])
        {
            for (int j = i * i; j <= limit; j += i)
            {
                bits[j] = false;
            }
        }
    }
    return bits;
}

public static List<int> GeneratePrimesSieveOfEratosthenes(int n)
{
    int limit = ApproximateNthPrime(n);
    BitArray bits = SieveOfEratosthenes(limit);
    List<int> primes = new List<int>();
    for (int i = 0, found = 0; i < limit && found < n; i++)
    {
        if (bits[i])
        {
            primes.Add(i);
            found++;
        }
    }
    return primes;
}

Решето Сундараму

Я відкрив це сито нещодавно, але його можна реалізувати досить просто. Моє впровадження не таке швидке, як сито Ератосфена, але воно значно швидше, ніж наївний метод.

public static BitArray SieveOfSundaram(int limit)
{
    limit /= 2;
    BitArray bits = new BitArray(limit + 1, true);
    for (int i = 1; 3 * i + 1 < limit; i++)
    {
        for (int j = 1; i + j + 2 * i * j <= limit; j++)
        {
            bits[i + j + 2 * i * j] = false;
        }
    }
    return bits;
}

public static List<int> GeneratePrimesSieveOfSundaram(int n)
{
    int limit = ApproximateNthPrime(n);
    BitArray bits = SieveOfSundaram(limit);
    List<int> primes = new List<int>();
    primes.Add(2);
    for (int i = 1, found = 1; 2 * i + 1 <= limit && found < n; i++)
    {
        if (bits[i])
        {
            primes.Add(2 * i + 1);
            found++;
        }
    }
    return primes;
}

FYI - мені довелося змінити ваш лічильник основного циклу на "for (int i = 0; i * i <= limit && i * i> 0; i ++)", щоб запобігти переповненню.
Jacobs Data Solutions

Ця реалізація Сита Сундараму є однією з небагатьох правильних. Більшість з них використовують неправильні межі для i та j під час обчислення, що i+j+2*i*jпризводить до неправильного виводу.
jahackbeth

14

Відрегулювавши старе запитання, але я натрапив на нього, граючи з LINQ.

Цей код вимагає .NET4.0 або .NET3.5 з паралельними розширеннями

public List<int> GeneratePrimes(int n) {
    var r = from i in Enumerable.Range(2, n - 1).AsParallel()
            where Enumerable.Range(1, (int)Math.Sqrt(i)).All(j => j == 1 || i % j != 0)
            select i;
    return r.ToList();
}

1
Чому це не прийнята відповідь? Код тут набагато коротший, елегантніший і набагато швидший, ніж код у прийнятій відповіді. Бажаю, щоб я міг підтримати більше одного разу!
Avrohom Yisroel

9

Ви на доброму шляху.

Кілька коментарів

  • прості числа.Add (3); робить, що ця функція не працює для числа = 1

  • Вам не потрібно перевіряти ділення із застосуванням чисел, більших, ніж квадратний корінь числа, що перевіряється.

Запропонований код:

ArrayList generatePrimes(int toGenerate)
{
    ArrayList primes = new ArrayList();

    if(toGenerate > 0) primes.Add(2);

    int curTest = 3;
    while (primes.Count < toGenerate)
    {

        int sqrt = (int) Math.sqrt(curTest);

        bool isPrime = true;
        for (int i = 0; i < primes.Count && primes.get(i) <= sqrt; ++i)
        {
            if (curTest % primes.get(i) == 0)
            {
                isPrime = false;
                break;
            }
        }

        if(isPrime) primes.Add(curTest);

        curTest +=2
    }
    return primes;
}

1
тестування того, що prime * prime <= curTest у циклі, замість попереднього обчислення квадратного кореня, швидше за все, зробить його швидшим та зробить його більш загальним (буде працювати для bignums тощо)
yairchu

Навіщо використовувати квадратний корінь? Яке математичне підґрунтя для такого варіанту? Я, мабуть, тупо, поділяв би лише на 2.
Луїс Філіпе

3
Оскільки, якщо число має прості множники, принаймні один з них повинен бути меншим або рівним квадратному кореню. Якщо a * b = c і a <= b, тоді a <= sqrt (c) <= b.
Девід Джонстоун,

8

вам слід поглянути на ймовірні прості числа . Зокрема, погляньте на рандомізовані алгоритми та тест на первинність Міллера – Рабіна .

Для повноти ви можете просто використовувати java.math.BigInteger :

public class PrimeGenerator implements Iterator<BigInteger>, Iterable<BigInteger> {

    private BigInteger p = BigInteger.ONE;

    @Override
    public boolean hasNext() {
        return true;
    }

    @Override
    public BigInteger next() {
        p = p.nextProbablePrime();
        return p;
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException("Not supported.");
    }

    @Override
    public Iterator<BigInteger> iterator() {
        return this;
    }
}

@Test
public void printPrimes() {
    for (BigInteger p : new PrimeGenerator()) {
        System.out.println(p);
    }
}

2
Міллер-Рабін дуже швидкий, а код дуже простий. Надання йому достатніх ітерацій робить його досить надійним, щоб конкурувати з випадковим відмовою процесора з точки зору ймовірності помилково позитивного. Недоліком алгоритму є те, що зрозуміти, чому він насправді працює, є складним завданням.
Брайан

6

Ні в якому разі не ефективним, але, можливо, найбільш читабельним:

public static IEnumerable<int> GeneratePrimes()
{
   return Range(2).Where(candidate => Range(2, (int)Math.Sqrt(candidate)))
                                     .All(divisor => candidate % divisor != 0));
}

з:

public static IEnumerable<int> Range(int from, int to = int.MaxValue)
{
   for (int i = from; i <= to; i++) yield return i;
}

Насправді лише варіація деяких публікацій тут з приємнішим форматуванням.


5

Авторські права 2009 від St.Wittum 13189 Berlin GERMANY за ліцензією CC-BY-SA https://creativecommons.org/licenses/by-sa/3.0/

Найпростішим, але найелегантнішим способом обчислення ВСІХ ПРИМУЛ, був би такий, але цей спосіб є повільним, а витрати пам’яті набагато вищі для більших чисел, оскільки використання функції факультету (!) ... але він демонструє варіацію теореми Вільсона в програмі генерувати всі прості числа за алгоритмом, реалізованим в Python

#!/usr/bin/python
f=1 # 0!
p=2 # 1st prime
while True:
    if f%p%2:
        print p
    p+=1
    f*=(p-2)

4

Використовуйте генератор простих чисел, щоб створити primes.txt, а потім:

class Program
{
    static void Main(string[] args)
    {
        using (StreamReader reader = new StreamReader("primes.txt"))
        {
            foreach (var prime in GetPrimes(10, reader))
            {
                Console.WriteLine(prime);
            }
        }
    }

    public static IEnumerable<short> GetPrimes(short upTo, StreamReader reader)
    {
        int count = 0;
        string line = string.Empty;
        while ((line = reader.ReadLine()) != null && count++ < upTo)
        {
            yield return short.Parse(line);
        }
    }
}

У цьому випадку я використовую Int16 у підписі методу, тому мій файл primes.txt містить цифри від 0 до 32767. Якщо ви хочете поширити це на Int32 або Int64, ваш primes.txt може бути значно більшим.


3
Посилаючись на OP: "Я не проти, який метод використовується (наївний, ситовий чи що-небудь ще), але я хочу, щоб він був досить коротким і очевидним, як він працює". Я думаю, що моя відповідь цілком доречна. Це також найшвидший метод.
Дарін Димитров

14
Навіть якщо він каже "Мені не проти, який метод ...", я не думаю, що це включає "відкрити список простих чисел". Це було б як відповідь на питання "як побудувати комп’ютер" шляхом "придбання комп’ютера". -1
stevenvh

8
Це було б швидше, якби ви насправді писали прості числа у самому вихідному коді, а не читали їх із файлу.
Даніель Даранас,

3
Споживати багато пам’яті? більше, ніж читання повного списку простих чисел як тексту в ... пам'ять? Чи знаєте ви, як працюють рядки в .net?
jmservera

6
Список простих чисел - це нескінченний, але незмінний список, тому має сенс використовувати попередньо розрахований список до ймовірної верхньої межі для програми. Навіщо витрачати час на написання коду, який може бути помилковим, якщо є правильний загальнодоступний список, який можна використовувати для задоволення вимог.
AndyM

4

Я можу запропонувати наступне рішення C #. Це далеко не швидко, але цілком зрозуміло, що це робить.

public static List<Int32> GetPrimes(Int32 limit)
{
    List<Int32> primes = new List<Int32>() { 2 };

    for (int n = 3; n <= limit; n += 2)
    {
        Int32 sqrt = (Int32)Math.Sqrt(n);

        if (primes.TakeWhile(p => p <= sqrt).All(p => n % p != 0))
        {
            primes.Add(n);
        }
    }

    return primes;
}

Я пропустив будь-які перевірки - якщо обмеження від’ємне або менше двох (на даний момент метод завжди буде принаймні повертати два як прості). Але це все легко виправити.

ОНОВЛЕННЯ

З наступними двома методами розширення

public static void Do<T>(this IEnumerable<T> collection, Action<T> action)
{
    foreach (T item in collection)
    {
        action(item);
    }
}

public static IEnumerable<Int32> Range(Int32 start, Int32 end, Int32 step)
{
    for (int i = start; i < end; i += step)
    }
        yield return i;
    }
}

Ви можете переписати його наступним чином.

public static List<Int32> GetPrimes(Int32 limit)
{
    List<Int32> primes = new List<Int32>() { 2 };

    Range(3, limit, 2)
        .Where(n => primes
            .TakeWhile(p => p <= Math.Sqrt(n))
            .All(p => n % p != 0))
        .Do(n => primes.Add(n));

    return primes;
}

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


Я майже впевнений, що обчислення квадратного кореня оптимізовано компілятором JIT (при компіляції з увімкненою оптимізацією). Вам доведеться перевірити це, вивчивши згенеровану збірку (ІЛ лише частково оптимізований і не наближається до оптимізації, виконаної компілятором JIT. Часи підйому циклу та інших мікрооптимізацій закінчилися. Насправді, іноді намагаються перехитрити JIT може уповільнити ваш код.
Дейв Блек

4

Ось реалізація решета Ератосфена в C #:

    IEnumerable<int> GeneratePrimes(int n)
    {
        var values = new Numbers[n];

        values[0] = Numbers.Prime;
        values[1] = Numbers.Prime;

        for (int outer = 2; outer != -1; outer = FirstUnset(values, outer))
        {
            values[outer] = Numbers.Prime;

            for (int inner = outer * 2; inner < values.Length; inner += outer)
                values[inner] = Numbers.Composite;
        }

        for (int i = 2; i < values.Length; i++)
        {
            if (values[i] == Numbers.Prime)
                yield return i;
        }
    }

    int FirstUnset(Numbers[] values, int last)
    {
        for (int i = last; i < values.Length; i++)
            if (values[i] == Numbers.Unset)
                return i;

        return -1;
    }

    enum Numbers
    {
        Unset,
        Prime,
        Composite
    }

я б зробив це з bool замість enum ...
Letterman

3

Використовуючи той самий алгоритм, ви можете зробити це трохи коротше:

List<int> primes=new List<int>(new int[]{2,3});
for (int n = 5; primes.Count< numberToGenerate; n+=2)
{
  bool isPrime = true;
  foreach (int prime in primes)
  {
    if (n % prime == 0)
    {
      isPrime = false;
      break;
    }
  }
  if (isPrime)
    primes.Add(n);
}

3

Я знаю, що ви просили рішення, яке не стосується Хаскелла, але я включаю це сюди, оскільки воно стосується питання, а також Хаскелл прекрасний для такого типу речей.

module Prime where

primes :: [Integer]
primes = 2:3:primes'
  where
    -- Every prime number other than 2 and 3 must be of the form 6k + 1 or 
    -- 6k + 5. Note we exclude 1 from the candidates and mark the next one as
    -- prime (6*0+5 == 5) to start the recursion.
    1:p:candidates = [6*k+r | k <- [0..], r <- [1,5]]
    primes'        = p : filter isPrime candidates
    isPrime n      = all (not . divides n) $ takeWhile (\p -> p*p <= n) primes'
    divides n p    = n `mod` p == 0

Так, я теж великий шанувальник Хаскелла (я б хотів, щоб я це краще знав)
Девід Джонстон,

3

Я написав просту реалізацію Ератосфена на c #, використовуючи деякі LINQ.

На жаль, LINQ не надає нескінченної послідовності ints, тому вам доведеться використовувати int.MaxValue :(

Мені довелося кешувати анонімним типом sqrt-кандидата, щоб уникнути його обчислення для кожного кешованого простого тексту (виглядає трохи потворно).

Я використовую список попередніх простих чисел до sqrt кандидата

cache.TakeWhile(c => c <= candidate.Sqrt)

і перевіряйте кожен Int, починаючи з 2 проти нього

.Any(cachedPrime => candidate.Current % cachedPrime == 0)

Ось код:

static IEnumerable<int> Primes(int count)
{
    return Primes().Take(count);
}

static IEnumerable<int> Primes()
{
    List<int> cache = new List<int>();

    var primes = Enumerable.Range(2, int.MaxValue - 2).Select(candidate => new 
    {
        Sqrt = (int)Math.Sqrt(candidate), // caching sqrt for performance
        Current = candidate
    }).Where(candidate => !cache.TakeWhile(c => c <= candidate.Sqrt)
            .Any(cachedPrime => candidate.Current % cachedPrime == 0))
            .Select(p => p.Current);

    foreach (var prime in primes)
    {
        cache.Add(prime);
        yield return prime;
    }
}

Ще однією оптимізацією є уникнення перевірки парних чисел і повернення лише 2 перед створенням Списку. Таким чином, якщо метод виклику просто запитує 1 прем’єр, це дозволить уникнути усіх безладів:

static IEnumerable<int> Primes()
{
    yield return 2;
    List<int> cache = new List<int>() { 2 };

    var primes = Enumerable.Range(3, int.MaxValue - 3)
        .Where(candidate => candidate % 2 != 0)
        .Select(candidate => new
    {
        Sqrt = (int)Math.Sqrt(candidate), // caching sqrt for performance
        Current = candidate
    }).Where(candidate => !cache.TakeWhile(c => c <= candidate.Sqrt)
            .Any(cachedPrime => candidate.Current % cachedPrime == 0))
            .Select(p => p.Current);

    foreach (var prime in primes)
    {
        cache.Add(prime);
        yield return prime;
    }
}

1
Мені б хотілося, щоб я знав LINQ достатньо, щоб краще оцінити та зрозуміти цю відповідь :-) Крім того, у мене відчуття, що це не реалізація сита Ератосфена, і що воно працює концептуально так само, як моя початкова функція (знайти наступну число, яке не ділиться на жодну з раніше знайдених простих чисел).
Девід Джонстон,

Так, але "знайти наступне число, яке не ділиться на жодну з раніше знайдених простих чисел (меншу за кількість)", концептуально схоже на сито ератосфена. Якщо ви віддаєте перевагу, я можу трохи його переформатувати, щоб зробити його більш читабельним, навіть якщо ви не знайомі з LINQ. Чи знайомі ви з ітераторами?
Maghis

Мені подобається такий підхід у тому, що наступний простий розраховується саме тоді, коли абонент просить про це, тому такі речі, як "взяти перші n простих чисел" або "взяти простих чисел, менших за n", стають тривіальними
Maghis

1
Дякую, але я розумію цього достатньо, щоб більш-менш знати, що це робить :-) Мені подобається ледача оцінка, але я все одно не назвав би це реалізацією сита Ератосфена.
Девід Джонстон,

1

Щоб зробити його більш елегантним, вам слід переформатувати ваш тест IsPrime в окремий метод, і обробити цикли та збільшення поза цим.


1

Я зробив це на Java за допомогою написаної мною функціональної бібліотеки, але оскільки моя бібліотека використовує ті самі поняття, що і Enumerations, я впевнений, що код можна адаптувати:

Iterable<Integer> numbers = new Range(1, 100);
Iterable<Integer> primes = numbers.inject(numbers, new Functions.Injecter<Iterable<Integer>, Integer>()
{
    public Iterable<Integer> call(Iterable<Integer> numbers, final Integer number) throws Exception
    {
        // We don't test for 1 which is implicit
        if ( number <= 1 )
        {
            return numbers;
        }
        // Only keep in numbers those that do not divide by number
        return numbers.reject(new Functions.Predicate1<Integer>()
        {
            public Boolean call(Integer n) throws Exception
            {
                return n > number && n % number == 0;
            }
        });
    }
});

1

Це найелегантніше, що я можу придумати за короткий термін.

ArrayList generatePrimes(int numberToGenerate)
{
    ArrayList rez = new ArrayList();

    rez.Add(2);
    rez.Add(3);

    for(int i = 5; rez.Count <= numberToGenerate; i+=2)
    {
        bool prime = true;
        for (int j = 2; j < Math.Sqrt(i); j++)
        {
            if (i % j == 0)
            {
                    prime = false;
                    break;
            }
        }
        if (prime) rez.Add(i);
    }

    return rez;
}

Сподіваюся, це допомагає дати вам уявлення. Я впевнений, що це можна оптимізувати, однак це повинно дати вам уявлення про те, як вашу версію можна зробити більш елегантною.

EDIT: Як зазначено в коментарях, цей алгоритм справді повертає неправильні значення для numberToGenerate <2. Я просто хочу зазначити, що я не намагався опублікувати йому чудовий метод для створення простих чисел (подивіться на відповідь Анрі для цього), Я лише вказував, як його метод можна зробити більш витонченим.


3
Цей повертає неправильний результат для numberToGenerate <2
Пітер Сміт

Це правда, однак я не розробляв алгоритм, я просто показував йому, як його метод можна зробити більш елегантним. Отже, ця версія однаково неправильна, як і версія, що відкриває питання.
Девід Божак,

1
Мені не спало на думку, що він був розбитий на n = 1. Я трохи змінив питання, так що функція повинна працювати лише при n> 1 :-)
Девід Джонстон,

це допускає квадратики простих чисел як прості числа.
Will Ness

1

Використовуючи програмування на основі потоку у функціональній Java , я придумав наступне. Тип Naturalпо суті a BigInteger> = 0.

public static Stream<Natural> sieve(final Stream<Natural> xs)
{ return cons(xs.head(), new P1<Stream<Natural>>()
  { public Stream<Natural> _1()
    { return sieve(xs.tail()._1()
                   .filter($(naturalOrd.equal().eq(ZERO))
                           .o(mod.f(xs.head())))); }}); }

public static final Stream<Natural> primes
  = sieve(forever(naturalEnumerator, natural(2).some()));

Тепер у вас є цінність, яку ви можете носити з собою, - це нескінченний потік простих чисел. Ви можете робити такі речі:

// Take the first n primes
Stream<Natural> nprimes = primes.take(n);

// Get the millionth prime
Natural mprime = primes.index(1000000);

// Get all primes less than n
Stream<Natural> pltn = primes.takeWhile(naturalOrd.lessThan(n));

Пояснення сита:

  1. Припустимо, що перше число в потоці аргументів є простим і поставте його в передній частині зворотного потоку. Решта поверненого потоку є обчисленням, яке виробляється лише за запитом.
  2. Якщо хтось просить залишок потоку, викличте решето для решти потоку аргументів, відфільтрувавши числа, що діляться на перше число (залишок ділення дорівнює нулю).

Вам потрібно мати такий імпорт:

import fj.P1;
import static fj.FW.$;
import static fj.data.Enumerator.naturalEnumerator;
import fj.data.Natural;
import static fj.data.Natural.*;
import fj.data.Stream;
import static fj.data.Stream.*;
import static fj.pre.Ord.naturalOrd;

1

Я особисто думаю, що це досить коротка і чиста реалізація (Java):

static ArrayList<Integer> getPrimes(int numPrimes) {
    ArrayList<Integer> primes = new ArrayList<Integer>(numPrimes);
    int n = 2;
    while (primes.size() < numPrimes) {
        while (!isPrime(n)) { n++; }
        primes.add(n);
        n++;
    }
    return primes;
}

static boolean isPrime(int n) {
    if (n < 2) { return false; }
    if (n == 2) { return true; }
    if (n % 2 == 0) { return false; }
    int d = 3;
    while (d * d <= n) {
        if (n % d == 0) { return false; }
        d += 2;
    }
    return true;
}

1

Спробуйте цей LINQ-запит, він генерує прості числа, як ви очікували

        var NoOfPrimes= 5;
        var GeneratedPrime = Enumerable.Range(1, int.MaxValue)
          .Where(x =>
            {
                 return (x==1)? false:
                        !Enumerable.Range(1, (int)Math.Sqrt(x))
                        .Any(z => (x % z == 0 && x != z && z != 1));
            }).Select(no => no).TakeWhile((val, idx) => idx <= NoOfPrimes-1).ToList();

1
// Create a test range
IEnumerable<int> range = Enumerable.Range(3, 50 - 3);

// Sequential prime number generator
var primes_ = from n in range
     let w = (int)Math.Sqrt(n)
     where Enumerable.Range(2, w).All((i) => n % i > 0)
     select n;

// Note sequence of output:
// 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
foreach (var p in primes_)
    Trace.Write(p + ", ");
Trace.WriteLine("");

0

Ось приклад коду python, який виводить суму всіх простих чисел нижче двох мільйонів:

from math import *

limit = 2000000
sievebound = (limit - 1) / 2
# sieve only odd numbers to save memory
# the ith element corresponds to the odd number 2*i+1
sieve = [False for n in xrange(1, sievebound + 1)]
crosslimit = (int(ceil(sqrt(limit))) - 1) / 2
for i in xrange(1, crosslimit):
    if not sieve[i]:
        # if p == 2*i + 1, then
        #   p**2 == 4*(i**2) + 4*i + 1
        #        == 2*i * (i + 1)
        for j in xrange(2*i * (i + 1), sievebound, 2*i + 1):
            sieve[j] = True
sum = 2
for i in xrange(1, sievebound):
    if not sieve[i]:
        sum = sum + (2*i+1)
print sum

0

Найпростіший метод - це спроби та помилки: ви намагаєтесь, якщо будь-яке число від 2 до n-1 ділить вашого кандидата на перше місце.
Перші комбінації клавіш, звичайно, а) вам потрібно перевірити лише непарні числа, і б) вам потрібно лише перевірити наявність дільників до sqrt (n).

У вашому випадку, коли ви також генеруєте всі попередні прості числа в процесі, вам потрібно лише перевірити, чи не розділяє будь-яка з простих чисел у вашому списку, до sqrt (n).
Це має бути найшвидше, що ви можете отримати за свої гроші :-)

редагувати
Добре, код, ти його просив. Але я попереджаю вас :-), це 5-хвилинний швидкий і брудний код Delphi:

procedure TForm1.Button1Click(Sender: TObject);
const
  N = 100;
var
  PrimeList: TList;
  I, J, SqrtP: Integer;
  Divides: Boolean;
begin
  PrimeList := TList.Create;
  for I := 2 to N do begin
    SqrtP := Ceil(Sqrt(I));
    J := 0;
    Divides := False;
    while (not Divides) and (J < PrimeList.Count) 
                        and (Integer(PrimeList[J]) <= SqrtP) do begin
      Divides := ( I mod Integer(PrimeList[J]) = 0 );
      inc(J);
    end;
    if not Divides then
      PrimeList.Add(Pointer(I));
  end;
  // display results
  for I := 0 to PrimeList.Count - 1 do
    ListBox1.Items.Add(IntToStr(Integer(PrimeList[I])));
  PrimeList.Free;
end;

1
І як ви це виражаєте в коді? :-)
Девід Джонстон,

0

Щоб дізнатися перші 100 простих чисел, можна розглянути наступний код Java.

int num = 2;
int i, count;
int nPrimeCount = 0;
int primeCount = 0;

    do
    {

        for (i = 2; i <num; i++)
        {

             int n = num % i;

             if (n == 0) {

             nPrimeCount++;
         //  System.out.println(nPrimeCount + " " + "Non-Prime Number is: " + num);

             num++;
             break;

             }
       }

                if (i == num) {

                    primeCount++;

                    System.out.println(primeCount + " " + "Prime number is: " + num);
                    num++;
                }


     }while (primeCount<100);

0

Я отримав це завдяки першому прочитанню "Решета Аткіна" на Вікі, а також деяким попереднім думкам, які я над цим придумав - я витрачаю багато часу на кодування з нуля і отримую абсолютно нуль, коли люди критично ставляться до мого компіляторного, дуже щільного кодування style + Я навіть не зробив першої спроби запустити код ... багато з парадигми, яку я навчився використовувати, тут, просто читайте і плачте, отримуйте те, що можете.

Будьте абсолютно і повністю впевнені, що реально протестуйте все це перед будь-яким використанням, напевно, нікому не показуйте - це для читання та розгляду ідей. Мені потрібно запустити інструмент первинності, тому я починаю кожен раз, коли мені потрібно щось працювати.

Отримайте один чистий компілятор, а потім починайте забирати дефектне - у мене майже 108 мільйонів натискань клавіш корисного коду, які роблять це таким чином, ... використовуйте те, що можете.

Завтра я попрацюю над своєю версією.

package demo;
// This code is a discussion of an opinion in a technical forum.
// It's use as a basis for further work is not prohibited.
import java.util.Arrays;
import java.util.HashSet;
import java.util.ArrayList;
import java.security.GeneralSecurityException;

/**
 * May we start by ignores any numbers divisible by two, three, or five
 * and eliminate from algorithm 3, 5, 7, 11, 13, 17, 19 completely - as
 * these may be done by hand. Then, with some thought we can completely
 * prove to certainty that no number larger than square-root the number
 * can possibly be a candidate prime.
 */

public class PrimeGenerator<T>
{
    //
    Integer HOW_MANY;
    HashSet<Integer>hashSet=new HashSet<Integer>();
    static final java.lang.String LINE_SEPARATOR
       =
       new java.lang.String(java.lang.System.getProperty("line.separator"));//
    //
    PrimeGenerator(Integer howMany) throws GeneralSecurityException
    {
        if(howMany.intValue() < 20)
        {
            throw new GeneralSecurityException("I'm insecure.");
        }
        else
        {
            this.HOW_MANY=howMany;
        }
    }
    // Let us then take from the rich literature readily 
    // available on primes and discount
    // time-wasters to the extent possible, utilizing the modulo operator to obtain some
    // faster operations.
    //
    // Numbers with modulo sixty remainder in these lists are known to be composite.
    //
    final HashSet<Integer> fillArray() throws GeneralSecurityException
    {
        // All numbers with modulo-sixty remainder in this list are not prime.
        int[]list1=new int[]{0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,
        32,34,36,38,40,42,44,46,48,50,52,54,56,58};        //
        for(int nextInt:list1)
        {
            if(hashSet.add(new Integer(nextInt)))
            {
                continue;
            }
            else
            {
                throw new GeneralSecurityException("list1");//
            }
        }
        // All numbers with modulo-sixty remainder in this list are  are
        // divisible by three and not prime.
        int[]list2=new int[]{3,9,15,21,27,33,39,45,51,57};
        //
        for(int nextInt:list2)
        {
            if(hashSet.add(new Integer(nextInt)))
            {
                continue;
            }
            else
            {
                throw new GeneralSecurityException("list2");//
            }
        }
        // All numbers with modulo-sixty remainder in this list are
        // divisible by five and not prime. not prime.
        int[]list3=new int[]{5,25,35,55};
        //
        for(int nextInt:list3)
        {
            if(hashSet.add(new Integer(nextInt)))
            {
                continue;
            }
            else
            {
                throw new GeneralSecurityException("list3");//
            }
        }
        // All numbers with modulo-sixty remainder in
        // this list have a modulo-four remainder of 1.
        // What that means, I have neither clue nor guess - I got all this from
        int[]list4=new int[]{1,13,17,29,37,41,49,53};
        //
        for(int nextInt:list4)
        {
            if(hashSet.add(new Integer(nextInt)))
            {
                continue;
            }
            else
            {
                throw new GeneralSecurityException("list4");//
            }
        }
        Integer lowerBound=new Integer(19);// duh
        Double upperStartingPoint=new Double(Math.ceil(Math.sqrt(Integer.MAX_VALUE)));//
        int upperBound=upperStartingPoint.intValue();//
        HashSet<Integer> resultSet=new HashSet<Integer>();
        // use a loop.
        do
        {
            // One of those one liners, whole program here:
            int aModulo=upperBound % 60;
            if(this.hashSet.contains(new Integer(aModulo)))
            {
                continue;
            }
            else
            {
                resultSet.add(new Integer(aModulo));//
            }
        }
        while(--upperBound > 20);
        // this as an operator here is useful later in your work.
        return resultSet;
    }
    // Test harness ....
    public static void main(java.lang.String[] args)
    {
        return;
    }
}
//eof

0

Спробуйте цей код.

protected bool isPrimeNubmer(int n)
    {
        if (n % 2 == 0)
            return false;
        else
        {
            int j = 3;
            int k = (n + 1) / 2 ;

            while (j <= k)
            {
                if (n % j == 0)
                    return false;
                j = j + 2;
            }
            return true;
        }
    }
    protected void btn_primeNumbers_Click(object sender, EventArgs e)
    {
        string time = "";
        lbl_message.Text = string.Empty;
        int num;

        StringBuilder builder = new StringBuilder();

        builder.Append("<table><tr>");
        if (int.TryParse(tb_number.Text, out num))
        {
            if (num < 0)
                lbl_message.Text = "Please enter a number greater than or equal to 0.";
            else
            {
                int count = 1;
                int number = 0;
                int cols = 11;

                var watch = Stopwatch.StartNew();

                while (count <= num)
                {
                    if (isPrimeNubmer(number))
                    {
                        if (cols > 0)
                        {
                            builder.Append("<td>" + count + " - " + number + "</td>");
                        }
                        else
                        {
                            builder.Append("</tr><tr><td>" + count + " - " + number + "</td>");
                            cols = 11;
                        }
                        count++;
                        number++;
                        cols--;
                    }
                    else
                        number++;
                }
                builder.Append("</table>");
                watch.Stop();
                var elapsedms = watch.ElapsedMilliseconds;
                double seconds = elapsedms / 1000;
                time = seconds.ToString();
                lbl_message.Text = builder.ToString();
                lbl_time.Text = time;
            }
        }
        else
            lbl_message.Text = "Please enter a numberic number.";

        lbl_time.Text = time;

        tb_number.Text = "";
        tb_number.Focus();
    }

Ось код aspx.

<form id="form1" runat="server">
    <div>
        <p>Please enter a number: <asp:TextBox ID="tb_number" runat="server"></asp:TextBox></p>

        <p><asp:Button ID="btn_primeNumbers" runat="server" Text="Show Prime Numbers" OnClick="btn_primeNumbers_Click" />
        </p>
        <p><asp:Label ID="lbl_time" runat="server"></asp:Label></p>
        <p><asp:Label ID="lbl_message" runat="server"></asp:Label></p>
    </div>
</form>

Результати: 10000 простих чисел менш ніж за одну секунду

100000 простих чисел за 63 секунди

Знімок екрану перших 100 простих чисел введіть тут опис зображення


1
Спробувавши це, я міг би оцінити його ефективність та подання результатів: будь ласка, аргументуйте його елегантність.
сіробора

Стиль результату - це лише додаткова частина. Дозвольте мені обговорити алгоритм повернення true / false як просте число. n% 2 усуне половину чисел, тому що парне число завжди ділиться на 2. В іншому коді я ділю лише на непарні числа, збільшуючи ділене на два (так що наступне ділене також непарне) до половини цього числа, яке є простим чи ні. Чому наполовину, щоб не втрачати час, бо це дасть нам відповідь частково.
Різ

log10 (63) ~ = 1,8, тобто ваші дані показують швидкість зростання n ^ 1,8. Це дуже повільно; оптимальне сито реалізацій Ератосфена exibit ~ n ^ 1.01..1.05; оптимальний пробний поділ ~ n ^ 1,35..1,45. Ви isPrimeNubmerсправді реалізуєте неоптимальний поділ трилів; його асимптотика погіршиться приблизно до n ^ 2 (або навіть вище нього), коли ви спробуєте згенерувати ще більше простих чисел.
Will Ness
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.