大数运算(一) - 中国WEB开发者网络 (http://www.webasp.net) -- 技术教程 (http://www.webasp.net/article/) --- 大数运算(一) (http://www.webasp.net/article/28/27947.htm) |
| -- 作者:未知 -- 发布日期: 2006-11-10 |
大数运算,值的存储和分割
Author:水如烟 一时兴起做做看看,发现真的不简单,花时间,头大。不过体会也相应深些。 运算单元改了又改。在基本运算单元中,变量能简则简,条件检测尽可能的放在外围。 下面的代码是关于大数值的存储和分割。其中把握好地址是关键。因为数值是分块运算的,如果地址转换不正确,最后合并结果时肯定也不正确的了。这个稍经修改,可以用来写文件切合器。区别是,处理对象不同,一是字符,一是字节。 整体的我还没做好。也是一个单元一个单元的做。如往后需要修改补充,以最后供下载的代码为准。
Friend Class Information Private Sub New() End Sub Private Shared gUnitMaxSize As Integer Private Shared gUnitDataType As TypeCode ''' <summary> ''' 运算单元最大位数 ''' </summary> Public Shared ReadOnly Property UnitMaxSize() As Integer Get Return 1 '测试,不用太大。gUnitMaxSize End Get End Property ''' <summary> ''' 运算单元数据类型 ''' </summary> ''' <value></value> Public Shared ReadOnly Property UnitDataType() As TypeCode Get Return gUnitDataType End Get End Property ''' <summary> ''' 转换为运算单元数据类型 ''' </summary> ''' <param name="Value">值</param> Public Shared Function ConverToUnitDataType(ByVal Value As Object) As Object Return System.Convert.ChangeType(Value, UnitDataType) End Function Public Shared Function ConverToUnitDataTypeFullString(ByVal Value As Object) As String Return Value.ToString.PadLeft(UnitMaxSize, "0"c) End Function Public Shared Function ConverToUnitDataTypeFullString(ByVal Value As Object, ByVal unitNum As Integer) As String Return Value.ToString.PadLeft(CInt(UnitMaxSize * Math.Pow(2, unitNum)), "0"c) End Function Public Shared Function SplitValueToArray(ByVal Value As String) As String() Return SplitValueToArray(Value, 0) End Function Public Shared Function SplitValueToArray(ByVal Value As String, ByVal unitNum As Integer) As String() Return CommonFunction.SplitStringToArray(Value, CInt(UnitMaxSize * Math.Pow(2, unitNum))) End Function Public Shared Function AddressConvertFromAToB(ByVal aAddress As Integer, ByVal aUnitNum As Integer, ByVal bUnitNum As Integer) As Integer If (aAddress * Math.Pow(2, aUnitNum)) Mod Math.Pow(2, bUnitNum) > 0 Then Throw New Exception("转换无效") Return CInt(aAddress * Math.Pow(2, aUnitNum) / (Math.Pow(2, bUnitNum))) End Function Public Shared Function IsZero(ByVal value As String) As Boolean Return System.Text.RegularExpressions.Regex.IsMatch(value, "^[0]+$") End Function Public Shared Function GetStringValue(ByVal Value As String) As String Dim mResult As String = System.Text.RegularExpressions.Regex.Match(Value, "^[0]{0,}(?<Value>[1-9].*)$").Groups("Value").Value If String.IsNullOrEmpty(mResult) Then mResult = "0" Return mResult End Function Public Shared Function GetStringsValue(ByVal valueCollection As Collections.ObjectModel.Collection(Of String), ByVal unitNum As Integer) As String Return GetStringsValue(valueCollection, 0, unitNum) End Function Public Shared Function GetStringsValue(ByVal valueCollection As Collections.ObjectModel.Collection(Of String), ByVal appendEmptyValueCount As Integer, ByVal unitNum As Integer) As String Return GetStringsValue(valueCollection, 0, valueCollection.Count - 1, appendEmptyValueCount, unitNum) End Function Public Shared Function GetStringsValue(ByVal valueCollection As Collections.ObjectModel.Collection(Of String), ByVal firstIndex As Integer, ByVal lastIndex As Integer, ByVal unitNum As Integer) As String Return GetStringsValue(valueCollection, firstIndex, lastIndex, 0, unitNum) End Function Public Shared Function GetStringsValue(ByVal valueCollection As Collections.ObjectModel.Collection(Of String), ByVal firstIndex As Integer, ByVal lastIndex As Integer, ByVal appendEmptyValueCount As Integer, ByVal unitNum As Integer) As String Dim b As New System.Text.StringBuilder Dim tmp As String For i As Integer = firstIndex To lastIndex If i = firstIndex Then tmp = valueCollection(i) Else tmp = Information.ConverToUnitDataTypeFullString(valueCollection(i), unitNum) End If b.Append(tmp) Next For i As Integer = 0 To appendEmptyValueCount - 1 tmp = Information.ConverToUnitDataTypeFullString("0", unitNum) b.Append(tmp) Next Return b.ToString End Function Shared Sub New() '寻找可表示为整数的且表示范围最大的数据类型 Dim mTypeName As String Dim mType As Type Dim mFieldInfo As Reflection.FieldInfo Dim mMaxValue As String = "0" Dim mDataTypeCode As TypeCode Dim mCurrentTypeMaxValue As String = "0" For Each c As TypeCode In [Enum].GetValues(GetType(TypeCode)) mTypeName = c.ToString mType = Type.GetType(String.Format("System.{0}", mTypeName)) mFieldInfo = mType.GetField("MaxValue") If mFieldInfo Is Nothing Then Continue For mCurrentTypeMaxValue = mFieldInfo.GetValue(Nothing).ToString If IsNumeric(mCurrentTypeMaxValue) Then If mCurrentTypeMaxValue.Length > mMaxValue.Length Then mMaxValue = mCurrentTypeMaxValue mDataTypeCode = c End If End If Next gUnitDataType = mDataTypeCode '确定该数据类型下,可表示的最大位数。保证两数相乘结果仍可表示为整数。 Dim x As Object x = ConverToUnitDataType(mMaxValue) Dim mSqrtResult As String = Math.Sqrt(CDbl(x)).ToString '最大数开方 Dim mPointIndex As Integer = mSqrtResult.IndexOf("."c) '只取整数部分 If mPointIndex > 0 Then mSqrtResult = mSqrtResult.Substring(0, mPointIndex) End If Dim mDigits As Integer = mSqrtResult.ToString.Length - 1 '可表示的最大位数是最大数开方后整数部分位数减一位 gUnitMaxSize = mDigits End Sub End Class
Friend Class CommonFunction Private Sub New() End Sub Public Shared Function Clone(Of T)(ByVal obj As T) As T Dim tmpT As T Dim mFormatter As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter Dim mMemoryStream As New System.IO.MemoryStream Using mMemoryStream mFormatter.Serialize(mMemoryStream, obj) mMemoryStream.Position = 0 tmpT = CType(mFormatter.Deserialize(mMemoryStream), T) mMemoryStream.Close() End Using Return tmpT End Function Public Shared Function SplitStringToArray(ByVal line As String, ByVal digits As Integer) As String() Dim mLineLength As Integer = line.Length Dim mCount As Integer = mLineLength \ digits If (mLineLength Mod digits) > 0 Then mCount += 1 Dim mResult(mCount - 1) As String Dim mCurrentIndex As Integer For i As Integer = mCount - 1 To 0 Step -1 mCurrentIndex = mLineLength - (mCount - i) * digits If mCurrentIndex < 0 Then mResult(0) = line.Substring(0, mLineLength - (mCount - 1) * digits) Else mResult(i) = line.Substring(mCurrentIndex, digits) End If Next Return mResult End Function End Class
<Serializable()> _ Public Class AddressInformation Private gAddress As Integer = 0 Private gUnitNum As Integer = 0 Public Property Address() As Integer Get Return gAddress End Get Friend Set(ByVal value As Integer) gAddress = value End Set End Property Public ReadOnly Property UnitNum() As Integer Get Return gUnitNum End Get End Property Friend Sub Add(ByVal value As Integer) gAddress += value End Sub Friend Sub SetZero() gAddress = 0 End Sub Friend Sub Increase() gAddress += 1 End Sub Friend Sub ResetAddressFrom(ByVal address As Integer, ByVal unitNum As Integer) gAddress = AddressConvertFromAToB(address, unitNum, gUnitNum) End Sub Friend Sub CopyAddressTo(ByRef addressInfo As AddressInformation) addressInfo.gAddress = AddressConvertFromAToB(gAddress, gUnitNum, addressInfo.gUnitNum) End Sub Friend Sub New() End Sub Friend Sub New(ByVal unitNum As Integer) gUnitNum = unitNum End Sub Public Function Clone() As AddressInformation Return CommonFunction.Clone(Of AddressInformation)(Me) End Function Public Function ConvertTo(ByVal unitNum As Integer) As AddressInformation Dim mAddressInfo As New AddressInformation(unitNum) CopyAddressTo(mAddressInfo) Return mAddressInfo End Function Private Shared Function AddressConvertFromAToB(ByVal aAddress As Integer, ByVal aUnitNum As Integer, ByVal bUnitNum As Integer) As Integer Return Information.AddressConvertFromAToB(aAddress, aUnitNum, bUnitNum) End Function Public Shadows Function ToString() As String Return String.Format("UnitNum :{0}, Address :{1}", gUnitNum, gAddress) End Function End Class
<Serializable()> _ Public Class StringValue Private gOrignalAddressInfo As AddressInformation Private gFirstAddressInfo As AddressInformation Private gLastAddressInfo As AddressInformation Private gValueArray As New System.Collections.ObjectModel.Collection(Of String) Friend ReadOnly Property ValueCollection() As System.Collections.ObjectModel.Collection(Of String) Get Return gValueArray End Get End Property Friend ReadOnly Property OrignalAddressInfo() As AddressInformation Get Return gOrignalAddressInfo End Get End Property Public ReadOnly Property FirstAddressInfo() As AddressInformation Get Return gFirstAddressInfo End Get End Property Public ReadOnly Property LastAddressInfo() As AddressInformation Get Return gLastAddressInfo End Get End Property Public ReadOnly Property UnitLength() As Integer Get Return CInt(Information.UnitMaxSize * Math.Pow(2, gFirstAddressInfo.UnitNum)) End Get End Property Public ReadOnly Property Size() As Integer Get Return gLastAddressInfo.Address - gFirstAddressInfo.Address + 1 End Get End Property Public ReadOnly Property FullValue() As String Get Return GetFullValue() End Get End Property Private Function GetFullValue() As String Return Information.GetStringsValue(gValueArray, gFirstAddressInfo.Address - gOrignalAddressInfo.Address, gFirstAddressInfo.UnitNum) End Function Public ReadOnly Property Value() As String Get Return GetValue() End Get End Property Private Function GetValue() As String Return Information.GetStringsValue(gValueArray, gFirstAddressInfo.UnitNum) End Function Private Sub Initialize(ByVal value As String) gOrignalAddressInfo = gFirstAddressInfo.Clone If Information.IsZero(value) Then gValueArray.Add("0") Else '处理这种情形0000000000000000000000000012344505454564646900000000000000000 Dim tmpValueArray As String() = Information.SplitValueToArray(value, gFirstAddressInfo.UnitNum) '忽略前面是零的项;每项,如全是0用一个0表示(000000000000=0),否则忽略开头的0(00000012300=12300) Dim mCurrentValue As String Dim mFirstIsZero As Boolean = True For i As Integer = 0 To tmpValueArray.Length - 1 mCurrentValue = Information.GetStringValue(tmpValueArray(i)) mFirstIsZero = mFirstIsZero And mCurrentValue.Equals("0") If Not mFirstIsZero Then gValueArray.Add(mCurrentValue) End If Next '最后面连续项全是0的,当进位处理,地址前移,移去这些项 Dim mLastIsZero As Boolean = True Dim mCount As Integer = 0 Do Until Not mLastIsZero mLastIsZero = mLastIsZero And gValueArray(gValueArray.Count - 1).Equals("0") If mLastIsZero Then mCount += 1 gValueArray.RemoveAt(gValueArray.Count - 1) End If Loop If mCount > 0 Then gFirstAddressInfo.Add(mCount) End If gLastAddressInfo = gFirstAddressInfo.Clone gLastAddressInfo.Add(gValueArray.Count - 1) End Sub Friend Sub New(ByVal value As String) gFirstAddressInfo = New AddressInformation() Initialize(value) End Sub Friend Sub New(ByVal unitNum As Integer, ByVal address As Integer, ByVal value As String) gFirstAddressInfo = New AddressInformation(unitNum) gFirstAddressInfo.Address = address Initialize(value) End Sub Friend Sub New(ByVal addressInfo As AddressInformation, ByVal value As String) gFirstAddressInfo = addressInfo.Clone Initialize(value) End Sub Public Function Test_OutPutValueInformation() As String Dim mFormat As String = "({0},{1})" Dim b As New System.Text.StringBuilder For i As Integer = 0 To Size - 1 b.Append(String.Format(mFormat, gLastAddressInfo.Address - i, gValueArray(i))) Next Return b.ToString End Function Private Function GetAddress(ByVal index As Integer) As Integer Return gLastAddressInfo.Address - index End Function Private Function GetIndex(ByVal Address As Integer) As Integer Return gLastAddressInfo.Address - Address End Function Public Function CopyToByIndex(ByVal unitNum As Integer, ByVal index As Integer) As StringValue Return CopyToByIndex(unitNum, index, index) End Function Public Function CopyToByIndex(ByVal unitNum As Integer, ByVal firstIndex As Integer, ByVal lastIndex As Integer) As StringValue If (firstIndex > lastIndex) OrElse (firstIndex < 0 OrElse lastIndex > Me.Size) Then Throw New Exception("引用索引无效") Dim mAddressInfo As AddressInformation = gFirstAddressInfo.ConvertTo(unitNum) mAddressInfo.ResetAddressFrom(GetAddress(lastIndex), gFirstAddressInfo.UnitNum) '地址以lastIndex地址为参照 Dim mValue As String mValue = Information.GetStringsValue(gValueArray, firstIndex, lastIndex, gFirstAddressInfo.UnitNum) Return New StringValue(mAddressInfo, mValue) End Function Public Function CopyToByAddress(ByVal unitNum As Integer, ByVal address As Integer) As StringValue Return CopyToByIndex(unitNum, GetIndex(address)) End Function Public Function CopyToByAddress(ByVal unitNum As Integer, ByVal firstAddress As Integer, ByVal lastAddress As Integer) As StringValue Return CopyToByIndex(unitNum, GetIndex(lastAddress), GetIndex(firstAddress)) End Function Public Function CopyTo(ByVal unitNum As Integer) As StringValue Return New StringValue(gFirstAddressInfo.ConvertTo(unitNum), Me.Value) End Function Public Function Clone() As StringValue Return CommonFunction.Clone(Of StringValue)(Me) End Function End Class
测试代码: Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click BigIntLibrary.Test.Run() End Sub
Public Class Test Public Shared Sub Run() Dim addressInfo As New AddressInformation(6) addressInfo.Address = 0 Dim Value As String = "700000000000800000000000000011111111110111111111111117880000000000000000000000088000000000000000000000000000000000000" Console.WriteLine(Value) Dim Result As New StringValue(addressInfo, Value) OutPut(Result) OutPut(Result.CopyTo(0)) OutPut(Result.CopyTo(1)) OutPut(Result.CopyTo(2)) OutPut(Result.CopyTo(3)) OutPut(Result.CopyTo(4)) OutPut(Result.CopyTo(5)) OutPut(Result.CopyTo(6)) OutPut(Result.CopyTo(5).CopyToByIndex(2, 0)) OutPut(Result.CopyTo(5).CopyToByIndex(2, 0, 1)) End Sub Private Shared Sub OutPut(ByVal info As StringValue) Console.WriteLine() With info Console.WriteLine("存储指数:{0},单位大小:{1},起始地址:{2}", .FirstAddressInfo.UnitNum, .UnitLength, .FirstAddressInfo.Address) Console.WriteLine("值:{0}", .Value) Console.WriteLine("存储情形:") Console.WriteLine(.Test_OutPutValueInformation) End With End Sub End Class 结果: 700000000000800000000000000011111111110111111111111117880000000000000000000000088000000000000000000000000000000000000 存储指数:6,单位大小:64,起始地址:0 存储指数:0,单位大小:1,起始地址:36 存储指数:1,单位大小:2,起始地址:18 存储指数:2,单位大小:4,起始地址:9 存储指数:3,单位大小:8,起始地址:4 存储指数:4,单位大小:16,起始地址:2 存储指数:5,单位大小:32,起始地址:1 存储指数:6,单位大小:64,起始地址:0 存储指数:2,单位大小:4,起始地址:26 存储指数:2,单位大小:4,起始地址:16 如果: Friend Class Information Private Sub New() End Sub Private Shared gUnitMaxSize As Integer Private Shared gUnitDataType As TypeCode ''' <summary> ''' 运算单元最大位数 ''' </summary> Public Shared ReadOnly Property UnitMaxSize() As Integer Get Return gUnitMaxSize '测试,不用太大。gUnitMaxSize End Get End Property 修改一下测试代码: Public Class Test Public Shared Sub Run() Dim addressInfo As New AddressInformation(6) addressInfo.Address = 0 Dim Value As String = "700000000000800000000000000011111111110111111111111117880000000000000000000000088000000000000000000000000000000000000" Console.WriteLine(Value) Dim Result As New StringValue(addressInfo, Value) OutPut(Result) OutPut(Result.CopyTo(0)) OutPut(Result.CopyTo(1)) OutPut(Result.CopyTo(2)) OutPut(Result.CopyTo(3)) OutPut(Result.CopyTo(4)) OutPut(Result.CopyTo(5)) OutPut(Result.CopyTo(6)) Result = Result.CopyTo(5) OutPut(Result.CopyToByIndex(2, Result.Size - 1)) OutPut(Result.CopyToByIndex(2, 0, Result.Size - 1)) OutPut(Result.CopyToByAddress(2, Result.FirstAddressInfo.Address, Result.LastAddressInfo.Address)) 结果是: 700000000000800000000000000011111111110111111111111117880000000000000000000000088000000000000000000000000000000000000 存储指数:6,单位大小:896,起始地址:0 存储指数:0,单位大小:14,起始地址:2 存储指数:1,单位大小:28,起始地址:1 存储指数:2,单位大小:56,起始地址:0 存储指数:3,单位大小:112,起始地址:0 存储指数:4,单位大小:224,起始地址:0 存储指数:5,单位大小:448,起始地址:0 存储指数:6,单位大小:896,起始地址:0 |