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