为什么C#BigInteger比C ++ MPIR更快?

时间:2017-03-06 22:40:17

标签: c# c++ biginteger mpir

我编写了一个简单的单线程应用程序,用于检查给定数字是否为素数。它意味着支持任意大整数,所以我在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分钟执行!

0 个答案:

没有答案