多数の課題がありますが、これは面白いかもしれません。
この課題では、剰余数システム(RNS)を使用して、大きな整数の加算、減算、乗算を実行します。
RNSとは
RNSは、整数を識別するために開発された多くの方法の1つです。このシステムでは、数値は剰余のシーケンス(モジュラス演算後の結果(整数除算後の剰余))で表されます。このシステムでは、各整数には多くの表現があります。物事をシンプルにするために、各整数が一意に表現されるように物事を制限します。具体的な例で何が起こっているかを説明する方が簡単だと思います。
最初の3つの素数、2、3、5を見てみましょう。RNSシステムでは、これら3つの数字を使用して、残基を使用して2 * 3 * 5 = 30未満の数字を一意に表すことができます。テイク21:
21は30未満なので、2、3、および5でモッドした後の結果を使用して表すことができます(つまり、整数を2、3、および5で除算した後の余り)
次の整数シーケンスで21を識別します。
21〜{21 mod 2、21 mod 3、21 mod 5} = {1、0、1}
したがって、RNSシステムでは、「21」の代わりに{1,0,1}を使用します。
一般に、整数nが与えられた場合、nは{ n mod 2、...、n mod p_k } として表されます。ここで、p_kは最小の素数であり、nはp_k以下のすべての素数の積より小さくなります。
別の例は、ので、私たちはここに2,3,5,7,11,13を使用する必要があり、我々は3412を持っていると言う2*3*5*7*11*13=30030
、一方2*3*5*7*11=2310
小さすぎます。
3412〜{3412 mod 2、3412 mod 3、3412、mod 5、...、3412 mod 13} = {0、1、2、3、2、6}
このシステムを使用すると、非常に大きな数を比較的簡単に表すことができます。{1、2、3、4、5、6、7、8、...}残基を使用して、最大{2、6、30、210、2310、30030、510510、9699690 ...}までの数を表すことができますそれぞれ。(ここにシリーズがあります)
私たちの仕事
これらの剰余を使用して、多数に対して+、-、および*を実行します。これらのプロセスを以下に説明します。ここでは、入力仕様と出力仕様を示します。
入力
stdinまたは関数の引数を介して2つの(潜在的に非常に大きい)数が与えられます。10桁の文字列として与えられます。
問題をさらに詳しく説明するために、最初の入力n
と2番目の入力を呼び出しますm
。n> m> = 0と仮定します。
また、実行する操作を指定する+
か、-
または*
指定します。
出力
ましょう、xは整数です。[ x ]を使用して、上記のxの RNS表現を参照します。
出力します [n] <operator> [m] = [result]
RNSで操作を実行する方法
これらの操作は比較的簡単です。RNS表記の2つの数値が与えられた場合、それらを加算、減算、または乗算するには、指定された演算をコンポーネントごとに実行し、モジュラスを取得します。
すなわち
{1、2、3} + {1、1、4} = {(1 + 1)mod 2、(2 + 1)mod 3、(3 + 4)mod 5} = {0、0、2}
2つの異なる数を表すために使用される残基の数が同じでない場合、操作を実行するとき、同じ数の残基を持つように「短い」数を拡張する必要があることに注意してください。これは同じプロセスに従います。例については、テストケースを参照してください。
結果がどちらの入力よりも多くの残基を必要とする場合も同じです。次に、両方の入力を「拡張」する必要があります。
重要な詳細
ここでは大きな数を扱いますが、arbitrarily意的に大きくはしません。最初の100個の素数の積までの数値を担当します(以下を参照)。このため、最初の100個の素数が無料で提供されます(バイトコストなし)。あなたはそれら
p
をあなたの言語と呼ばれる配列に固定するか、あなたの言語にとって慣用的なものにして、最終的な合計からこの配列を開始するために使用されるバイト数を引きます。もちろん、これは、それらがハードコーディングされているか、組み込みを使用して生成できることを意味します。何らかの理由でこれがあなたの言語で使用されるデフォルトの整数表現である場合。それは結構です。
言語のデフォルトでない限り、任意精度整数型を使用することはできません。デフォルトの場合、通常は64ビットに収まらない整数を格納するために使用することはできません。
明確にするために、各整数は常に可能な限り少ない残基で表されます。これは、入力と出力の両方に当てはまります。
他の仕様ではこれを防ぐ必要があると思いますが、冗長にするために、入力に対して所定の操作を実行してから、すべてをRNSに変更してから出力することはできません。RNSへの入力を変更してから、操作を実行して出力を生成する必要があります。
テストケース
入力:
n = 10
m = 4
+
出力:
{ 0, 1, 0 } + { 0, 1 } = { 0, 2, 4 }
説明:
まず、上記のように各番号をRNS表現に変更します。
10 ~ {0,1,0}
および4 ~ {0,1}
。コンポーネントごとの加算を行いたいとき、それ10
はより多くのコンポーネントを持っていることに注意してください4
。したがって、短い数字を「拡張」する必要があります。そこで、簡単に書き4 ~ {0,1} --> {0,1, 4 mod 5} = {0,1,4}
ます。ここで、加算を進めてからモジュラスを取得します。
- 入力
n=28
m=18
+
出力:
[ 0, 1, 3 ] + [0, 0, 3 ] = [ 0, 1, 1, 4 ]
- 入力(キーボードで顔を叩く)
n=1231725471982371298419823012819231982571923
m=1288488183
*
出力(読みやすくするために別々の行に分割):
[1, 2, 3, 6, 2, 10, 2, 1, 12, 16, 7, 15, 34, 29, 31, 5, 55, 32, 66, 61, 3, 76, 52, 14, 65, 44, 99, 57 ]
*
[1, 0, 3, 3, 4, 8, 9, 10, 8, 0 ]
=
[1, 0, 4, 4, 8, 2, 1, 10, 4, 0, 17, 7, 27, 21, 44, 51, 56, 9, 6, 9, 12, 0, 52, 36, 43, 68, 99, 24, 96, 39, 96, 66, 125]
n
28個の素数が必要です。m
10 n*m
が必要です。33 が必要です。
- 入力
n=8709668761379269784034173446876636639594408083936553641753483991897255703964943107588335040121154680170867105541177741204814011615930342030904704147856733048115934632145172739949220591246493529224396454328521288726490
m=1699412683745170450115957274739962577420086093042490863793456500767137147999161679589295549397604032154933975242548831536518655879433595016
-
出力:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 509]
-
[0, 2, 1, 6, 1, 12, 11, 18, 14, 28, 21, 36, 37, 42, 16, 52, 41, 60, 16, 70, 49, 78, 80, 88, 49, 100, 13, 106, 4, 112, 68, 130, 36, 138, 37, 150, 0, 162, 8, 172, 163, 180, 18, 192, 129, 198, 135, 222, 78, 228, 90, 238, 57, 250, 36, 262, 87, 270, 206, 280, 193, 292, 253, 310, 224, 316, 57, 336, 48, 348]
=
[0, 1, 4, 1, 10, 1, 6, 1, 9, 1, 10, 1, 4, 1, 31, 1, 18, 1, 51, 1, 24, 1, 3, 1, 48, 1, 90, 1, 105, 1, 59, 1, 101, 1, 112, 1, 0, 1, 159, 1, 16, 1, 173, 1, 68, 1, 76, 1, 149, 1, 143, 1, 184, 1, 221, 1, 182, 1, 71, 1, 90, 1, 54, 1, 89, 1, 274, 1, 299, 1, 266, 1, 228, 1, 340, 1, 170, 1, 107, 1, 340, 1, 88, 1, 157, 1, 143, 1, 22, 1, 22, 1, 58, 1, 296, 1, 371, 1, 140]
n
100個の素数を使用します。m
70個の素数を使用します。n-m
99個の素数を使用します。
ChineseRem
GAPの中国剰余定理の組み込み実装を使用してこれらをチェックしました(基本的にRNS番号を取得し、10進整数に変更します)。私は彼らが正しいと信じています。何か怪しいと思われる場合は、お知らせください。
気にする人にとって、最初の100個の素数の積は次のとおりです。
471193079990618495316248783476026042202057477340967552018863483961641533584503
422120528925670554468197243910409777715799180438028421831503871944494399049257
9030720635990538452312528339864352999310398481791730017201031090
この数は、指定されたシステムを使用して表現できる最大数(および100の素数の制限)よりも1大きくなります。
(a,b,o)=>a.map((v,i)=>eval(v+o+b[i]))
、たとえばES6でのみです。おそらく最も難しいのは、任意の精度の演算を使用せずに結果を表すために必要な素数を見つけることだと思いますが、その後のRNSへの変換は簡単ではありません。
1234,1234,+
か()?