我写了一个简单的单线程应用程序来检查给定的数字是否是素数。它旨在支持任意大的整数,所以我在 C# 中使用了BigInteger类。这是代码:
using System;
using System.Diagnostics;
using System.Numerics;
namespace PrimeChecker {
public static class Program {
/// <summary>
/// Gets the number of threads that parallel loops will use from the thread pool when called.
/// </summary>
private static BigInteger MinusOne = BigInteger.MinusOne,
Zero = BigInteger.Zero,
One = BigInteger.One,
Two = new BigInteger(2),
Three = new BigInteger(3),
Five = new BigInteger(5),
Six = new BigInteger(6);
public static void Main(string[] args) {
string value = args.Length == 0 ? null : args[0];
BigInteger num, factor;
Stopwatch elapsed = new Stopwatch();
do {
if (value == null) {
Console.Write("\nEnter integer to check if prime (or blank to exit): ");
value = Console.ReadLine();
}
if (value.Length == 0)
return;
value.Trim();
if (BigInteger.TryParse(value, out num)) {
Console.Write("Checking...");
elapsed.Restart();
if (num.IsZero)
Console.WriteLine("nope, divisible by 0. :/ (elapsed: 0ms)");
else if (num.IsOne)
Console.WriteLine("nope, divisible by 1. :/ (elapsed: 0ms)");
else {
factor = IsPrime(num);
elapsed.Stop();
if (factor.IsOne)
Console.WriteLine("prime! :D (elapsed: " + elapsed.Elapsed.ToString() + ")");
else
Console.WriteLine("nope, divisible by at least " + factor + " and " + (num / factor) + ". :/ (elapsed: " + elapsed.Elapsed.ToString() + ")");
}
} else
Console.WriteLine("Not a valid integer... :*");
value = null;
} while (true);
}
private static BigInteger LogBase2(BigInteger num) {
if (num <= Zero)
return MinusOne; //does not support negative values
BigInteger i = Zero;
while (!(num >>= 1).IsZero)
i++;
return i;
}
public static BigInteger Sqrt(this BigInteger num) {
if (num.IsZero)
return Zero;
else if (num.IsOne)
return One;
else if (num.Sign > 0) {
BigInteger root = LogBase2(num);
BigInteger lowerBound;
while (num < (lowerBound = root * root) || num > lowerBound + root + root) {
root += num / root;
root >>= 1;
}
return root;
} else
return MinusOne;
}
/// <summary>
/// Returns 0 if num is 0, 1 if the value is prime or 1, -1 if the value is negative,
/// else returns the smallest factor of the num.
/// </summary>
/// <param name="num">The integer to check if prime.</param>
public static BigInteger IsPrime(BigInteger num) {
if (num.IsZero)
return Zero;
else if (num.Sign < 0)
return MinusOne;
else if (num <= Three || num == Five)
return One;
else if (num.IsEven)
return Two;
else if ((num % Three).IsZero)
return Three;
BigInteger root = Sqrt(num);
if (!root.IsEven)
root++;
BigInteger i = Five;
while (i < root && !((num % i).IsZero || (num % (i += Two)).IsZero)) //main loop here
i += Four;
return i < root ? i : One;
}
}
}
但是,我想研究用 C++ 编写相同的应用程序。我设法通过使用 Windows 上的MPIR库来实现这一点。完整的代码代码和 Visual Studio 项目可以在 GitHub 上找到:https ://github.com/mathusummut/PrimeCheckerCpp ,但这里是主循环:
while (mpz_cmp(i, root) == -1) { //loop is here
mpz_mod(temp, num, i);
if (mpz_sgn(temp) == 0)
break;
mpz_add_ui(i, i, 2u);
mpz_mod(temp, num, i);
if (mpz_sgn(temp) == 0)
break;
mpz_add_ui(i, i, 4u);
}
当我运行 C# 可执行文件以检查是否2305843009213693951
为素数时,我得到以下输出:
Enter integer to check if prime (or blank to exit): 2305843009213693951
Checking...prime! :D (elapsed: 00:00:52.2468554)
而当我运行 C++ 可执行文件时,我得到以下输出:
Enter integer to check if prime (or blank to exit): 2305843009213693951
Checking...prime! :D (elapsed: 60.429640s)
为什么 C++ 实现较慢?MPIR 库在这里有问题,还是我做错了什么?
注意:两者都在 64 位发布模式下编译和测试,启用了完全优化,没有附加调试器。
注意:@hatchet 是对的,当我尝试使用 64 位无法表示的整数时,C++ 实现更快。比如trying 108446744073709551647
,C#版本用了13.5分钟,而C++版本只用了6.5分钟!