Cách tao nhã nhất để tạo số nguyên tố [đã đóng]


84

Cách thanh lịch nhất để triển khai chức năng này là gì:

ArrayList generatePrimes(int n)

Hàm này tạo ra các nsố nguyên tố đầu tiên (sửa: where n>1), vì vậy generatePrimes(5)sẽ trả về một ArrayListvới {2, 3, 5, 7, 11}. (Tôi đang làm điều này bằng C #, nhưng tôi hài lòng với việc triển khai Java - hoặc bất kỳ ngôn ngữ tương tự nào khác cho vấn đề đó (không phải Haskell)).

Tôi biết cách viết hàm này, nhưng khi tôi làm nó vào đêm qua, nó không tốt đẹp như tôi hy vọng. Đây là những gì tôi nghĩ ra:

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

Tôi không quá quan tâm đến tốc độ, mặc dù tôi không muốn nó rõ ràng là không hiệu quả. Tôi không bận tâm về phương pháp nào được sử dụng (ngây thơ hoặc sàng lọc hoặc bất cứ điều gì khác), nhưng tôi muốn nó khá ngắn gọn và rõ ràng về cách thức hoạt động.

Chỉnh sửa : Cảm ơn tất cả những người đã trả lời, mặc dù nhiều người đã không trả lời câu hỏi thực sự của tôi. Để nhắc lại, tôi muốn một đoạn mã sạch đẹp tạo ra một danh sách các số nguyên tố. Tôi đã biết cách thực hiện nhiều cách khác nhau, nhưng tôi thường viết mã không rõ ràng như nó có thể. Trong chủ đề này, một số phương án tốt đã được đề xuất:

  • Một phiên bản đẹp hơn của những gì tôi có ban đầu (Peter Smit, jmservera và Rekreativc)
  • Cách thực hiện rất sạch sẽ của sàng Eratosthenes (màu xanh sao)
  • Sử dụng Java BigIntegernextProbablePrimecho mã rất đơn giản, mặc dù tôi không thể tưởng tượng nó đặc biệt hiệu quả (dfa)
  • Sử dụng LINQ để tạo danh sách các số nguyên tố (Maghis) một cách lười biếng
  • Đặt nhiều số nguyên tố trong một tệp văn bản và đọc chúng khi cần thiết (darin)

Chỉnh sửa 2 : Tôi đã triển khai trong C # một vài phương thức được đưa ra ở đây và một phương pháp khác không được đề cập ở đây. Tất cả họ đều tìm thấy n số nguyên tố đầu tiên một cách hiệu quả (và tôi có một phương pháp tốt để tìm giới hạn cung cấp cho các sàng).


12
nope, và nó không phải cho Dự án Euler hoặc :-)
David Johnstone

1
nó sẽ được tốt hơn để tôi retutning IEnumerable <int> và năng suất từng người một
Felice Pollano

4
Điều tôi muốn biết là cách tạo số nguyên tố kém nhất là gì . Tôi đang nghĩ điều gì đó liên quan đến cơ sở dữ liệu Access?
j_random_hacker

1
để so sánh, mã Haskell năm 2008 của BMeph : nubBy (((>1).).gcd) [2..]. Nó chỉ để lại các số tự nhiên không trùng lặp, bắt đầu từ 2, trong khi coi như trùng lặp bất kỳ số gcdnào với bất kỳ số nào được tìm thấy trước đó lớn hơn 1. Nó rất kém hiệu quả, số nguyên tố được tạo ra là bậc hai. Nhưng nó là thanh lịch .
Will Ness

thanh lịch nhất , IMO, là của 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..]) ) }nhưng đó tất nhiên là hoàn toàn dựa trên quan điểm .
Will Ness

Câu trả lời:


48

Sử dụng ước tính

pi(n) = n / log(n)

cho số nguyên tố lên đến n để tìm một giới hạn, rồi dùng một cái rây. Ước lượng đánh giá thấp số lượng số nguyên tố lên đến n một chút, vì vậy sàng sẽ lớn hơn một chút so với mức cần thiết, điều này là ổn.

Đây là sàng Java tiêu chuẩn của tôi, tính toán hàng triệu số nguyên tố đầu tiên trong khoảng một giây trên một máy tính xách tay bình thường:

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
Đó là một thực hiện rất tốt đẹp của các sàng của Eratosthenes
David Johnstone

1
không đủ để lặp lại trong khi i <= Math.sqrt(limit)ở vòng ngoài?
Christoph

1
@David Johnstone Không, pi (n) = n / log (n) đánh giá thấp số lượng số nguyên tố lên đến n, đi theo hướng ngược lại. Tuy nhiên, tôi rất vui vì bạn đã tìm thấy một ước lượng gần đúng hơn nhiều.
starblue,

1
nếu bạn sẵn sàng loại bỏ tất cả các bội số của 2 trong vòng lặp của chính nó, bạn có thể sử dụng j + = 2 * i làm gia số vòng lặp của mình để tiết kiệm thêm một số thời gian chạy và bạn có thể tính toán điều đó một lần bằng cách sử dụng một bit shift
Nick Larsen

5
Bằng cách thay thế BitSetbởi một lớp thực hiện thừa số hóa bánh xe cho 2, 3 và 5, nó sẽ nhanh hơn gần 3 lần.
starblue

37

Rất cám ơn tất cả những người đã đưa ra câu trả lời hữu ích. Dưới đây là cách triển khai của tôi về một số phương pháp khác nhau để tìm n số nguyên tố trong C #. Hai phương pháp đầu tiên là khá nhiều những gì đã được đăng ở đây. (Tên áp phích nằm bên cạnh tiêu đề.) Tôi dự định đôi khi thực hiện sàng lọc Atkin, mặc dù tôi nghi ngờ rằng nó sẽ không đơn giản như các phương pháp ở đây hiện tại. Nếu ai đó có thể thấy bất kỳ cách nào để cải thiện bất kỳ phương pháp nào trong số này, tôi rất muốn biết :-)

Phương pháp chuẩn ( Peter Smit , jmservera , Rekreativc )

Số nguyên tố đầu tiên là 2. Thêm số này vào danh sách các số nguyên tố. Số nguyên tố tiếp theo là số tiếp theo không chia hết cho bất kỳ số nào trong danh sách này.

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

Điều này đã được tối ưu hóa bằng cách chỉ kiểm tra tính chia hết cho đến căn bậc hai của số đang được kiểm tra; và chỉ thử nghiệm các số lẻ. Điều này có thể được tối ưu hóa hơn nữa bằng cách chỉ kiểm tra các số của biểu mẫu 6k+[1, 5]hoặc 30k+[1, 7, 11, 13, 17, 19, 23, 29]hoặc hơn thế nữa .

Rây Eratosthenes ( màu xanh sao )

Điều này tìm thấy tất cả các số nguyên tố đến k . Để lập danh sách n số nguyên tố đầu tiên, trước tiên chúng ta cần tính giá trị gần đúng của số nguyên tố thứ n . Phương pháp sau, như được mô tả ở đây , thực hiện điều này.

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

Sàng Sundaram

Tôi chỉ mới phát hiện ra cái sàng này gần đây, nhưng nó có thể được thực hiện khá đơn giản. Việc thực hiện của tôi không nhanh như sàng của Eratosthenes, nhưng nó nhanh hơn đáng kể so với phương pháp ngây thơ.

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 - Tôi đã phải thay đổi bộ đếm vòng lặp chính của bạn thành "for (int i = 0; i * i <= limit && i * i> 0; i ++)" để tránh bị tràn.
Giải pháp dữ liệu Jacobs

Việc triển khai Sieve of Sundaram này là một trong số rất ít cách thực hiện đúng. Hầu hết trong số họ sử dụng sai giới hạn cho i và j trong khi tính toán i+j+2*i*jdẫn đến kết quả đầu ra không chính xác.
jahackbeth

14

Đang đánh giá lại một câu hỏi cũ, nhưng tôi đã vấp phải nó khi chơi với LINQ.

Mã này yêu cầu .NET4.0 hoặc .NET3.5 Với các tiện ích mở rộng song song

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
Tại sao đây không phải là câu trả lời được chấp nhận? Mã ở đây ngắn hơn, thanh lịch hơn và nhanh hơn nhiều so với mã trong câu trả lời được chấp nhận. Ước gì tôi có thể ủng hộ nhiều hơn một lần!
Avrohom Yisroel

9

Bạn đang trên con đường tốt.

Một vài bình luận

  • số nguyên tố.Add (3); làm cho hàm này không hoạt động đối với number = 1

  • Bạn không cần phải kiểm tra phép chia với các số nguyên tố lớn hơn bình phương của số được kiểm tra.

Mã đề xuất:

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
kiểm tra rằng thủ * Thủ <= curTest trong vòng lặp thay vì trước tính căn bậc hai có thể sẽ làm cho nó nhanh hơn và sẽ làm cho nó chung chung hơn (sẽ làm việc cho bignums, vv)
yairchu

Tại sao sử dụng căn bậc hai? Nền tảng toán học cho tùy chọn như vậy là gì? Tôi, có lẽ thẫn thờ, chỉ có thể chia hết cho 2.
Luis Filipe

3
Bởi vì nếu một số có thừa số nguyên tố thì ít nhất một trong số chúng phải nhỏ hơn hoặc bằng căn bậc hai. Nếu a * b = c và a <= b thì a <= sqrt (c) <= b.
David Johnstone

8

bạn nên xem xét các số nguyên tố có thể xảy ra . Đặc biệt, hãy xem các Thuật toán ngẫu nhiênkiểm tra tính nguyên thủy Miller – Rabin .

Để hoàn thiện, bạn chỉ có thể sử dụng 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 rất nhanh và mã rất đơn giản. Cung cấp cho nó đủ số lần lặp lại khiến nó đủ tin cậy để cạnh tranh với lỗi CPU ngẫu nhiên về khả năng dương tính giả. Nhược điểm của thuật toán là hiểu tại sao nó thực sự hoạt động là một nhiệm vụ khó khăn.
Brian

6

Không có nghĩa là hiệu quả, nhưng có thể dễ đọc nhất:

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

với:

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

Thực tế chỉ là một biến thể của một số bài đăng ở đây với định dạng đẹp hơn.


5

Bản quyền 2009 của St.Wittum 13189 Berlin GERMANY theo Giấy phép CC-BY-SA https://creativecommons.org/licenses/by-sa/3.0/

Cách đơn giản nhưng thanh lịch nhất để tính TẤT CẢ CÁC NGUYÊN TỬ sẽ là cách này, nhưng cách này chậm và chi phí bộ nhớ cao hơn nhiều đối với các số cao hơn vì sử dụng hàm khoa (!) ... nhưng nó cho thấy một biến thể của Định lý Wilson trong một ứng dụng tạo tất cả các số nguyên tố bằng thuật toán được triển khai trong 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

Sử dụng trình tạo số nguyên tố để tạo primes.txt và sau đó:

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

Trong trường hợp này, tôi sử dụng Int16 trong chữ ký phương thức, vì vậy tệp primes.txt của tôi chứa các số từ 0 đến 32767. Nếu bạn muốn mở rộng tệp này thành Int32 hoặc Int64, tệp primes.txt của bạn có thể lớn hơn đáng kể.


3
Trích dẫn OP: "Tôi không bận tâm đến phương pháp nào được sử dụng (ngây thơ hoặc sàng lọc hoặc bất cứ điều gì khác), nhưng tôi muốn nó khá ngắn gọn và rõ ràng về cách thức hoạt động". Tôi nghĩ rằng câu trả lời của tôi là hoàn toàn phù hợp. Nó cũng là phương pháp nhanh nhất.
Darin Dimitrov

14
Ngay cả khi anh ấy nói "Tôi không ngại phương pháp nào ..." Tôi không nghĩ rằng điều đó bao gồm "mở danh sách các số nguyên tố". Điều đó cũng giống như trả lời câu hỏi "làm thế nào để xây dựng một máy tính" bằng cách "mua một máy tính". -1
stevenvh

8
Sẽ nhanh hơn nếu bạn thực sự viết các số nguyên tố trong chính mã nguồn, thay vì đọc chúng từ một tệp.
Daniel Daranas

3
Tiêu tốn nhiều bộ nhớ? hơn là đọc toàn bộ danh sách số nguyên tố dưới dạng văn bản vào ... bộ nhớ? Bạn có biết chuỗi hoạt động như thế nào trong .net không?
jmservera

6
Danh sách các số nguyên tố là một danh sách vô hạn nhưng bất biến, vì vậy việc sử dụng một danh sách được tính toán trước cho đến giới hạn trên cho ứng dụng là rất hợp lý. Tại sao phải lãng phí thời gian để viết mã có thể bị lỗi khi có một danh sách công khai chính xác có thể được sử dụng để đáp ứng các yêu cầu.
AndyM

4

Tôi có thể đưa ra giải pháp C # sau đây. Nó không có nghĩa là nhanh, nhưng nó rất rõ ràng về những gì nó làm.

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

Tôi đã bỏ qua bất kỳ kiểm tra nào - nếu giới hạn là âm hoặc nhỏ hơn hai (hiện tại, phương thức này sẽ luôn trả về ít nhất hai dưới dạng số nguyên tố). Nhưng đó là tất cả dễ dàng để sửa chữa.

CẬP NHẬT

Xem hai phương pháp mở rộng sau

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

bạn có thể viết lại nó như sau.

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

Nó kém hiệu quả hơn (vì căn bậc hai được đánh giá lại khá thường xuyên) nhưng nó thậm chí còn là mã sạch hơn. Có thể viết lại mã để liệt kê các số nguyên tố một cách lười biếng, nhưng điều này sẽ làm mã lộn xộn một chút.


Tôi gần như khẳng định rằng việc tính toán căn bậc hai được tối ưu hóa bởi trình biên dịch JIT (khi được biên dịch với tối ưu hóa được bật). Bạn sẽ phải xác minh điều này bằng cách kiểm tra assembly được tạo (IL chỉ được tối ưu hóa một phần và không ở gần mức tối ưu hóa được thực hiện bởi trình biên dịch JIT. Thời gian lặp lại vòng lặp và tối ưu hóa vi mô khác đã qua. Thực tế, đôi khi cố gắng thông minh hơn JIT có thể làm chậm mã của bạn.
Dave Black

4

Đây là một triển khai của Sieve of Eratosthenes trong 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
    }

tôi muốn làm điều đó với một bool thay vì enum ...
Letterman

3

Sử dụng cùng một thuật toán của bạn, bạn có thể thực hiện nó ngắn hơn một chút:

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

Tôi biết bạn đã yêu cầu giải pháp không phải Haskell nhưng tôi đang bao gồm điều này ở đây vì nó liên quan đến câu hỏi và Haskell cũng rất đẹp cho loại thứ này.

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

Vâng, tôi là một fan hâm mộ lớn của Haskell quá (tôi chỉ muốn tôi biết nó tốt hơn)
David Johnstone

3

Tôi đã viết một triển khai Eratosthenes đơn giản trong c # bằng cách sử dụng một số LINQ.

Thật không may, LINQ không cung cấp chuỗi int vô hạn, vì vậy bạn phải sử dụng int.MaxValue :(

Tôi đã phải lưu vào bộ nhớ cache trong một loại vô danh mà sqrt ứng cử viên cần tránh để tính toán nó cho mỗi số nguyên tố được lưu trong bộ nhớ cache (trông hơi xấu xí).

Tôi sử dụng danh sách các số nguyên tố trước đó cho đến sqrt của ứng cử viên

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

và kiểm tra mọi Int bắt đầu từ 2 so với nó

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

Đây là mã:

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

Một cách tối ưu khác là tránh kiểm tra số chẵn và chỉ trả về 2 trước khi tạo Danh sách. Bằng cách này nếu phương thức gọi chỉ yêu cầu 1 số nguyên tố, nó sẽ tránh được tất cả sự lộn xộn:

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
Tôi ước mình biết LINQ đủ để đánh giá cao và hiểu câu trả lời này tốt hơn :-) Ngoài ra, tôi có cảm giác rằng đây không phải là một triển khai của sàng Eratosthenes và nó hoạt động về mặt khái niệm giống như chức năng ban đầu của tôi (tìm phần tiếp theo số không chia hết cho bất kỳ số nguyên tố nào đã tìm thấy trước đó).
David Johnstone

Đúng, nhưng "tìm số tiếp theo không chia hết cho bất kỳ số nguyên tố nào đã tìm thấy trước đó (số sau đó nhỏ hơn)" về mặt khái niệm tương tự như sàng eratosthenes. Nếu bạn thích, tôi có thể cấu trúc lại nó một chút để làm cho nó dễ đọc hơn ngay cả khi bạn không quen thuộc với LINQ. Bạn có quen thuộc với các trình vòng lặp?
Maghis

Điều tôi thích của cách tiếp cận này là số nguyên tố tiếp theo được tính ngay khi người gọi yêu cầu nó, vì vậy những thứ như "lấy n số nguyên tố đầu tiên" hoặc "lấy các số nguyên tố nhỏ hơn n" trở nên tầm thường
Maghis

1
Cảm ơn, nhưng tôi có thể hiểu điều đó đủ để ít nhiều biết nó đang làm gì :-) Tôi thích đánh giá lười biếng, nhưng tôi vẫn sẽ không gọi đây là một triển khai của sàng Eratosthenes.
David Johnstone

1

Để làm cho nó thanh lịch hơn, bạn nên cấu trúc lại bài kiểm tra IsPrime của mình thành một phương pháp riêng biệt và xử lý vòng lặp và số gia bên ngoài đó.


1

Tôi đã làm điều đó trong Java bằng cách sử dụng thư viện chức năng mà tôi đã viết, nhưng vì thư viện của tôi sử dụng các khái niệm tương tự như Enumerations, tôi chắc chắn rằng mã có thể thích ứng:

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

Đây là cách tao nhã nhất mà tôi có thể nghĩ đến trong thời gian ngắn.

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

Hy vọng điều này sẽ giúp cung cấp cho bạn một ý tưởng. Tôi chắc rằng điều này có thể được tối ưu hóa, tuy nhiên, nó sẽ cho bạn ý tưởng về cách phiên bản của bạn có thể trở nên thanh lịch hơn.

CHỈNH SỬA: Như đã lưu ý trong các nhận xét, thuật toán này thực sự trả về các giá trị sai cho numberToGenerate <2. Tôi chỉ muốn chỉ ra rằng tôi đã không cố gắng đăng cho anh ta một phương pháp tuyệt vời để tạo số nguyên tố (hãy xem câu trả lời của Henri cho điều đó), Tôi đã tận tình chỉ ra cách làm thế nào để phương pháp của anh ấy trở nên thanh lịch hơn.


3
Cái này trả về một kết quả sai cho numberToGenerate <2
Peter Smit

Điều này đúng, tuy nhiên tôi không thiết kế một thuật toán, tôi chỉ chỉ cho anh ấy thấy phương pháp của anh ấy có thể trở nên thanh lịch hơn như thế nào. Vì vậy, phiên bản này cũng sai tương tự như phiên bản trong câu hỏi mở đầu.
David Božjak

1
Tôi không nghĩ rằng nó đã bị hỏng vì n = 1. Tôi đã thay đổi câu hỏi hơi để các chức năng chỉ có làm việc cho n> 1 :-)
David Johnstone

điều này thừa nhận bình phương của số nguyên tố là số nguyên tố.
Will Ness

1

Sử dụng lập trình dựa trên luồng trong Java hàm , tôi đã nghĩ ra những điều sau. Kiểu Naturalcơ bản là 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()));

Bây giờ bạn có một giá trị mà bạn có thể mang theo, đó là một dòng số nguyên tố vô hạn. Bạn có thể làm những việc như sau:

// 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));

Giải thích về cái sàng:

  1. Giả sử số đầu tiên trong luồng đối số là số nguyên tố và đặt nó ở đầu luồng trả về. Phần còn lại của luồng trả về là một phép tính chỉ được tạo ra khi được yêu cầu.
  2. Nếu ai đó yêu cầu phần còn lại của luồng, hãy gọi sàng trên phần còn lại của luồng đối số, lọc ra các số chia hết cho số đầu tiên (phần còn lại của phép chia là 0).

Bạn cần có các lần nhập sau:

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

Cá nhân tôi nghĩ rằng đây là một triển khai (Java) khá ngắn gọn và sạch sẽ:

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

Hãy thử truy vấn LINQ này, nó tạo ra các số nguyên tố như bạn mong đợi

        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

Đây là một ví dụ về mã python in ra tổng của tất cả các số nguyên tố dưới hai triệu:

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

Phương pháp đơn giản nhất là thử và sai: bạn thử nếu bất kỳ số nào từ 2 đến n-1 chia số nguyên tố ứng viên của bạn là n.
Các phím tắt đầu tiên tất nhiên là a) bạn chỉ phải kiểm tra các số lẻ, và b) bạn chỉ kiểm tra các dấu chia cho đến sqrt (n).

Trong trường hợp của bạn, khi bạn cũng tạo ra tất cả các số nguyên tố trước đó trong quá trình này, bạn chỉ phải kiểm tra xem có bất kỳ số nguyên tố nào trong danh sách của bạn, lên đến sqrt (n), chia cho n hay không.
Sẽ là nhanh nhất bạn có thể nhận được cho số tiền của bạn :-)

chỉnh sửa
Ok, mã, bạn đã yêu cầu nó. Nhưng tôi đang cảnh báo bạn :-), đây là mã Delphi 5 phút nhanh và bẩn:

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
Và làm thế nào để bạn thể hiện điều này trong mã? :-)
David Johnstone

0

Để tìm ra 100 số nguyên tố đầu tiên, có thể xem xét mã java sau.

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

Tôi có được điều này nhờ lần đầu tiên đọc "Sieve of Atkin" trên Wikki cộng với một số suy nghĩ trước đó mà tôi đã đưa ra cho điều này - tôi dành rất nhiều thời gian để viết mã từ đầu và hoàn toàn không nhận ra mọi người chỉ trích việc viết mã giống như trình biên dịch của tôi. phong cách + Tôi thậm chí chưa thực hiện nỗ lực đầu tiên để chạy mã ... rất nhiều mô hình mà tôi đã học cách sử dụng ở đây, chỉ cần đọc và khóc, hãy lấy những gì bạn có thể.

Hãy hoàn toàn & hoàn toàn chắc chắn để thực sự kiểm tra tất cả những điều này trước khi sử dụng, chắc chắn không cho bất kỳ ai xem - đó là để đọc và xem xét các ý tưởng. Tôi cần làm cho công cụ cơ bản hoạt động vì vậy đây là nơi tôi bắt đầu mỗi khi tôi phải làm việc gì đó.

Nhận một bản biên dịch sạch, sau đó bắt đầu loại bỏ những gì bị lỗi - tôi có gần 108 triệu lần nhấn phím mã có thể sử dụng làm theo cách này, ... hãy sử dụng những gì bạn có thể.

Tôi sẽ làm việc trên phiên bản của tôi vào ngày mai.

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

Hãy thử mã này.

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

Đây là mã 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>

Kết quả: 10000 Số nguyên tố trong vòng chưa đầy một giây

100000 số nguyên tố trong 63 giây

Ảnh chụp màn hình của 100 số nguyên tố đầu tiên nhập mô tả hình ảnh ở đây


1
Dùng thử nó, tôi có thể đánh giá tính hiệu quả của nó và cách trình bày các kết cấu: hãy tranh luận về độ sang trọng của nó.
greybeard,

Kiểu dáng của kết quả chỉ là một phần được thêm vào. Hãy để tôi thảo luận về thuật toán trả về true / false dưới dạng số nguyên tố. n% 2 sẽ loại bỏ một nửa số vì số chẵn luôn chia hết cho 2. Trong mã khác, tôi chỉ chia cho số lẻ, tăng chia hết cho hai (vì vậy số chia tiếp theo cũng là số lẻ) lên đến một nửa số đó là số nguyên tố hay không. Tại sao một nửa, để không lãng phí thời gian vì nó sẽ cho chúng ta câu trả lời dưới dạng phân số.
riz

log10 (63) ~ = 1,8, tức là dữ liệu của bạn hiển thị tốc độ tăng trưởng n ^ 1,8. Điều này là rất chậm; sàng tối ưu của các triển khai Eratosthenes exibit ~ n ^ 1,01..1,05; phép chia tối ưu ~ n ^ 1.35..1.45. isPrimeNubmerThực sự của bạn thực hiện phép chia tril tối ưu; asymptotycs của nó sẽ xấu đi khoảng n ^ 2 (hoặc thậm chí cao hơn nó) khi bạn cố gắng tạo ra nhiều số nguyên tố hơn nữa.
Will Ness
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.