zoukankan      html  css  js  c++  java
  • [转]浅谈 BigInteger

    http://www.cnblogs.com/skyivben/archive/2008/07/13/1241681.html

    最近一段时间,我在 Timus Online Judge 网站做 ACM 题。发现其中不少题目需要用到 BigInteger,例如:

    于是,我就开始寻找可以用于 C# 的 BigInteger 类。结果如下表所示:

    项目 速度 自动增长 进制 ModPowPowSqrt 源文件大小 备注
    Fcl35Biginteger 最快 232 1,755行,47.1 KB 是 struct,不是 class
    SkyivBigInteger
    109 335行,9.16 KB 源文件短小精悍,功能全
    ChewBigInteger
    232 3,335行,102 KB 源文件中有大量的注释
    JavaBigInteger 极慢 28? (vjslib.dll)
    需要 J# run-time library

    在上表中,JavaBigInteger 是指 java.math.BigInteger,她在 vjslib.dll 中,需要 J# run-time library (Visual Studio 2005/2008 中已经包括 J# run-time library)。用 .NET Reflector 打开 C:\Windows\Microsoft.NET\Framework\v2.0.50727\vjslib.dll 文件,找到 java.math.BigInteger,查看其源程序代码如下:

    1 [Serializable, JavaInterfaces("2;System/IConvertible;java/lang/Comparable;")]
    2 public class BigInteger : Number, IConvertible, Comparable
    3 {
    4   // Fields
    5   private BigInteger __absOfthis;
    6   [JavaFlags(0x1002)]
    7   internal sbyte[] __bits;
    8   // 
    9 }

    根据第 7 行的 internal sbyte[] __bits; 猜想 java.math.BigInteger 的内部实现是基于 28 进制的。


    ChewBigInteger 就是 Chew Keong TANCodeProject 中的 C# BigInteger Class 。这是个很有名的 C# Biginteger 类,它写于2002年8月到9月,已经有很久的历史了。但是她有个致命的缺点,就是不能自动增长,需要事先指定最大允许有多大的数,如果在运算过程中产生超过你事先指定大小的数,就会引发异常。

    她的部分源程序代码如下所示:

     1 namespace ChewKeongTAN
     2 {
     3   /*public */class BigInteger
     4   {
     5     // maximum length of the BigInteger in uint (4 bytes)
     6     // change this to suit the required level of precision.
     7 
     8     private const int maxLength = 26591// == Math.Ceiling ( digits / log10(2^32) )
     9 
    10     private uint[] data = null;          // stores bytes from the Big Integer
    11     public int dataLength;               // number of actual chars used
    12     
    13     // 
    14     
    15   }
    16 }

    她内部使用一个 uint[] 来存储,是基于 232 ≈ 4.3 x 109 进制的。需要事先设定 maxLength 的值(程序中第 8 行),用以指定最多允许多少个 232 进制的数字,换算用的公式是:

    maxLength = Math.Ceiling ( digits / log10(232) ) ≈ digits / 9.6

    上式中 digits 表示十进制数字的个数。


    Fcl35Biginteger 就是 .NET Framework 3.5 Base Class Library 中的 System.Numeric.BigInteger,她在 System.Core.dll 中。注意,她是一个 internal struct。首先,她是一个 struct,不是一个 class,也就是说她是值类型而不是引用类型。其次,致命的是,她是 internal 的,而不是 public 的,也就是说,她就在那里,但是你就是不能使用她。实际上,在 .NET Framework 3.5 的 Beta 和 latest CTP 版本中,她还是 public 的,只不过是在 .NET Framework 3.5 正式发布时变成了 internal 的。这里有一篇 BCL Team 的 Melitta Andersen 于2008年1月4日发表的 Blog: Where did BigInteger go? 谈到这件事,要点是:

    So why was BigInteger cut?  The basic rationale behind making BigInteger internal was that it just wasn't ready to ship.  We thought our implementation met the needs for a BigInteger type.  But then we had some other teams take a look at it and they pointed out some performance and compatibility issues that we just didn't have time to fix before we shipped.

    于是, .NET Reflector 又上场了,用她打开 C:\Windows\assembly\GAC_MSIL\System.Core\3.5.0.0__b77a5c561934e089\System.Core.dll 文件,找到 System.Numeric.BigInteger,在 Disassembler 窗口中,点击最下方的 Expand Methods,然后把源程序代码全部复制到 Visual Studio 2008 的 IDE 窗口(注意,目标 Framework 设定为 .NET Framework 2.0)中。按下编译按钮,天啊,出现了好多错误。于是,就开始修改源程序代码:

    • 实现两个空的 sealed class: ImmutableAttribute 和 PureAttribute。(前一个好理解,后一个不知是什么东东。当然,实际上她们不应该是空的)
    • 实现一个 static class: Contract,其中包括两个重载的 public static void Requires 方法。(当然,实际上的 Contract.Requires 应该更复杂)
    • 实现一个 static class: Res,其中包括若干个 public static readonly string 字段。(当然,实际上 Res 应该从资源文件中获得本地化的数据)
    • 将程序中出现的某些常数值还原为 private const 字段。注意,这不是必须的,只是为了使程序更容易看懂。如果不做这一步,就可以将程序中所有的 private const 字段全部删除。
    • 修改程序中某些变量的名称。注意,这也不是必须的,也只是为了使程序更容易看懂。

    然后,再次编译,耶,通过了。但是,还是高兴得太早了一点,运行一些测试,发现答案根本不对。

    经检查,发现是 .NET Reflector 在反汇编为 C# 代码时会丢失一些强制类型转换,这在我的上一篇随笔 浅谈 Math.BigMul 方法 中已经提到过了:她把 Math.BigMul 方法中的唯一的语句 return (long)a * b; 错误地反汇编为 return (a * b); 了,但是反汇编为 IL 代码是正确的。

    于是,只好逐条语句检查,如果需要进行强制类型转换,就自己加上,同时还去掉一些不必要的强制类型转换和括号,以便使源程序代码更易读。真是一个很烦人和困难和工作,热切盼望 .NET Reflector 能够尽快修复这个 BUG。:)

    好了,经过调试,程序终于运行正常了。下面就是修改后的部分源程序代码:

     1 namespace Fcl35.Numeric
     2 {
     3   [Serializable, StructLayout(LayoutKind.Sequential), Immutable, ComVisible(false)]
     4   public /*internal*/ struct BigInteger : IFormattable,
     5     IEquatable<BigInteger>, IComparable<BigInteger>, IComparable
     6   {
     7     private const ulong Base = uint.MaxValue + 1UL// 0x100000000L;
     8     private readonly short _sign;
     9     private readonly uint[] _data;
    10     private int _length;
    11     // 
    12   }
    13 }

    ChewKeongTAN.BigInteger 类一样,Fcl35.Numberic.BigInteger 结构内部也使用一个 uint[] 来存储,同样也是基于 232 ≈ 4.3 x 109 进制的。但是,最重要的一点,Fcl35.Numberic.BigInteger 结构是动态增长的,不需要事先指定最大允许有多大的数。


    现在来讨论我最近写的一个 Skyiv.Numeric.BigInteger 类,她是从我在做 ACM 题时写的极简单的如下所示的 BigInteger 类发展而来的:

    1 sealed class BigInteger
    2 {
    3   int[] digits = new int[1800];
    4   // 
    5 }

    这个极简单的 BigInteger 类的全部源程序代码可以在本随笔开头给出的 URL 中找到,只有五十多行。她是基于 10 进制的,内部使用一个 int[] 来存储,需要事先指定该数组的大小,不能动态增长,而且只能表示非负整数。 

    下面是 Skyiv.Numeric.BigInteger 的源程序代码,非常短小精悍,只有区区 335 行,该有的功能基本上都有了。实现了基本的算术运算和逻辑运算,还实现了 Pow 和 Sqrt 方法。

      1 using System;
      2 using System.Text;
      3 using System.Collections.Generic;
      4 
      5 namespace Skyiv.Numeric
      6 {
      7   sealed class BigInteger : IEquatable<BigInteger>, IComparable<BigInteger>
      8   {
      9     static readonly int Len = 9// int.MaxValue = 2,147,483,647
     10     static readonly int Base = (int)Math.Pow(10, Len);
     11 
     12     int sign;
     13     List<int> data;
     14 

    她内部使用一个 List<int> 来存储(程序中第 13 行),是动态增长的,基 于 109 进制(第 9 和 10 行)。

     15     BigInteger(long x)
     16     {
     17       sign = (x == 0? 0 : ((x > 0? 1 : -1);
     18       data = new List<int>();
     19       for (ulong z = Abs(x); z != 0; z /= (ulong)Base) data.Add((int)(z % (ulong)Base));
     20     }
     21 
     22     BigInteger(BigInteger x)
     23     {
     24       sign = x.sign;  // x != null
     25       data = new List<int>(x.data);
     26     }
     27 
     28     public static implicit operator BigInteger(long x)
     29     {
     30       return new BigInteger(x);
     31     }
     32 

    她只有两个构造函数,而且都是 private 的。要获得该类的实例,主要途径之一是通过从 long、int 等类型隐式转换(第 28 到 31 行)而来,例如 BigInteger a = 2; 语句。

     33     public static BigInteger Parse(string s)
     34     {
     35       if (s == nullreturn null;
     36       s = s.Trim().Replace(","null);
     37       if (s.Length == 0return 0;
     38       BigInteger z = 0;
     39       z.sign = (s[0== '-'? -1 : 1;
     40       if (s[0== '-' || s[0== '+') s = s.Substring(1);
     41       int r = s.Length % Len;
     42       z.data = new List<int>(new int[s.Length / Len + ((r != 0? 1 : 0)]);
     43       int i = z.data.Count - 1;
     44       if (r != 0) z.data[i--= int.Parse(s.Substring(0, r));
     45       for (; i >= 0; i--, r += Len) z.data[i] = int.Parse(s.Substring(r, Len));
     46       z.Shrink();
     47       return z;
     48     }
     49 

    获得该类的实例的主要途径之二是调用静态的 Parse 方法,例如:var a = BigInteger.Parse("-123,456,789"); 语句。

     50     public static void Swap(ref BigInteger x, ref BigInteger y)
     51     {
     52       BigInteger z = x;
     53       x = y;
     54       y = z;
     55     }
     56 
     57     public static ulong Abs(long x)
     58     {
     59       return (x < 0? (ulong)-x : (ulong)x;
     60     }
     61 
     62     public static BigInteger Abs(BigInteger x)
     63     {
     64       if (x == nullreturn null;
     65       BigInteger z = new BigInteger(x);
     66       z.sign = Math.Abs(x.sign);
     67       return z;
     68     }
     69 

    上面三个静态的公共方法都很简单,主要是为了方便,如果不提供这些方法也没关系。要注意的是第二个 Abs(long) 方法返回 ulong 类型,而且从不抛出异常。而 Math.Abs(long) 返回 long 类型,而且当参数为 long.MinValue 时会抛出异常。

     70     public static BigInteger Pow(BigInteger x, int y)
     71     {
     72       if (x == nullreturn null;
     73       BigInteger z = 1, n = x;
     74       for (; y > 0; y >>= 1, n *= n) if ((y & 1!= 0) z *= n;
     75       return z;
     76     }
     77 
     78     public static BigInteger operator +(BigInteger x)
     79     {
     80       if (x == nullreturn null;
     81       return new BigInteger(x);
     82     }
     83 
     84     public static BigInteger operator -(BigInteger x)
     85     {
     86       if (x == nullreturn null;
     87       BigInteger z = new BigInteger(x);
     88       z.sign = -x.sign;
     89       return z;
     90     }
     91 

    注意,重载的单目 + 操作符不仅是为了和重载的单目 - 操作符对应,而且可以用于克隆。 BigInteger x = +y; 和 BigInteger x = y; 语句是完全不同的,前者为克隆一份新的对象,后者只是直接引用同一对象。之所以不实现 IClone 接口,是因为该接口的 Clone 方法的返回类型是 object,使用起来很不方便:var x = y.Clone() as BigInteger; 需要一次强制类型转换。如果 FCL 中有泛型版本的 IClone<T> 接口就好了。

     92     public static BigInteger operator ++(BigInteger x)
     93     {
     94       return x + 1;
     95     }
     96 
     97     public static BigInteger operator --(BigInteger x)
     98     {
     99       return x - 1;
    100     }
    101 

    在 C# 语言中重载 ++ 和 -- 运算符比在 C++ 语言中容易,C# 编译器会处理好前缀和后缀的情况。

    102     public static BigInteger operator +(BigInteger x, BigInteger y)
    103     {
    104       if (x == null || y == nullreturn null;
    105       if (x.AbsCompareTo(y) < 0) Swap(ref x, ref y);
    106       BigInteger z = new BigInteger(x);
    107       if (x.sign * y.sign == -1) z.AbsSubtract(y);
    108       else z.AbsAdd(y);
    109       return z;
    110     }
    111 
    112     public static BigInteger operator -(BigInteger x, BigInteger y)
    113     {
    114       if (x == null || y == nullreturn null;
    115       return x + (-y);
    116     }
    117 
    118     public static BigInteger operator *(BigInteger x, BigInteger y)
    119     {
    120       if (x == null || y == nullreturn null;
    121       BigInteger z = 0;
    122       z.sign = x.sign * y.sign;
    123       z.data = new List<int>(new int[x.data.Count + y.data.Count]);
    124       for (int i = x.data.Count - 1; i >= 0; i--)
    125         for (int j = y.data.Count - 1; j >= 0; j--)
    126         {
    127           long n = Math.BigMul(x.data[i], y.data[j]);
    128           z.data[i + j] += (int)(n % Base);
    129           z.CarryUp(i + j);
    130           z.data[i + j + 1+= (int)(n / Base);
    131           z.CarryUp(i + j + 1);
    132         }
    133       z.Shrink();
    134       return z;
    135     }
    136 
    137     public static BigInteger operator /(BigInteger dividend, BigInteger divisor)
    138     {
    139       BigInteger remainder;
    140       return DivRem(dividend, divisor, out remainder);
    141     }
    142 
    143     public static BigInteger operator %(BigInteger dividend, BigInteger divisor)
    144     {
    145       BigInteger remainder;
    146       DivRem(dividend, divisor, out remainder);
    147       return remainder;
    148     }
    149 

    在 C# 中重载加减乘除等运算符,C# 编译器也会自动添上相应的赋值运算符(+=, -=, ...)。

    150     public static BigInteger DivRem(BigInteger dividend, BigInteger divisor, out BigInteger remainder)
    151     {
    152       remainder = null;
    153       if (dividend == null || divisor == nullreturn null;
    154       if (divisor.sign == 0throw new Exception("division by zero");
    155       if (dividend.AbsCompareTo(divisor) < 0)
    156       {
    157         remainder = new BigInteger(dividend);
    158         return 0;
    159       }
    160       remainder = 0;
    161       BigInteger quotient = 0;
    162       quotient.sign = dividend.sign * divisor.sign;
    163       quotient.data = new List<int>(new int[dividend.data.Count]);
    164       divisor = Abs(divisor); // NOT: divisor.sign = Math.Abs(divisor.sign);
    165       for (int i = dividend.data.Count - 1; i >= 0; i--)
    166       {
    167         remainder = remainder * Base + dividend.data[i];
    168         int iQuotient = remainder.BinarySearch(divisor, -1);
    169         quotient.data[i] = iQuotient;
    170         remainder -= divisor * iQuotient;
    171       }
    172       quotient.Shrink();
    173       if (remainder.sign != 0) remainder.sign = dividend.sign;
    174       return quotient;
    175     }
    176 
    177     public static BigInteger Sqrt(BigInteger x)
    178     {
    179       if (x == null || x.sign < 0return null;
    180       BigInteger root = 0;
    181       root.sign = 1;
    182       root.data = new List<int>(new int[x.data.Count / 2 + 1]);
    183       for (int i = root.data.Count - 1; i >= 0; i--) root.data[i] = x.BinarySearch(root, i);
    184       root.Shrink();
    185       return root;
    186     }
    187 
    188     int BinarySearch(BigInteger divisor, int i)
    189     {
    190       int low = 0, high = Base - 1, mid = 0, cmp = 0;
    191       while (low <= high)
    192       {
    193         mid = (low + high) / 2;
    194         cmp = CompareTo(divisor, mid, i);
    195         if (cmp > 0) low = mid + 1;
    196         else if (cmp < 0) high = mid - 1;
    197         else return mid;
    198       }
    199       return (cmp < 0? (mid - 1) : mid;
    200     }
    201 
    202     int CompareTo(BigInteger divisor, int mid, int i)
    203     {
    204       if (i >= 0) divisor.data[i] = mid;
    205       return AbsCompareTo(divisor * ((i >= 0? divisor : mid));
    206     }
    207 
    208     void AbsAdd(BigInteger x)
    209     {
    210       for (int i = 0; i < x.data.Count; i++)
    211       {
    212         data[i] += x.data[i];
    213         CarryUp(i);
    214       }
    215     }
    216 
    217     void AbsSubtract(BigInteger x)
    218     {
    219       for (int i = 0; i < x.data.Count; i++)
    220       {
    221         data[i] -= x.data[i];
    222         CarryDown(i);
    223       }
    224       Shrink();
    225     }
    226 
    227     void CarryUp(int n)
    228     {
    229       for (; data[n] >= Base; n++)
    230       {
    231         if (n == data.Count - 1) data.Add(data[n] / Base);
    232         else data[n + 1+= data[n] / Base;
    233         data[n] %= Base;
    234       }
    235     }
    236 
    237     void CarryDown(int n)
    238     {
    239       for (; data[n] < 0; n++)
    240       {
    241         data[n + 1]--;
    242         data[n] += Base;
    243       }
    244       Shrink();
    245     }
    246 
    247     void Shrink()
    248     {
    249       for (int i = data.Count - 1; i >= 0 && data[i] == 0; i--) data.RemoveAt(i);
    250       if (data.Count == 0) sign = 0;
    251     }
    252 

    除法、取模和求平方根都是通过二分搜索来进行的,每次搜索时都有进行一次乘法和比较运算,以决定下次要搜索的区间。

    253     public static bool operator ==(BigInteger x, BigInteger y)
    254     {
    255       if (object.ReferenceEquals(x, null)) return object.ReferenceEquals(y, null);
    256       return x.Equals(y);
    257     }
    258 
    259     public static bool operator !=(BigInteger x, BigInteger y)
    260     {
    261       if (object.ReferenceEquals(x, null)) return !object.ReferenceEquals(y, null);
    262       return !x.Equals(y);
    263     }
    264 
    265     public static bool operator <(BigInteger x, BigInteger y)
    266     {
    267       if (object.ReferenceEquals(x, null)) return !object.ReferenceEquals(y, null);
    268       return x.CompareTo(y) < 0;
    269     }
    270 
    271     public static bool operator >(BigInteger x, BigInteger y)
    272     {
    273       if (object.ReferenceEquals(x, null)) return false;
    274       return x.CompareTo(y) > 0;
    275     }
    276 
    277     public static bool operator <=(BigInteger x, BigInteger y)
    278     {
    279       if (object.ReferenceEquals(x, null)) return true;
    280       return x.CompareTo(y) <= 0;
    281     }
    282 
    283     public static bool operator >=(BigInteger x, BigInteger y)
    284     {
    285       if (object.ReferenceEquals(x, null)) return object.ReferenceEquals(y, null);
    286       return x.CompareTo(y) >= 0;
    287     }
    288 
    289     public override string ToString()
    290     {
    291       StringBuilder sb = new StringBuilder();
    292       if (sign < 0) sb.Append('-');
    293       sb.Append((data.Count == 0? 0 : data[data.Count - 1]);
    294       for (int i = data.Count - 2; i >= 0; i--) sb.Append(data[i].ToString("D" + Len));
    295       return sb.ToString();
    296     }
    297 
    298     public override int GetHashCode()
    299     {
    300       int hash = sign;
    301       foreach (int n in data) hash ^= n;
    302       return hash;
    303     }
    304 
    305     public override bool Equals(object other)
    306     {
    307       if (other == null || GetType() != other.GetType()) return false;
    308       return Equals(other as BigInteger);
    309     }
    310 
    311     public bool Equals(BigInteger other)
    312     {
    313       return CompareTo(other) == 0;
    314     }
    315 
    316     public int CompareTo(BigInteger other)
    317     {
    318       if (object.ReferenceEquals(other, null)) return 1;
    319       if (sign < other.sign) return -1;
    320       if (sign > other.sign) return 1;
    321       if (sign == 0return 0;
    322       return sign * AbsCompareTo(other);
    323     }
    324 
    325     int AbsCompareTo(BigInteger other)
    326     {
    327       if (data.Count < other.data.Count) return -1;
    328       if (data.Count > other.data.Count) return 1;
    329       for (int i = data.Count - 1; i >= 0; i--)
    330         if (data[i] != other.data[i])
    331           return (data[i] < other.data[i]) ? -1 : 1;
    332       return 0;
    333     }
    334   }
    335 }

    最后就是各种逻辑运算符以及从基类继承的 ToString、GetHashCode、Equals 方法,以及用于实现接口的 Equals 和 CompareTo 方法。


    下面,让我们来比较这四种 BigInteger 的效率吧。测试程序如下所示:

    TestMain.cs:

     1 using System;
     2 
     3 namespace Skyiv
     4 {
     5   class TestMain
     6   {
     7     static void Main()
     8     {
     9       string s1 = new TestFcl35BigInteger().Run(20);
    10       string s2 = new TestSkyivBigInteger().Run(20);
    11       string s3 = new TestChewBigInteger().Run(20);
    12       string s4 = new TestJavaBigInteger().Run(14);
    13       Console.WriteLine(s1 == s2 && s1 == s3);
    14     }
    15   }
    16 }

    TestBase.cs:

     1 using System;
     2 using System.Diagnostics;
     3 
     4 namespace Skyiv
     5 {
     6   class TestBase
     7   {
     8     public string Run(int n)
     9     {
    10       var stopWatch = Stopwatch.StartNew();
    11       var s = RunTest(n);
    12       stopWatch.Stop();
    13       Console.WriteLine("{0,-25} {1,11:F7} {2,7:N0}",
    14         GetType(), stopWatch.Elapsed.TotalSeconds, s.Length);
    15       return s;
    16     }
    17 
    18     protected virtual string RunTest(int n)
    19     {
    20       throw new InvalidOperationException("TestBase.RunTest()");
    21     }
    22   }
    23 }

    Test?????BigInteger.cs (以 TestSkyivBigInteger 为例,其它的大同小异):

     1 using System;
     2 using BigInteger = Skyiv.Numeric.BigInteger;
     3 
     4 namespace Skyiv
     5 {
     6   sealed class TestSkyivBigInteger : TestBase
     7   {
     8     protected sealed override string RunTest(int n)
     9     {
    10       BigInteger a = 2, b;
    11       for (int i = 2; i <= n; i++, a *= b) b = a + i;
    12       return a.ToString();
    13     }
    14   }
    15 }

    这个测试程序主要是测试 BigInteger 的加法和乘法运算,其运行结果如下:

    Skyiv.TestFcl35BigInteger  25.8100647 256,142
    Skyiv.TestSkyivBigInteger 34.2497761 256,142
    Skyiv.TestChewBigInteger 396.8586529 256,142
    Skyiv.TestJavaBigInteger 61.9083458 4,003
    True

    上表中,第二栏是测试程序的运行时间,单位是秒。第三栏是最终运算结果的十进制数字的个数。前三个 BigInteger 都使用了同一测试用例,运算结果达 256,142 个十进制数字,并且经过比较这三个 BigInteger 的运算结果是相同的。最后的 java.math.BigInteger 由于运算速度极慢(估计是微软 J# 的问题,我想在真正的 java 语言中应该是比较快的),测试用例的规模更小,运算结果只有 4,003 个十进制数字。从测试结果可以看出:

    • Fcl35.Numeric.BigInteger 最快,推荐使用。
    • Skyiv.Numeric.BigInteger 也很快,并且源程序代码短小精悍,也推荐使用。
    • ChewKeongTAN.BigInteger 很慢,并且不能动态增长,不推荐使用。
    • java.math.BigInteger 极慢,并且需要 J# run-time library,也不推荐使用。

    Skyiv.Numeric.BigInteger 和 ChewKeongTAN.BigInteger 的乘法都是使用简单的乘法,时间复杂度是 O(N2)。Fcl35.Numeric.BigInteger 的乘法是使用 Karatsuba 算法,其时间复杂度约为 O(N1.585),如下所示:

     1 public static BigInteger Multiply(BigInteger x, BigInteger y)
     2 {
     3   int xLength = x.Length;
     4   int yLength = y.Length;
     5   if ((((xLength + yLength) >= UpperBoundForSchoolBookMultiplicationDigits /* 0x40 */)
     6     && (xLength >= ForceSchoolBookMultiplicationThresholdDigits /* 8 */))
     7     && (yLength >= ForceSchoolBookMultiplicationThresholdDigits /* 8 */))
     8   {
     9     return MultiplyKaratsuba(x, y);
    10   }
    11   return MultiplySchoolBook(x, y);
    12 }
    13 
    14 private static BigInteger MultiplyKaratsuba(BigInteger x, BigInteger y)
    15 {
    16   int length = Math.Max(x.Length, y.Length) / 2;
    17   if (length <= 2 * ForceSchoolBookMultiplicationThresholdDigits /* 0x10 */
    18     || x.Length < 2 * ForceSchoolBookMultiplicationThresholdDigits /* 0x10 */
    19     || y.Length < 2 * ForceSchoolBookMultiplicationThresholdDigits /* 0x10 */)
    20     return MultiplySchoolBook(x, y);
    21   int shift = BitsPerDigit /* 0x20 */ * length;
    22   BigInteger i1 = RightShift(x, shift);
    23   BigInteger i2 = x.RestrictTo(length);
    24   BigInteger i3 = RightShift(y, shift);
    25   BigInteger i4 = y.RestrictTo(length);
    26   BigInteger i5 = Multiply(i1, i3);
    27   BigInteger i6 = Multiply(i2, i4);
    28   BigInteger i7 = Multiply(i1 + i2, i3 + i4) - (i5 + i6);
    29   return (i6 + LeftShift(i7 + LeftShift(i5, shift), shift));
    30 }

    其实如果使用快速傅里叶变换(FFT)来进行乘法运算,时间复杂度可降低到 O(N logN loglogN)。而且除法可以用除数的倒数乘以被除数来计算,倒数值用牛顿迭代法:

    Ui+1 = Ui (2 - VUi)

    来计算,这导致 U二次收敛于 1/V。实际上,许多超级计算机和 RISC 机都是使用这种迭代法来实现除法的。用牛顿迭代法计算平方根和除法类似。若:

    Ui+1 = Ui (3 - VUi2) / 2

    则 U二次收敛于 1/√V,最后用 V 除就得到√V

    我可能会使用快速傅里叶变换来改写 Skyiv.Numeric.BigInteger 类。微软声称在下一版本的 .NET Framework 中会重新发布 System.Numeric.BigInteger,希望也使用快速傅里叶变换。:)

    本文中的所有源代码可以到 http://bitbucket.org/ben.skyiv/biginteger/downloads 页面下载。
    也可以使用 hg clone http://bitbucket.org/ben.skyiv/biginteger/ 命令下载。
    关于 hg ,请参阅 Mercurial 备忘录


    1. 2008-07-25 更新: 请参阅:再谈 BigInteger - 使用快速傅里叶变换
    2. 2010-04-22 更新: 对 Skyiv.Numeric.BigInteger 的 GetHashCode、Parse 和 ToString 方法进行了优化。请参阅:再谈 BigInteger - 优化
     

    作者: 火地晋
    出处: http://yelaiju.cnblogs.com
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    Python for Infomatics 第14章 数据库和SQL应用四(译)
    展望2017
    bing的简单英文字典工具
    自我安慰
    Python for Infomatics 第14章 数据库和SQL应用三(译)
    Python for Infomatics 第14章 数据库和SQL应用二(译)
    Python for Infomatics 第14章 数据库和SQL应用一(译)
    希望父亲早日恢复
    Python for Infomatics 第13章 网页服务四(译)
    Python for Infomatics 第13章 网页服务三(译)
  • 原文地址:https://www.cnblogs.com/yelaiju/p/1857182.html
Copyright © 2011-2022 走看看