我想通过尽可能多地限制迭代次数来查找数字是否为素数。以下程序在博客中提出。我可以理解代码的这些部分..
public static bool Isprime(long i)
{
if (i == 1)
{
return false;
}
else if (i < 4)
{
return true;
}
else if (i % 2 == 0)
{
return false;
}
else if (i < 9)
{
return true;
}
else if (i % 3 == 0)
{
return false;
}
但我不明白为什么f
会增加6。
else
{
double r = Math.Floor(Math.Sqrt(i));
int f = 5;
while (f <= r)
{
if (i % f == 0) { return false; }
if (i % (f + 2) == 0) { return false; }
f = f + 6;
}
return true;
}
}
答案 0 :(得分:5)
因为每个素数(2和3除外)的形式为6k +/- 1 其他所有数字都不能成为素数,因为它们可以被2或3整除 此外,对您的方法进行一些修改:
public static bool Isprime(long i)
{
if (i < 2)
{
return false;
}
else if (i < 4)
{
return true;
}
else if ((i & 1) == 0)
{
return false;
}
else if (i < 9)
{
return true;
}
else if (i % 3 == 0)
{
return false;
}
else
{
double r = Math.Floor(Math.Sqrt(i));
int f = 5;
while (f <= r)
{
if (i % f == 0) { return false; }
if (i % (f + 2) == 0) { return false; }
f = f + 6;
}
return true;
}
}
答案 1 :(得分:1)
虽然我已经接受了一个好的答案,并且我之前提供了另一个答案,但我为ulong
提供了一种新的方法,它可以帮助解释为什么6这个问题很重要。这使用for
循环而不是while
。
UPDATE :我已经使用并行线程运行的版本扩展了这个答案。 See this CodeReview Link for the parallel version.
编辑:添加了许多复合材料的快速删除
public static bool IsPrime6(ulong number)
{
// Get the quick checks out of the way.
if (number < 2) { return false; }
// Dispense with multiples of 2 and 3.
if (number % 2 == 0) { return (number == 2); }
if (number % 3 == 0) { return (number == 3); }
// Another quick check to eliminate known composites.
// http://programmers.stackexchange.com/questions/120934/best-and-most-used-algorithm-for-finding-the-primality-of-given-positive-number/120963#120963
if (!( ((number - 1) % 6 == 0) || ((number + 1) % 6 == 0)) )
{
return false;
}
// Quick checks are over. Number is at least POSSIBLY prime.
// Must iterate to determine the absolute answer.
// We loop over 1/6 of the required possible factors to check,
// but since we check twice in each iteration, we are actually
// checking 1/3 of the possible divisors. This is an improvement
// over the typical naive test of odds only which tests 1/2
// of the factors.
// Though the whole number portion of the square root of ulong.MaxValue
// would fit in a uint, there is better performance inside the loop
// if we don't need to implicitly cast over and over a few million times.
ulong root = (ulong)(uint)Math.Sqrt(number);
// Corner Case: Math.Sqrt error for really HUGE ulong.
if (root == 0) root = (ulong)uint.MaxValue;
// Start at 5, which is (6k-1) where k=1.
// Increment the loop by 6, which is same as incrementing k by 1.
for (ulong factor = 5; factor <= root; factor += 6)
{
// Check (6k-1)
if (number % factor == 0) { return false; }
// Check (6k+1)
if (number % (factor + 2UL) == 0) { return false; }
}
return true;
}
这是基于数学定理,其表明每个素数&gt; 3可以表示为(6k +/- 1)。但这并不意味着形式的每个数字(6k + / 1)都是素数。
正确的反过来说,如果你有一个未表示为(6k +/- 1)的数字,那么这个数字就不能是素数。
以后与模运算符一起使用时,(6k-1)相当于(6(k + 1)+5)。
因此我们的意图是在5处开始循环,即k = 1的第一次出现(6k-1),在循环内检查(6k-1)和(6k + 1),然后增加通过6进行循环的另一次迭代。
简而言之,通过向前一个因子添加6进行迭代与向k添加1相同。
Ugly Explicit Casts的解释
在进一步测试表明对于这个算法他们没有什么区别之后,我拿出了UL
个指示符。
<强>测试强>
要运行某些测试,您可以尝试:
const long Largest63bitPrime = 9223372036854775783L;
const ulong Largest64bitPrime = 18446744073709551557UL;
在我的笔记本电脑上,最大的63位素数需要13秒,最大的64位素数需要18秒。令人惊讶的是,上述版本相对于(ulong)Largest63bitPrime
比使用long
的其他答案的while
特定版本快1.5秒。
快速消除多种复合材料
根据对OP本身的评论,我添加了一张新支票。在这里找到最坏情况或最佳节省时间的情况有点困难。我针对Largest64bitPrime + 6
测试了它。没有检查,它是14.2微秒,相比之下是1.1微秒。但现在包括在内以便算法被认为是完整的。
答案 2 :(得分:0)
请参阅@ Dennis_E的答案和解释,我给了+1。我提供了两种变体。这些陈述可能会有数以百万计的隐式演员阵容:
double r = Math.Floor(Math.Sqrt(i));
int f = 5;
while (f <= r)
循环条件隐式地将f
强制转换为double。这掩盖了某些性能可能降低的地方。虽然long.MaxValue
的平方根的整数部分可以放在uint
中,但为了提高性能,最好将每个long
保持为public static bool Isprime1(long number)
{
// Get the quick checks out of the way.
if (number < 2) { return false; }
// 2 and 3 are prime.
if (number < 4) { return true; }
// besides 2, any other even number, i.e. any other multiple of 2, is not prime.
if ((number % 2) == 0) { return false; }
// 5 and 7 are also prime.
if (number < 9) { return true; }
// multiples of 3 are not prime.
if (number % 3 == 0) { return false; }
// Quick checks are over. Must iterate to find the answer.
// Though the whole number portion of the square root of long.MaxValue
// would fit in a uint, there is better performance inside the loop
// if we don't need to implicitly cast over and over a few million times.
long root = (long)Math.Sqrt(number);
long factor = 5;
while (factor <= root)
{
if (number % factor == 0L) { return false; }
if (number % (factor + 2L) == 0L) { return false; }
factor = factor + 6L;
}
return true;
}
。现在循环中的性能将受到任何隐式转换的影响。
public static bool Isprime2(long number)
{
// Get the quick checks out of the way.
if (number < 2) { return false; }
// 2, 3, 5, & 7 are prime.
if ((new long[] { 2L, 3L, 5L, 7L }).Contains(number)) { return true; }
// any other multiples of 2 are not prime.
if ((number % 2) == 0) { return false; }
// any other multiples of 3 are not prime.
if (number % 3 == 0) { return false; }
// Quick checks are over. Must iterate to find the answer.
// Though the whole number portion of the square root of long.MaxValue
// would fit in a uint, there is better performance inside the loop
// if we don't need to implicitly cast over and over a few million times.
long root = (long)Math.Sqrt(number);
long factor = 5;
while (factor <= root)
{
if (number % factor == 0L) { return false; }
if (number % (factor + 2L) == 0L) { return false; }
factor = factor + 6L;
}
return true;
}
以上所有内容都扩展了@Dennis_E的答案。另一种变化是:
sys.stdout.flush()
答案 3 :(得分:-1)
public class Prime {
public static void main(String[] args) {
Scanner get=new Scanner(System.in);
int n;
System.out.println("Enter a number to find its primality");
n=get.nextInt();
System.out.println(n+" isPRime? "+isPrime(Math.ceil(Math.sqrt(n)),n));
}
private static boolean isPrime(double n,int k){
boolean isPrim=true;
int p=(int)n;
for(int i=2;i<=p;i++){
boolean iprime=false;
for(int j=2;j<i/2;j++){
if(i%j==0){
iprime=true;
break;
}
}
if(!iprime && k>i){
if(k%i==0){
isPrim=false;
return isPrim;
}
}
}
return isPrim;
}
}