LINQ列表.并行返回布尔值

本文关键字:布尔值 返回 并行 列表 LINQ | 更新日期: 2023-09-27 18:16:15

我有一个已知质数的static List<long> primes,直到某一点,还有一个函数,像这样:

static bool isPrime(long p)
{
    double rootP = Math.Sqrt(p);
    foreach (long q in primes)
    {
        if (p % q == 0)
            return false;
        if (q >= rootP)
            return true;
    }
    return true;
}

可以像这样并行化:

static bool isPrime(long p)
{
    double rootP = Math.Sqrt(p);
    primes.AsParallel().ForAll(q =>
        {
            if (p % q == 0)
                return false;
            if (q > rootP)
                break;
        });
    return true;
}

然而,这会给出一个编译时错误,说我的块中的一些返回类型不能隐式地转换为委托的返回类型。

我对LINQ有点陌生,尤其是PLINQ。对我来说,这似乎是并行性的一个很好的候选,因为每个已知素数与候选素数的检查是一个独立的过程。

是否有一种简单的方法来修复我的块,使其工作,或者我需要以一种完全不同的方式来解决这个问题?

LINQ列表.并行返回布尔值

语法方面,您在代码中犯了两个错误:

  1. lambda中的return不从封闭方法返回,只从lambda返回,因此您的return false;无法正常工作。
  2. 你不能从lambda中取出break。即使lambda在循环中执行,这个循环对你来说基本上是不可见的,你当然不能直接控制它。

我认为修复代码的最佳方法是使用专门为此目的而制作的LINQ方法:Any(),以及TakeWhile()来过滤掉太大的素数:

static bool IsPrime(long p)
{
    double rootP = Math.Sqrt(p);
    return !primes.AsParallel().TakeWhile(q => q > rootP).Any(q => p % q == 0);
}

但是你的推理也有一个缺陷:

对我来说,这似乎是并行性的一个很好的候选,因为每个已知素数与候选素数的检查是一个独立的过程。

事情没那么简单。检查每个素数也是一个极其简单的过程。这意味着简单并行化的开销(就像我上面建议的那样)可能大于性能收益。一个更复杂的解决方案(就像Matthew Watson在评论中建议的那样)可以帮助解决这个问题。

这是你的问题的解决方案:

static List<long> primes = new List<long>() {
  2,3,5,7,11,13,17,19,23
};
static bool isPrime(long p) {
  var sqrt = (long)Math.Sqrt(p);
  if (sqrt * sqrt == p)
    return (false);
  return (primes.AsParallel().All(n => n > sqrt || p % n != 0));
}

它甚至试图减少二次数的并行性,一旦发现第一个候选项

,它就会停止检查更多的候选项

出现错误是因为如果您的条件

p % q == 0

true,闭包将返回false,当

q > rootP

就会中断并且不返回任何东西。这将在PHP中工作,但在。net中不起作用:P

lambda是一个完整的匿名函数,返回类型必须始终一致。

你必须重新设计你的代码。你在非并行化的例子中做得对…只需将break替换为return true(它不会更漂亮,但应该可以工作)。

使用Parallel可能更容易。为而不是

static volatile bool result = true;
static bool isPrime(long p)
{
    double rootP = Math.Sqrt(p);
    Parallel.For(0, primes.Count, (q, state) =>
        {
            if (p % q == 0)
            {
                result = false;
                state.Break();
            }
            if (q > rootP)
                state.Break();
        });
    return result;
}