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。对我来说,这似乎是并行性的一个很好的候选,因为每个已知素数与候选素数的检查是一个独立的过程。
是否有一种简单的方法来修复我的块,使其工作,或者我需要以一种完全不同的方式来解决这个问题?
语法方面,您在代码中犯了两个错误:
- lambda中的
return
不从封闭方法返回,只从lambda返回,因此您的return false;
无法正常工作。 - 你不能从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;
}