我编写了一个简单的单线程应用程序,用于检查给定数字是否为素数。它意味着支持任意大整数,所以我在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 ++实现更快。例如,尝试108446744073709551647
,C#版本需要13.5分钟,而C ++版本只花了6.5分钟执行!