LINQ List.AsParallel与布尔返回

时间:2013-05-30 06:17:58

标签: c# linq lambda plinq

我已经获得了所有已知质数的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。对我来说,这似乎是并行性的一个很好的候选者,因为对候选素数的每个已知素数的检查是一个独立的过程。

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

4 个答案:

答案 0 :(得分:1)

这是解决您问题的方法:

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

它甚至试图减少二次数的并行性,并且一旦找到第一个,就会停止检查更多的候选者

答案 1 :(得分:1)

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

  1. lambda中的return不会从封闭方法返回,只是从lambda返回,因此return false;将无法正常工作。
  2. 你不能break出自lambda。即使lambda在循环中执行,该循环对你来说基本上是不可见的,你肯定无法直接控制它。
  3. 我认为修复代码的最佳方法是使用完全为此目的而制作的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在评论中提出的解决方案)可以帮助解决这个问题。

答案 2 :(得分:0)

发生错误是因为如果您的情况

p % q == 0

true,闭包将返回false,何时

q > rootP

它打破并且什么都不返回。这将在PHP中运行,但不适用于.NET:P

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

您必须在此处重新设计代码。您已经在非并行化示例中完成了...只需将break替换为return true(它不会更漂亮,但它应该有效)。

答案 3 :(得分:0)

使用Parallel.For可能更容易

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