通过限制迭代次数来确定一个数字是否为素数——c#
本文关键字:数字 一个 是否 迭代 | 更新日期: 2023-09-27 18:02:08
我想通过尽可能多地限制迭代次数来确定一个数字是否是素数。下面的程序是在一个博客中提出的。我可以理解这些部分的代码。
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;
}
}
因为每个素数(除了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;
}
}
- 您没有检查负数
- 检查一个数字是否为偶数,(i &1) == 0效率更高。不幸的是,i % 3 没有这样的技巧。
虽然一个好的答案已经被接受,并且我之前已经提供了另一个答案,但我为ulong
提供了一个新方法,该方法所做的事情略有不同,这可能有助于解释为什么6很重要。这使用for
循环而不是while
。
UPDATE:我用一个并行线程运行的版本扩展了这个答案。查看这个CodeReview链接获取并行版本。
编辑:添加快速消除许多复合材料
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;
}
这是基于数学定理,即所有> 3的质数都可以表示为(6k+/-1)。但这并不意味着(6k+/1)形式的每个数都是素数。
正确的反面应该是,如果你有一个数字不被表示为(6k+/-1),那么这个数字不可能是素数。
为了以后与模算子一起使用,(6k-1)等价于(6(k+1)+5)。
因此,我们的目的是在5处开始循环,即(6k-1)第一次出现k=1时,在循环内检查(6k-1)和(6k+1),然后在循环的另一次迭代中增加6。简而言之,对前一个因子加6的迭代与对k加1的迭代相同。
丑陋的显式强制转换的解释
在进一步的测试表明,对于这个算法,它们几乎没有什么区别后,我去掉了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微秒。但是现在包含了,所以算法被认为是完整的
看@Dennis_E的回答和解释,我给了+1。我有两种不同的看法。这些语句可以执行上亿次隐式强制转换:
double r = Math.Floor(Math.Sqrt(i));
int f = 5;
while (f <= r)
循环条件隐式地将f
反复转换为双精度类型。这就模糊了某些性能可能会降低的地方。虽然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;
}
以上都扩展了@Dennis_E的答案。另一种变体是:
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;
}
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;
}
}