簡體   English   中英

使用LINQ生成素數

[英]Using LINQ to generate prime numbers

以下是面試問題:

以下單行生成並顯示前500個素數的列表。 如何使用並行LINQ優化它,同時仍保持單個C#語句:

MessageBox.Show(string.Join(",", 
    Enumerable.Range(2, (int)(500 * (Math.Log(500) + Math.Log(System.Math.Log(500)) - 0.5)))
                .Where(x => Enumerable.Range(2, x - 2)
                                      .All(y => x % y != 0))
                .TakeWhile((n, index) => index < 500)));

我嘗試將AsParallel()以及ParallelEnumerable引入查詢,但沒有看到多核機器的任何實際好處。 查詢仍然使用一個CPU核心,而其他核心享受休閑時間。 有人可以提出一項改進措施,將負載平均分配到所有內核上,從而縮短執行時間嗎?

對於發燒友 :以下公式返回一個上限,保證大於N個素數,即如果你檢查這個數字,你肯定會發現小於它的N個素數:

UpperBound = N * (Log(N) + Log(Log(N)) - 0.5) //Log is natural log

這在我的機器上做得很好。 到目前為止,我從來沒有真正看到我的所有內核都達到100%。 謝謝你給我借口玩:)

我增加了數字,直到我有足夠的時間測量(20,000)。

對我有所幫助的關鍵選項是將ExecutionMode設置為ForceParallelism。

因為我使用NotBuffered合並選項,所以當我完成時我會對它進行重新排序。 如果您不關心結果的順序(也許您將結果放在HashSet中),則不需要這樣做。

DegreeOfParallelism和MergeOptions僅為我的機器上的性能提供了微小的收益(如果有的話)。 此示例顯示如何在單個Linq語句中使用所有選項,這是原始問題。

var numbers = Enumerable.Range(2, (int)(20000 * (Math.Log(20000) + Math.Log(System.Math.Log(20000)) - 0.5)))
                .AsParallel()
                .WithDegreeOfParallelism(Environment.ProcessorCount) 
                .WithExecutionMode(ParallelExecutionMode.ForceParallelism)
                .WithMergeOptions(ParallelMergeOptions.NotBuffered) // remove order dependancy
                .Where(x => Enumerable.Range(2, x - 2)
                                      .All(y => x % y != 0))
                .TakeWhile((n, index) => index < 20000);
string result = String.Join(",",numbers.OrderBy (n => n));

你只能檢查有價值的SQRT(從上面升級的代碼)

var numbers = new[] {2, 3}.Union(Enumerable.Range(2, (int) (i*(Math.Log(i) + Math.Log(Math.Log(i)) - 0.5)))
                                           .AsParallel()
                                           .WithDegreeOfParallelism(Environment.ProcessorCount)
                                           // 8 cores on my machine
                                           .WithExecutionMode(ParallelExecutionMode.ForceParallelism)
                                           .WithMergeOptions(ParallelMergeOptions.NotBuffered)
                                           // remove order dependancy
                                           .Where(x => Enumerable.Range(2, (int) Math.Ceiling(Math.Sqrt(x)))
                                                                 .All(y => x%y != 0))
                                           .TakeWhile((n, index) => index < i))
                          .ToList();

但是當你有一個簡單而極其快速的諷刺時,它會很瘋狂:

private static IEnumerable<int> GetPrimes(int k)
{
    int n = (int)Math.Ceiling((k * (Math.Log(k) + Math.Log(Math.Log(k)) - 0.5)));
    bool[] prime = new bool[n + 1];
    prime[0] = prime[1] = false;
    for (int i = 2; i < prime.Length; i++)
    {
        prime[i] = true;
    }
    for (int i = 2; i*i <= n; ++i) // valid for n < 46340^2 = 2147395600
        if (prime[i])
        {
            for (int j = i*i; j <= n; j += i)
                prime[j] = false;
            yield return i;
        }
}

當然它不如LINQ好,因為它不是解決問題的時髦方法,但你應該知道它存在。

Stopwatch t = new Stopwatch();
            t.Start();
            var numbers = Enumerable.Range(2, (int)(500 * (Math.Log(500) + Math.Log(System.Math.Log(500)) - 0.5)))
                .Where(x => Enumerable.Range(2, x - 2)
                                      .All(y => x % y != 0))
                .TakeWhile((n, index) => index < 500);
            t.Stop();
            MessageBox.Show(t.ElapsedMilliseconds.ToString());
            MessageBox.Show(string.Join(",", numbers));

它在3毫秒內進行評估。 好的linq查詢。

對於未來的讀者來說,這就是我最終的結果。 它很快。 在我不起眼的機器上,它會在一秒鍾內生成前20,000個素數的列表。

Enumerable.Range(5, (int)(N * (Math.Log(N) + Math.Log(System.Math.Log(N)) - 0.5)))
            .AsParallel()
            .WithDegreeOfParallelism(Environment.ProcessorCount)
            .WithExecutionMode(ParallelExecutionMode.ForceParallelism)
            .WithMergeOptions(ParallelMergeOptions.NotBuffered) // remove order dependancy
            .Where(x => Enumerable.Range(2, (int)Math.Ceiling(Math.Sqrt(x)))
                                  .All(y => x % y != 0))
            .TakeWhile((n, index) => index < N).Concat(new int[] { 2, 3 }.AsParallel()).OrderBy(x => x).Take(N);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM