소수를 생성하는 가장 우아한 방법 [닫힌]


84

이 기능을 구현하는 가장 우아한 방법은 무엇입니까?

ArrayList generatePrimes(int n)

이 함수는 첫 번째 n소수 (편집 : where n>1)를 생성하므로 with generatePrimes(5)를 반환합니다 . (저는 C #에서이 작업을 수행하고 있지만 Java 구현 또는 다른 유사한 언어 (하스켈이 아님)에 만족합니다).ArrayList{2, 3, 5, 7, 11}

이 함수를 작성하는 방법을 알고 있지만 어제 밤에했을 때 내가 기대했던 것만 큼 멋지지 않았습니다. 내가 생각해 낸 것은 다음과 같습니다.

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;
}

분명히 비효율적이기를 원하지는 않지만 속도에 대해서는 너무 걱정하지 않습니다. 나는 어떤 방법이 사용되는지 (순진하거나 체 또는 다른 것) 상관하지 않지만, 그것이 어떻게 작동하는지 상당히 짧고 분명하기를 원합니다.

편집 : 많은 사람들이 내 실제 질문에 대답하지 않았지만 응답 해 주신 모든 분들께 감사드립니다. 다시 말해서, 소수 목록을 생성하는 깔끔한 코드를 원했습니다. 나는 이미 여러 가지 방법을 알고 있지만 명확하지 않은 코드를 작성하는 경향이 있습니다. 이 스레드에서 몇 가지 좋은 옵션이 제안되었습니다.

  • 원래 가지고 있던 것의 더 좋은 버전 (Peter Smit, jmservera 및 Rekreativc)
  • Eratosthenes (starblue) 체의 매우 깨끗한 구현
  • 특히 효율적이라고 상상할 수는 없지만 (dfa) Java의 BigIntegernextProbablePrime매우 간단한 코드를 사용하십시오.
  • LINQ를 사용하여 소수 목록 (Maghis)을 느리게 생성
  • 많은 소수를 텍스트 파일에 넣고 필요할 때 읽습니다 (다린).

편집 2 : 여기에 제공된 두 가지 방법과 여기에 언급되지 않은 다른 방법 을 C # 으로 구현했습니다 . 그들은 모두 처음 n 개의 소수를 효과적으로 찾습니다 (그리고 체에 제공 할 한계를 찾는 적절한 방법 이 있습니다).


12
아니요, 프로젝트 오일러도 마찬가지입니다. :-)
David Johnstone

1
그것은 retutning를 IEnumerable <INT> 하나 하나 양보 나에게 더 나은 위해 것
펠리스 Pollano

4
내가 알고 싶은 것은 소수를 생성 하는 가장 우아한 방법 이 무엇인지입니다 . Access 데이터베이스와 관련된 것을 생각하고 있습니까?
j_random_hacker

1
비교를 위해 BMeph2008 Haskell 코드 : nubBy (((>1).).gcd) [2..]. 2부터 시작하는 자연수 중 중복되지 않은 숫자 만 남기고 gcd, 이전에 발견 된 숫자 중 하나가 1보다 큰 숫자는 중복으로 간주 합니다. 생성 된 소수의 수가 매우 비효율적이고 2 차입니다. 그러나 그것은 우아 합니다.
Will Ness

가장 우아한 IMO는 Haskell의 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..]) ) }것이지만 물론 전적으로 의견을 기반으로 합니다.
Will Ness

답변:


48

견적 사용

pi(n) = n / log(n)

n까지 소수의 수에 대해 한계를 찾은 다음 체를 사용하십시오. 추정치는 n까지 소수의 수를 다소 과소 평가하므로 체가 필요한 것보다 약간 더 커질 것입니다.

이것은 내 표준 Java sieve이며 일반 랩톱에서 약 1 초 만에 처음 백만 개의 소수를 계산합니다.

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
그것은 Eratosthenes의 체의 아주 멋진 구현입니다
David Johnstone

1
i <= Math.sqrt(limit)외부 루프에있는 동안 루프하는 것으로 충분하지 않습니까?
Christoph

1
@David Johnstone 아니요, pi (n) = n / log (n)은 반대 방향으로가는 n까지 소수의 수를 과소 평가합니다. 그래도 훨씬 더 좋은 근사값을 찾아서 다행입니다.
starblue 2009-07-04

1
자체 루프에서 2의 배수를 모두 제거하려면 j + = 2 * i를 루프 증분으로 사용하여 추가 런타임을 절약 할 수 있으며 비트 시프트를 사용하여 한 번 계산할 수 있습니다
Nick Larsen

5
BitSet2, 3 및 5에 대한 휠 인수 분해를 구현하는 클래스 로 대체 하면 거의 3 배 빨라집니다.
starblue

37

도움을 주신 모든 분들께 감사드립니다. 다음은 C #에서 처음 n 개의 소수 를 찾는 몇 가지 다른 방법의 구현입니다 . 처음 두 가지 방법은 여기에 게시 된 것과 거의 같습니다. (포스터 이름은 제목 옆에 있습니다.) 현재의 방법만큼 간단하지는 않지만 Atkin의 체를 만들 계획입니다. 누구나 이러한 방법을 개선하는 방법을 볼 수 있다면 알고 싶습니다. :-)

표준 방법 ( Peter Smit , 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]또는 등등 .

에라토스테네스의 체 ( starblue )

이것은 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;
}

순 다람의 체

최근에 야이 체를 발견 했지만 아주 간단하게 구현할 수 있습니다. 내 구현은 Eratosthenes의 체만큼 빠르지는 않지만 순진한 방법보다 훨씬 빠릅니다.

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;
}

참고로-오버플로를 방지하기 위해 메인 루프 카운터를 "for (int i = 0; i * i <= limit && i * i> 0; i ++)"로 변경해야했습니다.
Jacobs Data Solutions

이 Sundaram의 Sieve 구현은 몇 안되는 올바른 구현 중 하나입니다. 대부분은 i와 j에 대해 잘못된 경계를 사용하여 계산 i+j+2*i*j하는 동안 잘못된 출력으로 이어집니다.
jahackbeth

15

오래된 질문을 구제했지만 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

당신은 좋은 길을 가고 있습니다.

일부 댓글

  • primes.Add (3); 이 함수는 number = 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를 테스트하면 아마도 속도가 더 빨라지고 더 일반적으로 만들 것입니다 (큰
숫자

왜 제곱근을 사용합니까? 그러한 옵션의 수학적 배경은 무엇입니까? 나는, 아마 둔하게 만 2로 나누는 것
루이스 필리페에게

3
숫자에 소인수가 있으면 그 중 적어도 하나는 제곱근보다 작거나 같아야합니다. a * b = c이고 a <= b이면 a <= sqrt (c) <= b입니다.
David Johnstone

8

가능한 소수를 살펴 봐야 합니다. 특히 Randomized AlgorithmsMiller-Rabin 소수성 테스트를 살펴보십시오 .

완전성을 위해 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
Miller-Rabbin은 매우 빠르고 코드는 매우 간단합니다. 충분한 반복을 제공하면 오탐 가능성 측면에서 임의의 CPU 오류와 경쟁 할 수있을만큼 신뢰할 수 있습니다. 알고리즘의 단점은 실제로 작동하는 이유를 이해하는 것이 어려운 작업이라는 것입니다.
Brian

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

Copyrights 2009 by St. Wittum 13189 Berlin GERMANY under CC-BY-SA License https://creativecommons.org/licenses/by-sa/3.0/

모든 프라임을 계산하는 간단하지만 가장 우아한 방법은 이것이 겠지만,이 방법은 느리고 교수 (!) 함수를 사용하기 때문에 메모리 비용이 훨씬 높지만 응용 프로그램에서 Wilson Theoreme의 변형을 보여줍니다. 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를 인용하면 : "어떤 방법이 사용되는지 (순진하거나 체 또는 다른 것) 신경 쓰지 않지만, 그것이 어떻게 작동하는지 상당히 짧고 분명하게하고 싶습니다". 내 대답은 완벽하게 관련이 있다고 생각합니다. 또한 가장 빠른 방법입니다.
Darin Dimitrov

14
그가 "어느 방법은 상관 없어 ..."라고 말하더라도 "소수 목록 열기"는 포함되지 않는다고 생각합니다. 그것은 "컴퓨터 구입"으로 "컴퓨터를 만드는 방법"에 대한 질문에 대답하는 것과 같습니다. -1
stevenvh 2009-06-25

8
소수를 파일에서 읽는 대신 소스 코드 자체에 실제로 작성하면 더 빠를 것입니다.
Daniel Daranas

3
많은 메모리를 소비합니까? 전체 소수 목록을 텍스트로 읽는 것 이상으로 ... 메모리? .net에서 문자열이 어떻게 작동하는지 알고 있습니까?
jmservera 2009-06-25

6
소수 목록은 무한하지만 변경할 수없는 목록이므로 응용 프로그램의 가능한 상한까지 미리 계산 된 목록을 사용하는 것이 좋습니다. 요구 사항을 충족하는 데 사용할 수있는 올바른 공개 목록이있을 때 버그가있을 수있는 코드를 작성하는 데 시간을 낭비하는 이유.
AndyM 09-06-25

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;
}

제한이 음수이거나 2보다 작 으면 수표를 생략했습니다 (현재 메서드는 항상 적어도 2 개를 소수로 반환합니다). 그러나 그것은 모두 쉽게 고칠 수 있습니다.

최신 정보

다음 두 가지 확장 방법으로

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 컴파일러 (최적화를 활성화 한 상태로 컴파일 할 때)에 의해 최적화된다는 사실은 거의 긍정적입니다. 생성 된 어셈블리를 검사하여이를 확인해야합니다 (IL은 부분적으로 만 최적화되고 JIT 컴파일러가 수행하는 최적화에 가깝지 않습니다. 루프 호이 스팅 및 기타 마이크로 최적화의 시대는 끝났습니다. 사실, 때로는 JIT를 능가하면 코드 속도느려질 수 있습니다
Dave Black

4

다음 은 C #에서 Sieve of Eratosthenes 를 구현 한 것입니다 .

    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
    }

enum 대신 bool을 사용하여 수행합니다.
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

나는 당신이 Haskell이 아닌 솔루션을 요청했다는 것을 알고 있지만 질문과 관련하여 여기에 포함하고 있으며 Haskell은 이러한 유형의 일에 아름답습니다.

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

그래, 나도 Haskell의 열렬한 팬이다 (난 더 잘 알았 으면 좋겠다)
David Johnstone

3

LINQ를 사용하여 C #으로 간단한 Eratosthenes 구현을 작성했습니다.

불행히도 LINQ는 무한 시퀀스의 int를 제공하지 않으므로 int.MaxValue :(

캐시 된 각 프라임에 대해 계산하는 것을 피하기 위해 후보 sqrt를 익명 유형으로 캐시해야했습니다 (조금 못 생겼습니다).

후보의 sqrt까지 이전 소수 목록을 사용합니다.

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

2부터 시작하는 모든 Int를 확인하십시오.

.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를 충분히 알고 있었으면 좋겠습니다. 이전에 발견 된 소수로 나눌 수없는 숫자).
David Johnstone

예,하지만 "이전에 발견 된 소수 (숫자보다 작음)로 나눌 수없는 다음 숫자 찾기"는 개념적으로 에라토스테네스의 체와 유사합니다. 원하는 경우 LINQ에 익숙하지 않더라도 좀 더 읽기 쉽게 리팩터링 할 수 있습니다. 반복자에 대해 잘 알고 있습니까?
Maghis 2009-06-25

이 접근 방식에서 내가 좋아하는 점은 호출자가 요청할 때 다음 소수가 계산된다는 것입니다. 따라서 "처음 n 개의 소수를 취하십시오"또는 "n보다 작은 소수를 취하십시오"와 같은 것은 사소 해집니다
Maghis

1
감사합니다.하지만 저는 그것이 무엇을하고 있는지 어느 정도 알 수 있다는 것을 이해할 수 있습니다. :-) 저는 게으른 평가를 좋아하지만 여전히 이것을 에라토스테네스 체의 구현이라고 부르지 않습니다.
David Johnstone

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;
}

이것이 당신에게 아이디어를주는 데 도움이되기를 바랍니다. 나는 이것이 최적화 될 수 있다고 확신하지만, 당신의 버전을 어떻게 더 우아하게 만들 수 있는지에 대한 아이디어를 줄 것입니다.

편집 : 주석에서 언급 했듯이이 알고리즘은 실제로 numberToGenerate <2에 대해 잘못된 값을 반환합니다. 나는 그에게 소수를 생성하는 훌륭한 방법을 게시하려고하지 않았 음을 지적하고 싶습니다 (Henri의 대답을보십시오). 나는 그의 방법이 어떻게 더 우아해질 수 있는지 겨우 지적하고 있었다.


3
이것은 numberToGenerate <2에 대해 잘못된 결과를 반환합니다
Peter Smit

이것은 사실이지만 알고리즘을 설계 한 것이 아니라 그의 방법을 더 우아하게 만들 수있는 방법을 그에게 보여주고있었습니다. 따라서이 버전은 시작 질문의 버전과 똑같이 잘못되었습니다.
David Božjak 2009-06-25

1
n = 1에 대해 고장난 것은 나에게 발생하지 않았습니다. 함수가 n> 1에서만 작동하도록 질문을 약간 변경했습니다. :-)
David Johnstone

이것은 소수의 제곱을 소수로 인정합니다.
Will Ness 2013

1

Functional Java 에서 스트림 기반 프로그래밍을 사용하여 다음을 생각해 냈습니다. 유형 Natural은 본질적으로 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. 누군가가 나머지 스트림을 요청하면 나머지 인수 스트림에 대해 sieve를 호출하여 첫 ​​번째 숫자로 나눌 수있는 숫자를 필터링합니다 (나머지 나눗셈은 0 임).

다음 가져 오기가 필요합니다.

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

다음은 2 백만 미만의 모든 소수의 합을 출력하는 파이썬 코드 예제입니다.

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 사이의 숫자가 후보 소수 n을 나누면 시도합니다.
첫 번째 단축키는 물론 a) 홀수 만 확인하면되고 b) sqrt (n)까지의 구분선 만 확인하면됩니다.

프로세스에서 이전 소수를 모두 생성하는 경우에는 목록에있는 소수 중 sqrt (n)까지 n을 나누는 것이 있는지 확인하기 만하면됩니다.
당신의 돈을 위해 얻을 수있는 가장 빠른 것이어야합니다 :-)

수정
좋아, 코드, 당신이 그것을 요청했습니다. 그러나 나는 당신에게 경고합니다 :-), 이것은 5 분 빠르고 더러운 델파이 코드입니다 :

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
그리고 이것을 코드로 어떻게 표현합니까? :-)
David Johnstone

0

처음 100 개의 소수를 찾으려면 다음 자바 코드를 고려할 수 있습니다.

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

나는 Wikki에서 "Sieve of Atkin"을 처음 읽었을뿐 아니라 이것에 대해 몇 가지 사전 생각을했습니다. 저는 처음부터 코딩하는 데 많은 시간을 소비하고 사람들이 제 컴파일러와 같은 매우 조밀 한 코딩을 비판하는 것을 완전히 제로화합니다. 스타일 + 코드를 실행하려는 첫 번째 시도도하지 않았습니다. 제가 사용하는 방법을 배운 많은 패러다임이 여기에 있습니다. 읽고 울면서 할 수있는 것을 얻으십시오.

이 모든 것을 사용하기 전에 절대적으로 확실하게 테스트하고 누구에게도 보여주지 마십시오. 아이디어를 읽고 고려하기위한 것입니다. 나는 소수 도구가 작동하도록해야한다. 그래서 이것이 내가 무언가를해야 할 때마다 시작하는 곳이다.

깨끗한 컴파일을 한 다음 결함이있는 것을 제거하기 시작합니다. 저는 거의 1 억 8 백만 번의 사용 가능한 코드 키 입력이 이런 식으로 수행됩니다.

내일 내 버전 작업을 할 것입니다.

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>

결과 : 1 초 이내에 10000 개의 소수

63 초에 100000 개의 소수

처음 100 개의 소수 스크린 샷 여기에 이미지 설명 입력


1
그것을 시도해 보면 그 효과와 결과의 표현을 측정 할 수있었습니다. 그 우아함을 주장하십시오.
greybeard

결과 스타일링은 추가 된 부분 일뿐입니다. 참 / 거짓을 소수로 반환하는 알고리즘에 대해 설명하겠습니다. n % 2는 짝수가 항상 2로 나눌 수 있기 때문에 숫자의 절반을 제거합니다. 그렇지 않으면 홀수로만 나눕니다. 다른 코드에서는 2로 나눌 수 있도록 증가합니다 (그러므로 다음 나눌 수있는 것도 홀수). 또는 아닙니다. 시간을 낭비하지 않는 이유는 반 정도입니다.
riz

log10 (63) ~ = 1.8, 즉 데이터 가 n ^ 1.8의 성장률을 보여줍니다 . 이것은 매우 느립니다. 에라토스테네스 구현의 최적 체 exibit ~ n ^ 1.01..1.05; 최적 시도 분할 ~ n ^ 1.35..1.45. 당신은 isPrimeNubmer실제로 차선의 tril 부문을 구현합니다. 더 많은 소수를 생성하려고하면 점근선이 약 n ^ 2 (또는 그 이상)로 악화됩니다.
Will Ness
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.