C# 枚举范围(…)。任何(…)都优于基本循环:为什么?

C# 枚举范围(…)。任何(…)都优于基本循环:为什么?,c#,performance,algorithm,linq,C#,Performance,Algorithm,Linq,我在改编一个简单的素数生成一行,从Scala到C#(作者在评论中提到)。我得出了以下结论: int NextPrime(int from) { while(true) { n++; if (!Enumerable.Range(2, (int)Math.Sqrt(n) - 1).Any((i) => n % i == 0)) return n; } } 它可以工作,返回与运行博客中引用的代码相同的结果。事实上,它工作得相当快。在LinqPad中,它

我在改编一个简单的素数生成一行,从Scala到C#(作者在评论中提到)。我得出了以下结论:

int NextPrime(int from)
{
  while(true)
  {
    n++;
    if (!Enumerable.Range(2, (int)Math.Sqrt(n) - 1).Any((i) => n % i == 0))
      return n;
  }
} 
它可以工作,返回与运行博客中引用的代码相同的结果。事实上,它工作得相当快。在LinqPad中,它在大约1秒内生成了第100000个素数。出于好奇,我重写了它,没有使用
Enumerable.Range()
Any()

int-NextPrimeB(int-from)
{
while(true)
{
n++;
bool-hasFactor=false;

for(int i=2;i对于for循环的每次迭代,您都会找到n的平方根。请缓存它

int root = (int)Math.Sqrt(n);
for (int i = 2; i <= root; i++)
int root=(int)Math.Sqrt(n);
for(int i=2;i如果条件成功,而循环不成功,则提前退出

一旦可以确定结果,
的枚举即停止

这是一个错误基准的示例。请尝试修改您的循环,并查看差异:

    if (n % i == 0) { hasFactor = true; break; }
}

throw new InvalidOperationException("Cannot satisfy criteria.");

LINQ版本会短路,你的循环不会。我的意思是,当你确定一个特定的整数实际上是一个因子时,LINQ代码会停止,返回它,然后继续。你的代码会一直循环,直到完成为止

如果将
更改为包含该短路,则应看到类似的性能:

int NextPrimeB(int from)
{
  while(true)
  {
    n++;
    for (int i = 2; i <= (int)Math.Sqrt(n); i++)
    {
        if (n % i == 0) return n;;
    }
  }
}
int-NextPrimeB(int-from)
{
while(true)
{
n++;

对于(int i=2;i而言,这似乎是罪魁祸首:

for (int i = 2; i <= (int)Math.Sqrt(n); i++)
{
    if (n % i == 0) hasFactor = true;
}

正如其他人所指出的,将Math.Sqrt调用移到循环之外,以避免每次循环调用它。

问题的一部分是Math.Sqrt执行的次数。
这将给你一个更具代表性的细分

int limit = (int)Math.Sqrt(n);
for (int i = 2; i <= limit; i++)
int limit=(int)Math.Sqrt(n);

对于(int i=2;i,以优化的名义,您可以通过避免2之后的偶数来更聪明一些:

if (n % 2 != 0)
{
  int quux = (int)Math.Sqrt(n);

  for (int i = 3; i <= quux; i += 2)
  {
    if (n % i == 0) return n;
  }
}
if(n%2!=0)
{
int qux=(int)Math.Sqrt(n);

对于(int i=3;i您可以通过从内部for循环中断来加快它的速度乍一看,我想您应该想在
if
块中添加一个
break
。这将大大提高性能。这就是linq查询所做的,它会在找到答案时停止,因为for循环完成了整个迭代woah,4个答案几乎都是一样的。事实上,这是解决方案中更大的部分。看起来BradM的建议在循环之外计算
sqrt
,也产生了显著的不同。这两个变化导致了比Enumerable()/Any()更快的算法@KChaloux这通常是意料之中的。只要您在正确的时间使用正确的运算符,LINQ就可以更轻松地编写中等效率的代码,因为实现复杂查询操作所需的工作量比人们通常愿意投入的工作量要多。如果您确实实现了这些操作(正确)尽管您可以自己忽略所有迭代器块、委托创建等的开销。这种开销通常不是问题,但可以是问题,具体取决于正在执行的操作类型。+1用于提及其他人没有做过的事情。这是减速的一个重要部分,除了在第一次发现f时没有中断演员。我后来真的考虑过。请注意,这并不是在生产代码中使用的,但玩弄它很有趣。+1对于任何可能将其用于更严肃的事情的人来说都是一个很好的建议。一旦你开始进行严肃的优化,你最终会完全脱离这个算法,进入一个算法我的目的是高效地找到素数。这种方法通常有致命的缺陷,而且只能表现得很好。是的,我同意。我的评论更多的是为了鼓励思考在寻找素数的过程中,你实际上在做什么,这是考虑其他算法的一个途径。
int limit = (int)Math.Sqrt(n);
for (int i = 2; i <= limit; i++)
if (n % 2 != 0)
{
  int quux = (int)Math.Sqrt(n);

  for (int i = 3; i <= quux; i += 2)
  {
    if (n % i == 0) return n;
  }
}