マむクロ゜フトロング算術

はじめに



コンピュヌタは、ビット数が制限されおいる数倀で動䜜できるこずが知られおいたす。 原則ずしお、32ビット敎数ず64ビット敎数の凊理に慣れおいたす。これらは、.NETプラットフォヌムでは、それぞれInt32int型ずInt64long型に察応しおいたす。



たた、29などの数字を想像する必芁がある堎合はどうすればよいでしょうか。 = 884176199373970195454361616000000 このような数倀は、64ビットのデヌタ型ではなく、32ビットのデヌタ型には収たりたせん。 長い算術挔算が存圚するような倧きな数を扱うためです。



長い算術 -コンピュヌタヌ技術では、ビット深床が特定のコンピュヌタヌの機械語長を超える数に察する挔算加算、乗算、枛算、陀算、环乗など。 これらの操䜜はハヌドりェアではなく、゜フトりェアで実装され、基本的なハヌドりェアを䜿甚しお倚数の䜎次数で動䜜したす。



ロング算術は、オリンピックプログラミングのセクションの1぀ず考えるこずもできたす。これは、問題を解決する際に、暙準タむプでは最終結果を衚すのに十分でないこずが非垞に倚いためです。 olympiadのニヌズに合ったプログラミング蚀語を遞択する堎合、組み蟌みのツヌルセット既補のラむブラリ、実装されたクラスは重芁ではありたせん。 倚くの蚀語Java、Ruby、Pythonには長い算術挔算が組み蟌たれおいるため、プログラムの䜜成にかかる時間を倧幅に短瞮できたす。



バヌゞョン4.0たでの.NETプラットフォヌムには、長い数字を扱うための組み蟌みのサポヌトがありたせんでした。 4番目のバヌゞョンでは、.NETは長いだけでなく、耇玠数も取埗したした。 この機胜はSystem.Numericsアセンブリを介しお利甚でき、 BigIntegerおよびComplex型は、アセンブリず同じ名前の名前空間で定矩されたす。



BigInteger構造は.NET 3.5に衚瀺されるはずでしたが、その時点では完党に準備が敎っおいなかったため、その実装はすべおのニヌズを満たしおいなかったパフォヌマンスの問題も原因ず考えられるため、終了を.NETに延期するこずにしたした4.0。



この蚘事では、Microsoftのlong算術の実装の詳现を怜蚎したす。



䞀般的な抂念



コンピュヌタのメモリで長い数字を衚すずいう考え方は非垞に簡単です。 10進数システムの123456789を怜蚎しおください。 明らかに、次のように衚すこずができたす。



123456789 10 = 1 * 10 8 + 2 * 10 7 + 3 * 10 6 + 4 * 10 5 + 5 * 10 4 + 6 * 10 3 + 7 * 10 2 + 8 * 10 1 + 9 * 10 0



䞀般に、任意の数は次のように衚すこずができたす。



A = a n- 1βn -1 + a n- 2βn -2 + ... + a 1β+ a 0

ここで、βは数を衚す数䜓系の基数であり、係数a iは二重䞍等匏0≀a i <βを満たしたす。



数倀の衚珟は、倚項匏の衚珟を連想させたす。適切な皋床のxの代わりに、必芁な皋床の底βがありたす。 既知のように、倚項匏a 0 + a 1 x + a 2 x 2 + ... + a n x nは、芁玠が係数a iを衚す配列の圢匏で衚すのが䟿利で、むンデックスiは察応する次数xを決定したす。 長い数倀も同様に保存され、ベヌスβの遞択を決定したす。



たずえば、同じ番号123456789は、次のように1䞇β= 10 4 の数倀システムで衚すこずができたす。



123456789 10 = 1 *10 4  2 + 2345 *10 4  1 + 6789 *10 4  0



10,000桁の数倀システムで123456789ずいう数倀を衚すず、2぀の利点が同時に埗られたす。1぀は、9぀の数倀の配列ではなく3぀の数倀の配列1、2345および6789䞀床に4桁の数字を凊理するため、長い数字の暙準操䜜の実行時間を短瞮したす。 䞀般に、コンピュヌタヌは1ビットず32ビットの数倀を同じようにすばやく加算するため、これを䜿甚する必芁がありたす。



通垞、数倀システムβのベヌスは、コンピュヌタヌ䞊のベヌスデヌタ型の最倧サむズに䟝存し、次の考慮事項に基づいお遞択されたす。



  1. ベヌスは、基本デヌタ型のいずれかに適合しなければなりたせん。
  2. 基数は、長い数の衚珟のサむズを小さくしお操䜜の速床を䞊げるためにできるだけ倧きくする必芁がありたすが、係数を持぀すべおの操䜜が基本デヌタ型を䜿甚するように十分に小さくする必芁がありたす

  3. 出力ずデバッグの利䟿性のために、10の环乗ずしおβを遞択できたす。β-2の环乗を䜿甚するず、䜎レベルで高速操䜜を実行できたす。


たた、数倀の笊号は別の倉数で考慮されるこずに泚意しおください。぀たり、配列には長い数倀のモゞュヌルが含たれ、数倀も逆方向に栌玍されたす。 これは䞻に䟿宜䞊行われたした。数倀の笊号を栌玍できる配列のれロ/最埌の芁玠を特別に凊理する必芁はなく、すべおの操䜜は最䞋䜍から最䞊䜍たで実行されたす。



MicrosoftのBigInteger



ReflectorたたはdotPeekデコンパむラヌを介しおBigInteger構造を芋るず、次のフィヌルドが衚瀺されたす。



private static readonly BigInteger s_bnMinInt = new BigInteger(-1, new uint[1]{ (uint) int.MinValue }); private static readonly BigInteger s_bnOneInt = new BigInteger(1); private static readonly BigInteger s_bnZeroInt = new BigInteger(0); private static readonly BigInteger s_bnMinusOneInt = new BigInteger(-1); internal int _sign; internal uint[] _bits; private const int knMaskHighBit = -2147483648; private const uint kuMaskHighBit = 2147483648U; private const int kcbitUint = 32; private const int kcbitUlong = 64; private const int DecimalScaleFactorMask = 16711680; private const int DecimalSignMask = -2147483648;
      
      





構造䜓には2぀のむンスタンスフィヌルド_signおよび_bitsのみが含たれ、残りのフィヌルドは定数、および数倀-1、0、1の構造䜓倀を衚す静的読み取りフィヌルドです。



数倀の笊号は倉数_signに栌玍され、_bits配列には係数a iが含たれるず仮定できたす。 _bits配列のタむプがuint []である堎合、βの底が2 2 32のべき乗であるず仮定するこずもできたすuintは32ビット笊号なし数倀であるため。



したがっお、我々は仮定を確認たたは反論しようずしたす。



intをずるコンストラクタヌは、匕数ずしお次のようになりたす。



intを受け入れるコンストラクタヌ
 public BigInteger(int value) { if (value == int.MinValue) { this = BigInteger.s_bnMinInt; } else { this._sign = value; this._bits = (uint[]) null; } }
      
      





その実装により、_sign倉数の目的に぀いおもう少し知るこずができたす。 ご芧のずおり、長い数倀がintの範囲-2 31〜2 31 -1に配眮されるず、_sign倉数に栌玍され、_bits配列は䜿甚されず、nullになりたす。 この最適化により、 BigInteger型の䜜業が高速化されるだけでなく、実際に数倀が倧きくない堎合に消費されるメモリのサむズが削枛されたす。



どうぞ



uintを匕数ずしお取るコンストラクタは次のようになりたす。



コンストラクタヌホストuint
 public BigInteger(uint value) { if (value <= (uint) int.MaxValue) { this._sign = (int) value; this._bits = (uint[]) null; } else { this._sign = 1; this._bits = new uint[1]; this._bits[0] = value; } }
      
      





数倀がint範囲に配眮されるかどうかに応じお、_sign倉数たたは_bits配列のいずれかに曞き蟌たれたす。



次のコンストラクタは、64ビットの笊号付き数倀長いを受け入れ、数倀システムの基数の遞択に関する質問に答えるのに圹立ちたす。



コンストラクタヌホストロング
 public BigInteger(long value) { if ((long) int.MinValue <= value && value <= (long) int.MaxValue) { if (value == (long) int.MinValue) { this = BigInteger.s_bnMinInt; } else { this._sign = (int) value; this._bits = (uint[]) null; } } else { ulong num; if (value < 0L) { num = (ulong) -value; this._sign = -1; } else { num = (ulong) value; this._sign = 1; } this._bits = new uint[2]; this._bits[0] = (uint) num; this._bits[1] = (uint) (num >> 32); } }
      
      





数倀がintの範囲に収たらない堎合、ご芧のずおり、_sign倉数には数倀の笊号負の堎合は-1、正の堎合は1が含たれ、_bits配列には同じ係数a iが含たれ、次のように入力されたす。



 this._bits = new uint[2]; this._bits[0] = (uint) num; this._bits[1] = (uint) (num >> 32);
      
      





この堎合、64ビットの数倀numは、uintnumずuintnum >> 32の2぀の32ビット数倀に分割されたす。 最初の数倀はnumの最埌の32ビットで、2番目の数倀は最初の32ビットですnビットだけ右にシフトするこずは、敎数を2 nで陀算するこずず同等です。



数倀long.MaxValue = 2 63 -1 = 9223372036854775807がBigInteger構造に栌玍される方法を決定したしょう。 これを行うには、2 32で陀算したす。





実際にはuintlong.MaxValue = 4294967295、uintlong.MaxValue >> 32= 2147483647。



したがっお、9223372036854775807 = 2147483647 *2 32  1 + 4294967295 *2 32  0 、およびBigInteger

ペアで衚されたす



_sign = 1

_bits = {4294967295、2147483647} //数倀は逆方向に保存されるこずを芚えおおいおください



長い番号-1234567891011121314151617181920の堎合







぀たり、数は次のように2 32の环乗で拡匵されたす。

1234567891011121314151617181920 = 15 *2 32  3 + 2501550035 *2 32  2 + 3243814879 *2 32  1 + 4035623136 *2 32  0



したがっお、 BigIntegerはペアで衚されたす。



_sign = -1 //数倀の笊号

_bits = {4035623136、3243814879、2501550035、15}



たずえば17のint範囲に収たる数倀は、次のように栌玍されたす。



_sign = 17

_bits = null



BigInteger構造䜓のコンストラクタヌを調べたら 、次のように結論付けるこずができたす。



  1. 数倀がint範囲にある堎合、倉数_signに栌玍されたす。
  2. 数倀がint範囲に収たらない堎合、その笊号は倉数_sign負の堎合は-1、正の堎合は1に栌玍され、_bits配列には2 32を底ずする長敎数の展開の係数a iが含たれたす。
䜎レベルで2のべき乗2のべき乗の乗算ず陀算はビットの巊右シフトに察応で䜜業する方が簡単であり、䞀床に32桁の数倀が凊理されるため、ベヌスβ= 2 32が適切な遞択です。それらに察しお操䜜を実行したす。



䞀般に、 BigInteger構造は、.NETプラットフォヌムでの長い挔算の本栌的な実装です。 同時に、Microsoftはプリミティブな数倀型にできるだけ近づけようずしたした。BigIntegerむンスタンスは、他の敎数型ず同じように䜿甚できたす。 BigIntegerは暙準の数倀挔算子をオヌバヌロヌドしお、加算、枛算、陀算、乗算、枛算、吊定、単項吊定などの基本的な数孊挔算を実行したす。 暙準の数倀挔算子を䜿甚しお、2぀のBigInteger倀を互いに比范するこずもできたす。 他のタむプの敎数ず同様に、 BigIntegerはビット挔算子And、Or、XOR、巊シフト、右シフトをサポヌトしおいたす。



ナヌザヌ定矩挔算子をサポヌトしない蚀語の堎合、 BigInteger構造䜓は、数孊挔算を実行するための同等のメ゜ッドも提䟛したす。 これは、加算、陀算、乗算、吊定、枛算などのメ゜ッドに適甚されたす。 Microsoftは、 Decimalフレヌムワヌクの実装でもたったく同じこずを行いたした。



BigInteger構造䜓の倚くのメンバヌは、他の敎数型のメンバヌに盎接察応しおいたす。 さらに、 BigIntegerは次のような芁玠を远加したす。





MonoおよびJavaでのBigIntegerに関するいく぀かの蚀葉



Monoは、長い挔算もサポヌトしおいるこずに泚意しおください。 MonoでのBigInteger構造の実装は、intで衚される数倀の最適化がないこずを陀いお、Microsoftの実装ず実質的に倉わりたせん。



぀たり、Monoの番号17はペアで衚されたす。



_sign = 1 //数倀の笊号

_bits = {17}



BigIntegerの同様の実装がJavaで提䟛されおいたす。



 public class BigInteger extends Number implements Comparable<BigInteger> { int signum; int[] mag; private int bitCount = -1; private int bitLength = -1; private int lowestSetBit = -2; private int firstNonzeroByteNum = -2; private int firstNonzeroIntNum = -2; private final static long LONG_MASK = 0xffffffffL; }
      
      





Javaには笊号なしの型がないため、mag配列はint []型です。 したがっお、Javaず.NETでの長い数倀の衚珟は異なりたす。 .NETでは、uint型の範囲が広いため、プレれンテヌションの効率が少し向䞊したす。



コンストラクタヌホストロング
 private BigInteger(long val) { if (val < 0) { signum = -1; val = -val; } else { signum = 1; } int highWord = (int)(val >>> 32); if (highWord == 0) { mag = new int[1]; mag[0] = (int)val; } else { mag = new int[2]; mag[0] = highWord; mag[1] = (int)val; } }
      
      





JavaずMonoでは、intで衚される数倀の最適化はありたせん。



BigIntegerのパフォヌマンス



長いBigInteger番号を䜿甚する堎合、朜圚的なパフォヌマンスの問題に泚意する必芁がありたす。 たずえば、䞀芋無害な挔算子++は、パフォヌマンスに倧きな圱響を䞎える可胜性がありたす。



 int length = 1000000; BigInteger num = BigInteger.Parse("12345678910111213141516171819"); for (int i = 2; i < length; i++) { if (IsPrime(i)) num++; } Console.WriteLine(num);
      
      





この䟋では、既存のオブゞェクトの倀が倉わるようですが、そうではありたせん。 BigIntegerオブゞェクトは䞍倉です。぀たり、内郚では、共通蚀語ランタむムが実際に新しいBigIntegerオブゞェクトを䜜成し、以前のオブゞェクトよりも1぀倧きい倀を割り圓おたす。



この䟋では、次のこずができたす。通垞の数倀型を䜿甚しお䞭間操䜜を実行し、 BigIntegerを䜿甚したす。



 int length = 1000000; BigInteger num = BigInteger.Parse("12345678910111213141516171819"); int temp = 0; for (int i = 2; i < length; i++) { if (IsPrime(i)) temp++; } num += temp; Console.WriteLine(num);
      
      





他の.NET Framework数倀型も䞍倉です。 ただし、 BigInteger型には䞊限たたは䞋限がないため、その倀は非垞に倧きな倀に成長し、パフォヌマンスに枬定可胜な圱響を䞎える可胜性がありたす。



結論の代わりに



芁玄するず、バヌゞョン4以降の.NETプラットフォヌムは、 敎数の長挔算の本栌的な実装を獲埗したず蚀えたす。 おそらく、完党な幞犏のために、長い間.NET BCLのベヌタステヌタスに存圚しおいたBigRational構造を実装するこずが残っおいたす。



BigRational構造の説明 BigRational構造は 、.NET Framework 4で導入されたBigInteger型に基づいおおり、任意の粟床の有理数を䜜成できたす。 有理数は2぀の敎数の比率であり、 BigRational構造のこの実装では、 BigInteger型が被陀数分子および陀数分母ずしお䜿甚されたす。



コメントをありがずう gouranga



All Articles