浅谈 BigInteger.docx

上传人:b****5 文档编号:7933557 上传时间:2023-01-27 格式:DOCX 页数:21 大小:24.67KB
下载 相关 举报
浅谈 BigInteger.docx_第1页
第1页 / 共21页
浅谈 BigInteger.docx_第2页
第2页 / 共21页
浅谈 BigInteger.docx_第3页
第3页 / 共21页
浅谈 BigInteger.docx_第4页
第4页 / 共21页
浅谈 BigInteger.docx_第5页
第5页 / 共21页
点击查看更多>>
下载资源
资源描述

浅谈 BigInteger.docx

《浅谈 BigInteger.docx》由会员分享,可在线阅读,更多相关《浅谈 BigInteger.docx(21页珍藏版)》请在冰豆网上搜索。

浅谈 BigInteger.docx

浅谈BigInteger

浅谈BigInteger

最近一段时间,我在TimusOnlineJudge网站做ACM题。

发现其中不少题目需要用到BigInteger,例如:

1531.Zonesonaplane

1013.K-basednumbers.Version3

1012.K-basednumbers.Version2

1153.Supercomputer

1108.Heritage

于是,我就开始寻找可以用于C#的BigInteger类。

结果如下表所示:

项目速度自动增长进制ModPowPowSqrt源文件大小备注

Fcl35Biginteger最快是232是是否1,755行,47.1KB是struct,不是class

SkyivBigInteger

快是109否是是335行,9.16KB源文件短小精悍,功能全

ChewBigInteger

慢否232是否是3,335行,102KB源文件中有大量的注释

JavaBigInteger极慢是28?

是是否(vjslib.dll)

需要J#run-timelibrary

在上表中,JavaBigInteger是指java.math.BigInteger,她在vjslib.dll中,需要J#run-timelibrary(VisualStudio2005/2008中已经包括J#run-timelibrary)。

用.NETReflector打开C:

\Windows\Microsoft.NET\Framework\v2.0.50727\vjslib.dll文件,找到java.math.BigInteger,查看其源程序代码如下:

复制代码

1[Serializable,JavaInterfaces("2;System/IConvertible;java/lang/Comparable;")]

2publicclassBigInteger:

Number,IConvertible,Comparable

3{

4//Fields

5privateBigInteger__absOfthis;

6[JavaFlags(0x1002)]

7internalsbyte[]__bits;

8//

9}

复制代码

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

 

ChewBigInteger就是ChewKeongTAN在CodeProject中的C#BigIntegerClass。

这是个很有名的C#Biginteger类,它写于2002年8月到9月,已经有很久的历史了。

但是她有个致命的缺点,就是不能自动增长,需要事先指定最大允许有多大的数,如果在运算过程中产生超过你事先指定大小的数,就会引发异常。

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

复制代码

1namespaceChewKeongTAN

2{

3/*public*/classBigInteger

4{

5//maximumlengthoftheBigIntegerinuint(4bytes)

6//changethistosuittherequiredlevelofprecision.

7

8privateconstintmaxLength=26591;//==Math.Ceiling(digits/log10(2^32))

9

10privateuint[]data=null;//storesbytesfromtheBigInteger

11publicintdataLength;//numberofactualcharsused

12

13//

14

15}

16}

复制代码

她内部使用一个uint[]来存储,是基于232≈4.3x109进制的。

需要事先设定maxLength的值(程序中第8行),用以指定最多允许多少个232进制的数字,换算用的公式是:

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

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

 

Fcl35Biginteger就是.NETFramework3.5BaseClassLibrary中的System.Numeric.BigInteger,她在System.Core.dll中。

注意,她是一个internalstruct。

首先,她是一个struct,不是一个class,也就是说她是值类型而不是引用类型。

其次,致命的是,她是internal的,而不是public的,也就是说,她就在那里,但是你就是不能使用她。

实际上,在.NETFramework3.5的Beta和latestCTP版本中,她还是public的,只不过是在.NETFramework3.5正式发布时变成了internal的。

这里有一篇BCLTeam的MelittaAndersen于2008年1月4日发表的Blog:

WheredidBigIntegergo?

谈到这件事,要点是:

SowhywasBigIntegercut?

ThebasicrationalebehindmakingBigIntegerinternalwasthatitjustwasn'treadytoship.WethoughtourimplementationmettheneedsforaBigIntegertype.Butthenwehadsomeotherteamstakealookatitandtheypointedoutsomeperformanceandcompatibilityissuesthatwejustdidn'thavetimetofixbeforeweshipped.

于是,.NETReflector又上场了,用她打开C:

\Windows\assembly\GAC_MSIL\System.Core\3.5.0.0__b77a5c561934e089\System.Core.dll文件,找到System.Numeric.BigInteger,在Disassembler窗口中,点击最下方的ExpandMethods,然后把源程序代码全部复制到VisualStudio2008的IDE窗口(注意,目标Framework设定为.NETFramework2.0)中。

按下编译按钮,天啊,出现了好多错误。

于是,就开始修改源程序代码:

实现两个空的sealedclass:

ImmutableAttribute和PureAttribute。

(前一个好理解,后一个不知是什么东东。

当然,实际上她们不应该是空的)

实现一个staticclass:

Contract,其中包括两个重载的publicstaticvoidRequires方法。

(当然,实际上的Contract.Requires应该更复杂)

实现一个staticclass:

Res,其中包括若干个publicstaticreadonlystring字段。

(当然,实际上Res应该从资源文件中获得本地化的数据)

将程序中出现的某些常数值还原为privateconst字段。

注意,这不是必须的,只是为了使程序更容易看懂。

如果不做这一步,就可以将程序中所有的privateconst字段全部删除。

修改程序中某些变量的名称。

注意,这也不是必须的,也只是为了使程序更容易看懂。

然后,再次编译,耶,通过了。

但是,还是高兴得太早了一点,运行一些测试,发现答案根本不对。

经检查,发现是.NETReflector在反汇编为C#代码时会丢失一些强制类型转换,这在我的上一篇随笔浅谈Math.BigMul方法中已经提到过了:

她把Math.BigMul方法中的唯一的语句return(long)a*b;错误地反汇编为return(a*b);了,但是反汇编为IL代码是正确的。

于是,只好逐条语句检查,如果需要进行强制类型转换,就自己加上,同时还去掉一些不必要的强制类型转换和括号,以便使源程序代码更易读。

真是一个很烦人和困难和工作,热切盼望.NETReflector能够尽快修复这个BUG。

:

好了,经过调试,程序终于运行正常了。

下面就是修改后的部分源程序代码:

复制代码

1namespaceFcl35.Numeric

2{

3[Serializable,StructLayout(LayoutKind.Sequential),Immutable,ComVisible(false)]

4public/*internal*/structBigInteger:

IFormattable,

5IEquatable,IComparable,IComparable

6{

7privateconstulongBase=uint.MaxValue+1UL;//0x100000000L;

8privatereadonlyshort_sign;

9privatereadonlyuint[]_data;

10privateint_length;

11//

12}

13}

复制代码

和ChewKeongTAN.BigInteger类一样,Fcl35.Numberic.BigInteger结构内部也使用一个uint[]来存储,同样也是基于232≈4.3x109进制的。

但是,最重要的一点,Fcl35.Numberic.BigInteger结构是动态增长的,不需要事先指定最大允许有多大的数。

 

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

1sealedclassBigInteger

2{

3int[]digits=newint[1800];

4//

5}

这个极简单的BigInteger类的全部源程序代码可以在本随笔开头给出的URL中找到,只有五十多行。

她是基于10进制的,内部使用一个int[]来存储,需要事先指定该数组的大小,不能动态增长,而且只能表示非负整数。

下面是Skyiv.Numeric.BigInteger的源程序代码,非常短小精悍,只有区区335行,该有的功能基本上都有了。

实现了基本的算术运算和逻辑运算,还实现了Pow和Sqrt方法。

复制代码

1usingSystem;

2usingSystem.Text;

3usingSystem.Collections.Generic;

4

5namespaceSkyiv.Numeric

6{

7sealedclassBigInteger:

IEquatable,IComparable

8{

9staticreadonlyintLen=9;//int.MaxValue=2,147,483,647

10staticreadonlyintBase=(int)Math.Pow(10,Len);

11

12intsign;

13Listdata;

14

复制代码

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

复制代码

15BigInteger(longx)

16{

17sign=(x==0)?

0:

((x>0)?

1:

-1);

18data=newList();

19for(ulongz=Abs(x);z!

=0;z/=(ulong)Base)data.Add((int)(z%(ulong)Base));

20}

21

22BigInteger(BigIntegerx)

23{

24sign=x.sign;//x!

=null

25data=newList(x.data);

26}

27

28publicstaticimplicitoperatorBigInteger(longx)

29{

30returnnewBigInteger(x);

31}

32

复制代码

她只有两个构造函数,而且都是private的。

要获得该类的实例,主要途径之一是通过从long、int等类型隐式转换(第28到31行)而来,例如BigIntegera=2;语句。

复制代码

33publicstaticBigIntegerParse(strings)

34{

35if(s==null)returnnull;

36s=s.Trim().Replace(",",null);

37if(s.Length==0)return0;

38BigIntegerz=0;

39z.sign=(s[0]=='-')?

-1:

1;

40if(s[0]=='-'||s[0]=='+')s=s.Substring

(1);

41intr=s.Length%Len;

42z.data=newList(newint[s.Length/Len+((r!

=0)?

1:

0)]);

43inti=z.data.Count-1;

44if(r!

=0)z.data[i--]=int.Parse(s.Substring(0,r));

45for(;i>=0;i--,r+=Len)z.data[i]=int.Parse(s.Substring(r,Len));

46z.Shrink();

47returnz;

48}

49

复制代码

获得该类的实例的主要途径之二是调用静态的Parse方法,例如:

vara=BigInteger.Parse("-123,456,789");语句。

复制代码

50publicstaticvoidSwap(refBigIntegerx,refBigIntegery)

51{

52BigIntegerz=x;

53x=y;

54y=z;

55}

56

57publicstaticulongAbs(longx)

58{

59return(x<0)?

(ulong)-x:

(ulong)x;

60}

61

62publicstaticBigIntegerAbs(BigIntegerx)

63{

64if(x==null)returnnull;

65BigIntegerz=newBigInteger(x);

66z.sign=Math.Abs(x.sign);

67returnz;

68}

69

复制代码

上面三个静态的公共方法都很简单,主要是为了方便,如果不提供这些方法也没关系。

要注意的是第二个Abs(long)方法返回ulong类型,而且从不抛出异常。

而Math.Abs(long)返回long类型,而且当参数为long.MinValue时会抛出异常。

复制代码

70publicstaticBigIntegerPow(BigIntegerx,inty)

71{

72if(x==null)returnnull;

73BigIntegerz=1,n=x;

74for(;y>0;y>>=1,n*=n)if((y&1)!

=0)z*=n;

75returnz;

76}

77

78publicstaticBigIntegeroperator+(BigIntegerx)

79{

80if(x==null)returnnull;

81returnnewBigInteger(x);

82}

83

84publicstaticBigIntegeroperator-(BigIntegerx)

85{

86if(x==null)returnnull;

87BigIntegerz=newBigInteger(x);

88z.sign=-x.sign;

89returnz;

90}

91

复制代码

注意,重载的单目+操作符不仅是为了和重载的单目-操作符对应,而且可以用于克隆。

BigIntegerx=+y;和BigIntegerx=y;语句是完全不同的,前者为克隆一份新的对象,后者只是直接引用同一对象。

之所以不实现IClone接口,是因为该接口的Clone方法的返回类型是object,使用起来很不方便:

varx=y.Clone()asBigInteger;需要一次强制类型转换。

如果FCL中有泛型版本的IClone接口就好了。

复制代码

92publicstaticBigIntegeroperator++(BigIntegerx)

93{

94returnx+1;

95}

96

97publicstaticBigIntegeroperator--(BigIntegerx)

98{

99returnx-1;

100}

101

复制代码

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

复制代码

102publicstaticBigIntegeroperator+(BigIntegerx,BigIntegery)

103{

104if(x==null||y==null)returnnull;

105if(x.AbsCompareTo(y)<0)Swap(refx,refy);

106BigIntegerz=newBigInteger(x);

107if(x.sign*y.sign==-1)z.AbsSubtract(y);

108elsez.AbsAdd(y);

109returnz;

110}

111

112publicstaticBigIntegeroperator-(BigIntegerx,BigIntegery)

113{

114if(x==null||y==null)returnnull;

115returnx+(-y);

116}

117

118publicstaticBigIntegeroperator*(BigIntegerx,BigIntegery)

119{

120if(x==null||y==null)returnnull;

121BigIntegerz=0;

122z.sign=x.sign*y.sign;

123z.data=newList(newint[x.data.Count+y.data.Count]);

124for(inti=x.data.Count-1;i>=0;i--)

125for(intj=y.data.Count-1;j>=0;j--)

126{

127longn=Math.BigMul(x.data[i],y.data[j]);

128z.data[i+j]+=(int)(n%Base);

129z.CarryUp(i+j);

130z.data[i+j+1]+=(int)(n/Base);

131z.CarryUp(i+j+1);

132}

133z.Shrink();

134returnz;

135}

136

137publicstaticBigIntegeroperator/(BigIntegerdividend,BigIntegerdivisor)

138{

139BigIntegerremainder;

140returnDivRem(dividend,divisor,outremainder);

141}

142

143publicstaticBigIntegeroperator%(BigIntegerdividend,BigIntegerdivisor)

144{

145BigIntegerremainder;

146DivRem(dividend,divisor,outremainder);

147returnremainder;

148}

149

复制代码

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

复制代码

150publicstaticBigIntegerDivRem(BigIntegerdividend,BigIntegerdivisor,outBigIntegerremainder)

151{

152remainder=null;

153if(dividend==null||divisor==null)returnnull;

154if(divisor.sign==0)thrownewException("divisionbyzero");

155if(dividend.AbsC

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 工学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1