[英]Generate prime number sequences, Loop Break vs LINQ TakeWhile
我嘗試用 C# 生成素數序列。到目前為止,代碼似乎運行良好。
List<int> _primes = new List<int>();
bool IsPrime(int num)
{
if (num < 2)
return false;
int chkEnd = (int)Math.Sqrt(num);
foreach (var p in _primes)
{
if (p > chkEnd)
break;
if (num % p == 0)
return false;
}
return true;
}
for (int i=2; i<1000000; i++)
{
if (IsPrime(i))
_primes.Add(i);
}
然后我把IsPrime()改成LINQ TakeWhile(),我覺得這兩個方法的時間復雜度是一樣的,但是執行時間明顯比前者長。
bool IsPrime(int num)
{
if (num < 2)
return false;
int chkEnd = (int)Math.Sqrt(num);
foreach (var p in _primes.TakeWhile(x => x <= chkEnd))
{
if (num % p == 0)
return false;
}
return true;
}
要么
bool IsPrime(int num)
{
if (num < 2)
return false;
int chkEnd = (int)Math.Sqrt(num);
return _primes.TakeWhile(x => x <= chkEnd).All(p => num % p != 0);
}
有誰知道嗎?
編輯:我將循環迭代增加到 5000000,在 consoleApplication 中運行代碼,執行時間為 output
00:00:01.1841593 -- IsPrime_PureLoopBreak
00:00:03.2560654 -- IsPrime_TakeWhileAndLoop
00:00:03.4178782 -- IsPrime_TakeWhileAndAll
static void Main(string[] args)
{
List<int> _primes;
bool IsPrime_PureLoopBreak(int num)
{
if (num < 2)
return false;
int chkEnd = (int)Math.Sqrt(num);
foreach (var p in _primes)
{
if (p > chkEnd)
break;
if (num % p == 0)
return false;
}
return true;
}
bool IsPrime_TakeWhileAndLoop(int num)
{
if (num < 2)
return false;
int chkEnd = (int)Math.Sqrt(num);
foreach (var p in _primes.TakeWhile(x => x <= chkEnd))
{
if (num % p == 0)
return false;
}
return true;
}
bool IsPrime_TakeWhileAndAll(int num)
{
if (num < 2)
return false;
int chkEnd = (int)Math.Sqrt(num);
return _primes.TakeWhile(x => x <= chkEnd).All(p => num % p != 0);
}
var t1 = Measure(() =>
{
_primes = new List<int>();
for (int i = 2; i < 5000000; i++)
{
if (IsPrime_PureLoopBreak(i))
_primes.Add(i);
}
});
Console.WriteLine($"{t1} -- IsPrime_PureLoopBreak");
var t2 = Measure(() =>
{
_primes = new List<int>();
for (int i = 2; i < 5000000; i++)
{
if (IsPrime_TakeWhileAndLoop(i))
_primes.Add(i);
}
});
Console.WriteLine($"{t2} -- IsPrime_TakeWhileAndLoop");
var t3 = Measure(() =>
{
_primes = new List<int>();
for (int i = 2; i < 5000000; i++)
{
if (IsPrime_TakeWhileAndAll(i))
_primes.Add(i);
}
});
Console.WriteLine($"{t3} -- IsPrime_TakeWhileAndAll");
Console.ReadLine();
}
public static TimeSpan Measure(Action action)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
action?.Invoke();
stopwatch.Stop();
return stopwatch.Elapsed;
}
考慮編譯器需要生成的代碼。 像 Linq 這樣的“高級”代碼簡單易寫,但編譯器通常更難優化。
您之前的示例可以進一步簡化為使用普通的 for 循環而不是 foreach。 所以每次迭代應該只需要少數指令。
使用 linq 時,編譯器將為迭代器生成代理 object。 這將需要調用您的 lambda 以檢查它是否需要繼續迭代,並且編譯器可能無法內聯方法調用。 方法調用很便宜,但仍然比簡單的算術指令貴幾倍。
因此,根據經驗,使用 Linq 和其他高級模式來提高可讀性。 如果您發現代碼的某些部分沒有達到性能目標,請使用分析器來准確確定哪個部分需要時間,然后重寫該部分直到它足夠快或者您已使其盡可能快。
使用 LINQ 有一些額外的開銷。 您正在創建和調用 lambda 個表達式。
但是,請確保在得出結論之前嘗試發布版本。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.