检查一个数是否为质数

66

我只想询问一下,这种检查数字是不是质数的方法是否正确?因为我读到0和1不是质数。

int num1;

Console.WriteLine("Accept number:");
num1 = Convert.ToInt32(Console.ReadLine());
if (num1 == 0 || num1 == 1)
{
    Console.WriteLine(num1 + " is not prime number");
    Console.ReadLine();
}
else
{
    for (int a = 2; a <= num1 / 2; a++)
    {
        if (num1 % a == 0)
        {
            Console.WriteLine(num1 + " is not prime number");
            return;
        }

    }
    Console.WriteLine(num1 + " is a prime number");
    Console.ReadLine();
}

1
是的,质数被定义为大于一。 - Matthew Watson
3
只是想问一下,这种检查方式是否正确?- 是的。也许您想问的是这种检查方式是否高效? - Ilya Ivanov
4
不行。简单来说,你可以将 a 的起始值设为 3,并将其递增 2 而不是 1(并将 2 是质数的情况视为特例)。但请参考这里:http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes。 - Matthew Watson
2
@MatthewWatson 如果想要生成某个限制范围内的所有质数,那么筛法是很好的选择,但是如果想要检查一个数字是否为质数,它就没什么用了。 - Daniel Fischer
2
@Servy 你说的“如果足够小,甚至不会变得低效”是什么意思?如果你筛选到sqrt(n)以获取需要进行试除法的素数,则筛选比通过复合数进行不必要的除法更加繁琐,如果避免2、3和可能是5的倍数,那么您就是一个企业家。 如果您筛选到n来查找sieve中是否为素数,则您的算法渐近更差(并且常数因子也不会让其在小数字方面获胜)。 - Daniel Fischer
显示剩余2条评论
31个回答

126
var number;

Console.WriteLine("Accept number:");
number = Convert.ToInt32(Console.ReadLine());

if (IsPrime(number))
{
    Console.WriteLine("It is prime");
}
else
{
    Console.WriteLine("It is not prime");
}       

public static bool IsPrime(int number)
{
    if (number <= 1) return false;
    if (number == 2) return true;
    if (number % 2 == 0) return false;

    var boundary = (int)Math.Floor(Math.Sqrt(number));
          
    for (int i = 3; i <= boundary; i += 2)
        if (number % i == 0)
            return false;
    
    return true;        
}

我把number / 2改成了Math.Sqrt(number),因为在维基百科上面说:

这个程序的核心是将n除以每个大于1且小于或等于n的平方根的整数m。如果任何一个这些除法的结果是整数,则n不是素数;否则它是一个素数。事实上,如果n = a*b是合数(a和b ≠ 1),则因数之一ab必定最多为n的平方根


9
好的解决方案。请注意,您每次循环时都会重新计算平方根。 - Eric Lippert
4
考虑三种情况。如果这个数字 确实是质数,那么无论你在向上取整还是向下取整时停止,你都会正确地推断出它是质数。现在假设它是复合数且是一个完全平方数。然后,天花板和地板是相等的,所以无论你选择哪个都没有关系,因为它们是相同的。现在假设它是复合数但不是完全平方数。那么它有一个因子,小于它的平方根,这时地板就是正确的。无论我们处于这三种情况中的哪一种,你都可以选择向下取整。 - Eric Lippert
2
请注意,这个参数要求我的第二个声明是正确的:对于每个完全平方数,其平方根的上取整和下取整相等。如果 Math.Sqrt 声明 10000 的平方根为 99.9999999999999 而不是 100.0000000000000,那么我的声明是错误的,您应该使用上取整。是否存在任何我的声明错误的情况? - Eric Lippert
5
让我们思考一下算法效率低下的其他方式。假设您正在检查一个大质数。首先,您检查它是否可被2整除,结果不行。那么接下来您检查3,还是不行。然后您检查4,为什么要检查4呢?如果它可以被4整除,那么它必定已经被2整除了。您接着检查5,然后6。同样的问题,为什么要检查6呢?只有当它可以同时被2和3整除时才能被6整除,而这两个因子您已经检查过了。 - Eric Lippert
1
@SonerGönül:回到这个老问题——我检查了所有的平方int,它们都具有i == (int)Floor(Sqrt(i * i))的属性。我还检查了所有小于2<sup>54</sup>的平方long,它们也没问题。我还没有检查2<sup>54</sup>和2<sup>63</sup>之间的平方长整型。 - Eric Lippert
显示剩余19条评论

20
使用Soner的算法,但稍微有所变化:我们将一直运行,直到i等于Math.Ceiling(Math.Sqrt(number)),这是朴素解法的关键。
boolean isPrime(int number)
{
    if (number == 1) return false;
    if (number == 2) return true;

    var limit = Math.Ceiling(Math.Sqrt(number)); //hoisting the loop limit

    for (int i = 2; i <= limit; ++i)  
       if (number % i == 0)  
           return false;
    return true;

}

在循环中每次计算平方根?这不是低效的吗? - Mangesh
2
@Mangesh 的意思是,for 循环每次都会重新计算 Sqrt 并测试每个可能的除数 - 显然优化不会将常量表达式提升出来,因为它无法知道 Math.Ceiling 或 Math.Sqrt 计算的内容(想象一下如果是 (new Random()).Next(number))所以你应该将其提升出来。 - NetMage

12

这是一个不错的做法。

    static bool IsPrime(int n)
    {
        if (n > 1)
        {
            return Enumerable.Range(1, n).Where(x => n%x == 0)
                             .SequenceEqual(new[] {1, n});
        }

        return false;
    }

一个快速编写程序的方法是:

        for (;;)
        {
            Console.Write("Accept number: ");
            int n = int.Parse(Console.ReadLine());
            if (IsPrime(n))
            {
                Console.WriteLine("{0} is a prime number",n);
            }
            else
            {
                Console.WriteLine("{0} is not a prime number",n);
            }
        }

6
3年后,你是否还会像这样写代码 for(;;) - Rafael Herscovici
如果你有一个需要 long 类型的大数,那么 Enumerable.Range 似乎不起作用,因为它没有针对 long 类型的重载。此外,创建 1 到 n 之间所有数字的列表会消耗大量内存和分配时间。最后,你不需要检查每个值,因为所有偶数都已知不是质数(即可被 2 整除)。 - Matt Ruwe
3
在提出批评之后,我要说我喜欢你的解决方案的简洁性。 - Matt Ruwe
3
我不同意@MattRuwe有关“创建介于...之间所有数字列表”的评论。据我所知,Range、Where和SequenceEqual是通过流式传输序列的方式工作的,除了已读取的最后一个元素外,它们不存储任何元素。 - Thariq Nugrohotomo
1
有趣的是 - 我不知道 Range、Where 和 SequenceEqual 的那些特性。 - Matt Ruwe
1
@Dementic 哈哈,不。 - Rezo Megrelidze

8
这基本上是Eric Lippert在之前提出的一个精彩建议的实现。
    public static bool isPrime(int number)
    {
        if (number == 1) return false;
        if (number == 2 || number == 3 || number == 5) return true;
        if (number % 2 == 0 || number % 3 == 0 || number % 5 == 0) return false;

        var boundary = (int)Math.Floor(Math.Sqrt(number));

        // You can do less work by observing that at this point, all primes 
        // other than 2 and 3 leave a remainder of either 1 or 5 when divided by 6. 
        // The other possible remainders have been taken care of.
        int i = 6; // start from 6, since others below have been handled.
        while (i <= boundary)
        {
            if (number % (i + 1) == 0 || number % (i + 5) == 0)
                return false;

            i += 6;
        }

        return true;
    }

1
为什么不将循环改为 for (int i = 6; i <= boundary; i += 6) - Yetti99
1
可能会更改第一行为if (number <= 1) return false; - Yetti99
1
@Yetti99,你不需要使用FOR循环,因为在那个return false;之前,它是逐一递增1而不是6。这可能是此页面上最好的代码。 - Marco Antonio

7
我实现了一种不同的质数检查方法,原因如下:
  • 大多数解决方案都会在同一个倍数上进行不必要的迭代(例如,它们会检查5、10,然后是15,而单个% 5将测试这些内容)。
  • % 2将处理所有偶数(所有以0、2、4、6或8结尾的整数)。
  • % 5将处理所有5的倍数(所有以5结尾的整数)。
  • 剩下的就是测试以1、3、7或9结尾的整数的偶数除法。但美妙之处在于我们可以每次增加10,而不是每次增加2,我将演示一种线程化的解决方案。
  • 其他算法没有线程化,所以它们不能像我希望的那样充分利用您的核心。
  • 我还需要支持非常大的质数,因此我需要使用BigInteger数据类型,而不是int、long等。

这是我的实现:

public static BigInteger IntegerSquareRoot(BigInteger value)
{
    if (value > 0)
    {
        int bitLength = value.ToByteArray().Length * 8;
        BigInteger root = BigInteger.One << (bitLength / 2);
        while (!IsSquareRoot(value, root))
        {
            root += value / root;
            root /= 2;
        }
        return root;
    }
    else return 0;
}

private static Boolean IsSquareRoot(BigInteger n, BigInteger root)
{
    BigInteger lowerBound = root * root;
    BigInteger upperBound = (root + 1) * (root + 1);
    return (n >= lowerBound && n < upperBound);
}

static bool IsPrime(BigInteger value)
{
    Console.WriteLine("Checking if {0} is a prime number.", value);
    if (value < 3)
    {
        if (value == 2)
        {
            Console.WriteLine("{0} is a prime number.", value);
            return true;
        }
        else
        {
            Console.WriteLine("{0} is not a prime number because it is below 2.", value);
            return false;
        }
    }
    else
    {
        if (value % 2 == 0)
        {
            Console.WriteLine("{0} is not a prime number because it is divisible by 2.", value);
            return false;
        }
        else if (value == 5)
        {
            Console.WriteLine("{0} is a prime number.", value);
            return true;
        }
        else if (value % 5 == 0)
        {
            Console.WriteLine("{0} is not a prime number because it is divisible by 5.", value);
            return false;
        }
        else
        {
            // The only way this number is a prime number at this point is if it is divisible by numbers ending with 1, 3, 7, and 9.
            AutoResetEvent success = new AutoResetEvent(false);
            AutoResetEvent failure = new AutoResetEvent(false);
            AutoResetEvent onesSucceeded = new AutoResetEvent(false);
            AutoResetEvent threesSucceeded = new AutoResetEvent(false);
            AutoResetEvent sevensSucceeded = new AutoResetEvent(false);
            AutoResetEvent ninesSucceeded = new AutoResetEvent(false);
            BigInteger squareRootedValue = IntegerSquareRoot(value);
            Thread ones = new Thread(() =>
            {
                for (BigInteger i = 11; i <= squareRootedValue; i += 10)
                {
                    if (value % i == 0)
                    {
                        Console.WriteLine("{0} is not a prime number because it is divisible by {1}.", value, i);
                        failure.Set();
                    }
                }
                onesSucceeded.Set();
            });
            ones.Start();
            Thread threes = new Thread(() =>
            {
                for (BigInteger i = 3; i <= squareRootedValue; i += 10)
                {
                    if (value % i == 0)
                    {
                        Console.WriteLine("{0} is not a prime number because it is divisible by {1}.", value, i);
                        failure.Set();
                    }
                }
                threesSucceeded.Set();
            });
            threes.Start();
            Thread sevens = new Thread(() =>
            {
                for (BigInteger i = 7; i <= squareRootedValue; i += 10)
                {
                    if (value % i == 0)
                    {
                        Console.WriteLine("{0} is not a prime number because it is divisible by {1}.", value, i);
                        failure.Set();
                    }
                }
                sevensSucceeded.Set();
            });
            sevens.Start();
            Thread nines = new Thread(() =>
            {
                for (BigInteger i = 9; i <= squareRootedValue; i += 10)
                {
                    if (value % i == 0)
                    {
                        Console.WriteLine("{0} is not a prime number because it is divisible by {1}.", value, i);
                        failure.Set();
                    }
                }
                ninesSucceeded.Set();
            });
            nines.Start();
            Thread successWaiter = new Thread(() =>
            {
                AutoResetEvent.WaitAll(new WaitHandle[] { onesSucceeded, threesSucceeded, sevensSucceeded, ninesSucceeded });
                success.Set();
            });
            successWaiter.Start();
            int result = AutoResetEvent.WaitAny(new WaitHandle[] { success, failure });
            try
            {
                successWaiter.Abort();
            }
            catch { }
            try
            {
                ones.Abort();
            }
            catch { }
            try
            {
                threes.Abort();
            }
            catch { }
            try
            {
                sevens.Abort();
            }
            catch { }
            try
            {
                nines.Abort();
            }
            catch { }
            if (result == 1)
            {
                return false;
            }
            else
            {
                Console.WriteLine("{0} is a prime number.", value);
                return true;
            }
        }
    }
}
更新:如果你想更快地使用试除法实现方案,你可以考虑使用质数缓存。 一个数字只有在它不被小于它平方根的其他质数整除时才是质数。此外,如果你处理的是足够大的值(从 Rosetta Code 中获取,以防该网站关闭),你可以考虑使用Miller-Rabin素性检验的概率版本来检查数字的质数性:

// Miller-Rabin primality test as an extension method on the BigInteger type.
// Based on the Ruby implementation on this page.
public static class BigIntegerExtensions
{
  public static bool IsProbablePrime(this BigInteger source, int certainty)
  {
    if(source == 2 || source == 3)
      return true;
    if(source < 2 || source % 2 == 0)
      return false;

    BigInteger d = source - 1;
    int s = 0;

    while(d % 2 == 0)
    {
      d /= 2;
      s += 1;
    }

    // There is no built-in method for generating random BigInteger values.
    // Instead, random BigIntegers are constructed from randomly generated
    // byte arrays of the same length as the source.
    RandomNumberGenerator rng = RandomNumberGenerator.Create();
    byte[] bytes = new byte[source.ToByteArray().LongLength];
    BigInteger a;

    for(int i = 0; i < certainty; i++)
    {
      do
      {
        // This may raise an exception in Mono 2.10.8 and earlier.
        // http://bugzilla.xamarin.com/show_bug.cgi?id=2761
        rng.GetBytes(bytes);
        a = new BigInteger(bytes);
      }
      while(a < 2 || a >= source - 2);

      BigInteger x = BigInteger.ModPow(a, d, source);
      if(x == 1 || x == source - 1)
        continue;

      for(int r = 1; r < s; r++)
      {
        x = BigInteger.ModPow(x, 2, source);
        if(x == 1)
          return false;
        if(x == source - 1)
          break;
      }

      if(x != source - 1)
        return false;
    }

    return true;
  }
}

1
从7开始,每次增加30。你真正需要哪些数字从7到36?这些数字不能是2、3或5的倍数。只有8个这样的数字。 - Will Ness
1
你需要每次将这八个数字都增加30,参考“轮式分解”(尽管我认为维基百科的文章写得不好)。另外可以看这里:http://stackoverflow.com/a/21310956/849891 -- 235 = .... - Will Ness
1
快速增长的投资面临着收益递减的限制:它是 1/2,2/6,8/30,48/210,480/2310,... = 0.5, 0.3333, 0.2667, 0.2286, 0.2078, ... 所以收益为 50%,25%,16.67%,10%,... 对于轮上的更多数字处理,就需要更多倍数的投资。如果我们使用循环展开来实现,意味着代码将会变成2x,4x,6x,10x等倍数的膨胀。 - Will Ness
1
...所以"投资回报率"是25%,6.25%,2.8%,1%,...,因此扩大轮子到11以上并没有多大意义。周长为PRODUCT(p_i){i=1..n}的每个车轮都包含PRODUCT(p_i - 1){i=1..n}个尖刺,但只能让我们在(p_(n+1))^2之前得到无合数的质数。滚动100个质数的车轮,我们只能得到最大为547^2=299209的质数,但该车轮上有4181833108490708127856970969853073811885209475016770818056714802062057564305290‌348961566798327912719763961768373051814396765475489229643362657214962862299679072‌90044555142202583817713509990400000000000000000000000000000个尖刺。 - Will Ness
1
来得有点晚了,你可以在C#中重新实现https://github.com/kimwalisch/primesieve...他们使用3种方法来创建素数的位掩码。1)一个210(2x3x5x7)轮。2)一个小于块大小的质数列表,需要应用到每个块。3)跟踪仅适用于一个块的质数的列表。我首先会检查mod(n,210),然后生成一个素数列表,测试每个素数是否符合n。 - Jeremy Lakeman
显示剩余7条评论

5

这里有一个很好的例子。我把代码放在这里以防网站有一天崩溃。

using System;

class Program
{
    static void Main()
    {
    //
    // Write prime numbers between 0 and 100.
    //
    Console.WriteLine("--- Primes between 0 and 100 ---");
    for (int i = 0; i < 100; i++)
    {
        bool prime = PrimeTool.IsPrime(i);
        if (prime)
        {
        Console.Write("Prime: ");
        Console.WriteLine(i);
        }
    }
    //
    // Write prime numbers between 10000 and 10100
    //
    Console.WriteLine("--- Primes between 10000 and 10100 ---");
    for (int i = 10000; i < 10100; i++)
    {
        if (PrimeTool.IsPrime(i))
        {
        Console.Write("Prime: ");
        Console.WriteLine(i);
        }
    }
    }
}

这里是包含 IsPrime 方法的类:

using System;

public static class PrimeTool
{
    public static bool IsPrime(int candidate)
    {
    // Test whether the parameter is a prime number.
    if ((candidate & 1) == 0)
    {
        if (candidate == 2)
        {
        return true;
        }
        else
        {
        return false;
        }
    }
    // Note:
    // ... This version was changed to test the square.
    // ... Original version tested against the square root.
    // ... Also we exclude 1 at the end.
    for (int i = 3; (i * i) <= candidate; i += 2)
    {
        if ((candidate % i) == 0)
        {
        return false;
        }
    }
    return candidate != 1;
    }
}

4
OP只是想检查给定的数字是否为质数,而不是计算两个数字之间的所有质数。 - Paras Wadehra
@ParasWadehra IsPrime方法正是OP所需的。显示数字的代码只是说明如何使用它的示例。 - Crono

3
/***
 * Check a number is prime or not
 * @param n the number
 * @return {@code true} if {@code n} is prime
 */
public static boolean isPrime(int n) {
    if (n == 2) {
        return true;
    }
    if (n < 2 || n % 2 == 0) {
        return false;
    }
    for (int i = 3; i <= Math.sqrt(n); i += 2) {
        if (n % i == 0) {
            return false;
        }
    }
    return true;
}

2

这个版本计算了一个质数平方根列表,并仅检查平方根以下的质数列表,并使用二分搜索在列表中查找已知的质数。我循环检查了前100万个质数,用时约为7秒。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            //test();
            testMax();
            Console.ReadLine();
        }

        static void testMax()
        {
            List<int> CheckPrimes = Enumerable.Range(2, 1000000).ToList();
            PrimeChecker pc = new PrimeChecker(1000000);
            foreach (int i in CheckPrimes)
            {
                if (pc.isPrime(i))
                {
                    Console.WriteLine(i);
                }
            }
        }
    }

    public class PrimeChecker{
        public List<int> KnownRootPrimesList;
        public int HighestKnownPrime = 3;

        public PrimeChecker(int Max=1000000){
            KnownRootPrimesList = new List<int>();
            KnownRootPrimesList.Add(2);
            KnownRootPrimesList.Add(3);
            isPrime(Max);
        }

        public bool isPrime(int value)
        {
            int srt = Convert.ToInt32(Math.Ceiling(Math.Sqrt(Convert.ToDouble(value))));
            if(srt > HighestKnownPrime)
            {
                for(int i = HighestKnownPrime + 1; i <= srt; i++)
                {
                    if (i > HighestKnownPrime)
                    {
                        if(isPrimeCalculation(i))
                        {
                                KnownRootPrimesList.Add(i);
                                HighestKnownPrime = i;
                        }
                    }
                }
            }
            bool isValuePrime = isPrimeCalculation(value);
            return(isValuePrime);
        }

        private bool isPrimeCalculation(int value)
        {
            if (value < HighestKnownPrime)
            {
                if (KnownRootPrimesList.BinarySearch(value) > -1)
                {
                    return (true);
                }
                else
                {
                    return (false);
                }
            }
            int srt = Convert.ToInt32(Math.Ceiling(Math.Sqrt(Convert.ToDouble(value))));
            bool isPrime = true;
            List<int> CheckList = KnownRootPrimesList.ToList();
            if (HighestKnownPrime + 1 < srt)
            {
                CheckList.AddRange(Enumerable.Range(HighestKnownPrime + 1, srt));
            }
            foreach(int i in CheckList)
            {
                isPrime = ((value % i) != 0);
                if(!isPrime)
                {
                    break;
                }
            }
            return (isPrime);
        }

        public bool isPrimeStandard(int value)
        {
            int srt = Convert.ToInt32(Math.Ceiling(Math.Sqrt(Convert.ToDouble(value))));
            bool isPrime = true;
            List<int> CheckList = Enumerable.Range(2, srt).ToList();
            foreach (int i in CheckList)
            {
                isPrime = ((value % i) != 0);
                if (!isPrime)
                {
                    break;
                }
            }
            return (isPrime);
        }
    }
}

1
你可以通过用户输入的数字找到质数的范围。
代码:
class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Input a number to find Prime numbers\n");
            int inp = Convert.ToInt32(Console.ReadLine());
            Console.WriteLine("\n Prime Numbers are:\n------------------------------");
            int count = 0;

            for (int i = 1; i <= inp; i++)
            {
                for (int j = 2; j < i; j++) // j=2 because if we divide any number with 1 the remaider will always 0, so skip this step to minimize time duration.
                {
                    if (i % j != 0)
                    {
                        count += 1;
                    }
                }
                if (count == (i - 2))
                    {
                        Console.Write(i + "\t"); 
                    }

                count = 0;
            }

            Console.ReadKey();

        }
    }

Prime numbers


1

原始答案

  • 质数除了2以外都是奇数
  • 质数不为负数
  • 1或0既非质数也非合数

方法

  1. 添加一个计数器以检查输入的数字被i整除的次数(并且有0(零)余数)
  2. 如果计数器= 2,则输入是质数,否则不是质数
  3. 如果计数器> 2“中断”以避免不必要的过程(如果要计算输入数字的因子,请在第一个if语句上删除“ || counter> 2”)
  4. 如果您想查看具有余数0(或存在因子)的数字数量,请在for循环中的第2个if语句中添加此行代码:
Console.WriteLine( $" {inputNumber} / {i} = { inputNumber / i} (remainder: {inputNumber % i})" ); 
  1. 在第4行的代码末尾添加以下代码,以查看所有被您输入的数字除以的数字(如果您想要查看余数输出和商数)
Console.Write( "Enter a Positive Number: " );
int inputNumber = Convert.ToInt32( Console.ReadLine() );
int counter = 0;

    for ( int i = 1; i <= inputNumber; i++ ) {
        if ( inputNumber == 0 || inputNumber == 1 || counter > 2 ) { break; }
        if ( inputNumber % i == 0 ) { counter++; }
    }

    if ( counter == 2 ) {
        Console.WriteLine( $"{inputNumber} is a prime number." );
    } else if ( inputNumber == 1 || inputNumber == 0 ) {
        Console.WriteLine( $"{inputNumber} is neither prime nor composite." );
    } else {
        Console.WriteLine( $"{inputNumber} is not a prime number. (It is a composite number)" );
    }

我的参考资料:https://www.tutorialspoint.com/Chash-Program-to-check-if-a-number-is-prime-or-not。这是一个用于检查数字是否为质数的C#程序。

简化版:

我在这里使用了 uint 而不是 int,以避免输入 负数

public bool IsPrime(uint number)
{
    if (number <= 1) { return false; }

    int counter = 0;
    for (int i = 1; i <= number; i++)
    {
        if (number % i == 0) { counter++; }
        if (counter > 2) { return false; }
    }

    return true;
}

我喜欢你的简化版本,虽然它是一个好的天真实现,但在处理更大的数字时不会很好扩展。一个(非常非常小的)优化是对于2返回true,并从2开始计数,因为我们知道所有数字都能被1整除,所以没有测试它的必要。因此,你可以简单地在number % i == 0成立时直接返回false,而不需要计数器。但这只是一个非常小的优化:在我的PC上使用LINQPad和Benchmark.Net进行测试,当输入为479001599 - 第9个阶乘质数时,它只节省了0.222秒(总时间的不到10%)。我只是好奇 :) - Steve Pettifer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接