残基番号システム


26

多数の課題がありますが、これは面白いかもしれません。

この課題では、剰余数システム(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は最小の素数であり、np_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番目の入力を呼び出しますmn> 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への入力を変更してから、操作を実行して出力を生成する必要があります。

テストケース

  1. 入力:

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}ます。ここで、加算を進めてからモジュラスを取得します。

  1. 入力
n=28
m=18
+

出力:

 [ 0, 1, 3 ] + [0, 0, 3 ] = [ 0, 1, 1, 4 ]
  1. 入力(キーボードで顔を叩く)
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] 

n28個の素数が必要です。m10 n*mが必要です。33 が必要です。

  1. 入力
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]

n100個の素数を使用します。m70個の素数を使用します。n-m99個の素数を使用します。

ChineseRemGAPの中国剰余定理の組み込み実装を使用してこれらをチェックしました(基本的にRNS番号を取得し、10進整数に変更します)。私は彼らが正しいと信じています。何か怪しいと思われる場合は、お知らせください。


気にする人にとって、最初の100個の素数の積は次のとおりです。

471193079990618495316248783476026042202057477340967552018863483961641533584503
422120528925670554468197243910409777715799180438028421831503871944494399049257
9030720635990538452312528339864352999310398481791730017201031090

この数は、指定されたシステムを使用して表現できる最大数(および100の素数の制限)よりも1大きくなります。

やや関連


手術を行うことは最も難しい部分ではありませんが、この挑戦​​について私は不思議に思います。
njpipeorgan

@njpipeorgan私は同意します、操作の実行は(a,b,o)=>a.map((v,i)=>eval(v+o+b[i]))、たとえばES6でのみです。おそらく最も難しいのは、任意の精度の演算を使用せずに結果を表すために必要な素数を見つけることだと思いますが、その後のRNSへの変換は簡単ではありません。
ニール

このような入力をすることはできます1234,1234,+か()?
clismique

@derpfacePython yes関数も受け入れられます
Liam

「コンポーネントごとに指定された操作を単純に実行する」 -出力の余分なコンポーネントはどこから来るのでしょうか?
-smls

回答:


6

ギャップ

ある程度の背景:この質問を作成したとき、何ヶ月も前に、この質問の難しい部分を解決する方法がなかったことを認めます:使用する素数の正しい数を決定します。このサイトには非常に賢い人がたくさんいます。私は、誰かがかなり早くそれを行う方法を見つけ出すと本当に期待していました。しかし、これは起こらなかったため、この問題を本当に解決できるかどうかさえわかりませんでした。そのため、時間をかけてメソッドを考案する必要がありました。私がやったことは、このチャレンジのルールを破らないと信じています。もちろん、これを事実確認するのが大好きです。

また、ソリューションが通常はタグ形式に合うよりも少し深いため、選択を少し後悔しています。これを言って、サイトのルールに従うために、この投稿の下部に私のソリューションの「ゴルフ」バージョンがあります。


コード

### The first 100 primes;
primes := Primes{[1..100]};

### In many of the functions below, the 'string' variable is a string of digits
###


### Returns the 'index' digit of 'string' as an integer
GetValueAsInt := function(string, index) 
    return IntChar(string[index]) - 48;
end;

### Used in the 'modulus' function. See that comment for more information. 
### Calculates the contribution to the modulus of a digit 'digit' in the 10^power place.
### 'integer' is the modulus
digit_contribution := function(digit, integer, power)
    local result, i;
    result := 1;
    for i in [0..power-1] do
        result := ( result * (10 mod integer) ) mod integer;
    od;
    result := (result * (digit mod integer) ) mod integer;
    return result;
end;

### This modulus function is used to calculate the modulus of large numbers without storing them
##### as large numbers.
### It does so by breaking them into digits, and calculating the contribution of each digit.
### e.g. 1234 mod 5 = (1000 mod 5)(1 mod 5) + (200 mod 5)(2 mod 5) + (10 mod 5)(3 mod 5) + (4 mod 5)
### It actually mods after every calculation to ensure that we never get a number larger
##### than the modulus ('integer') squared, which will never be even close to 10^64-1
modulus := function(string, integer)
    local i, result, digit, len;
    len := Length(string);
    result := 0;
    for i in [1..len] do
        digit :=  IntChar(string[i]) -48;
        result := ( result + digit_contribution(digit, integer, len-i) )  mod integer;
    od;
    return result;
end;

### This returns the product of the first i-1 primes (mod j). It must not (and does not)
##### ever store an integer larger than 2^64-1
phi_i := function(i,j)
    local index, result;
    result := 1;
    for index in [1..i-1] do
        result := ( result * primes[index] ) mod primes[j];
    od;
    return result;
end;

### Calculates the first residues of 'string' mod the first 100 primes
get_residues := function(string) 
    local p, result;
    result := [];
    for p in primes do
        Add( result, modulus(string, p) );  
    od; 
    return result;
end;

### Gets the ith element in the partial_chinese array, given the previous elements
### See the explanation section and partial_chinese function for more info
get_partial_i := function( i, residues, previous_array )
    local index, result;
    result := residues[i];
    for index in [1..Length(previous_array)] do
        result := ( result - previous_array[index]*phi_i(index,i) ) mod primes[i]; 
    od;     
    result := ( result / phi_i(i,i) ) mod primes[i];
    return result;
end;

### returns an array such that the sum of prod_primes(i)*array[i] is equal to the integer value
##### that is represented by the residues. (It basically just does the CRT without
##### actually summing everything.) prod_primes(i) is the product of the first i-1 primes 
### See the explanation for a bit more info
### This is what allows us to determine the minimal number of primes to represent a RNS number
partial_chinese := function( string )
    local array, i, residues;
    residues := get_residues(string);
    array := [];        
    for i in [1 .. Length(primes)] do
        Add( array, get_partial_i( i, residues, array ) );
    od;
    return array;   
end;

### Same as partial_chinese but takes input in a different form.
partial_chinese_from_residues := function(residues)
    local array, i;
    array := [];        
    for i in [1 .. Length(primes)] do
        Add( array, get_partial_i( i, residues, array ) );
    od;
    return array;
end;

### gives you the number of primes needed to represent an integer. Basically asks how 
##### many trailing zeros there are in the chinese array.
get_size := function(string)
    local array, i, len, result;
    array := partial_chinese(string);
    len := Length(array);
    for i in [0..len-1] do
        if  not (array[len-i] = 0) then
            return len -i;
        fi; 
    od; 
    Print("ERROR: get_size().\n");
    return 0;
end;

### Same as above but with different input format
get_size_from_residues := function(residues)
    local array, i, len, result;
    array := partial_chinese_from_residues(residues);
    len := Length(array);
    for i in [0..len-1] do
        if  not (array[len-i] = 0) then
            return len -i;
        fi; 
    od; 
    Print("ERROR: get_size().\n");
    return 0;
end;

### the actual function. inputs are all strings
f := function(in1, in2, opperation)
    local residues_1, residues_2, residues_result, i;
    residues_1 := get_residues(in1);
    residues_2 := get_residues(in2);
    residues_result := [];
    if opperation = "+" then
        for i in [1..Length(primes)] do
            Add( residues_result, ( residues_1[i] + residues_2[i] ) mod primes[i]);
        od;     
    elif opperation = "*" then
        for i in [1..Length(primes)] do
            Add( residues_result, ( residues_1[i] * residues_2[i] ) mod primes[i]);
        od;     
    elif opperation = "-" then
        for i in [1..Length(primes)] do
            Add( residues_result, ( residues_1[i] - residues_2[i] ) mod primes[i]);
        od;     
    fi;
    Print(residues_1{[1..get_size(in1)]}, " ", opperation, " ", residues_2{[1..get_size(in2)]}, " = ", residues_result{[1..get_size_from_residues(residues_result)]} );
end;

説明:

最初に、両方の入力に対して100個すべての剰余を計算します。これmodulusは、コード内の関数を使用して行います。modすべてのステップの後に組み込み関数を使用するように注意しました。これにより540^2、が100の2乗よりも1小さいより大きい数を持たないことが保証されます。

すべての残基を取得したら、指定された操作とmod各エントリを再度実行できます。これで結果に一意の指定子ができましたが、結果と各入力を表すために使用する必要があるエントリの最小数を決定する必要があります。

実際に必要な残留物の数を把握することが、この問題の最も難しい部分です。これを決定するために、中国剰余定理(CRT)のほとんどの手順を実行します。ただし、数字が大きくなりすぎないように、明らかに変更する必要があります。

ましょうprod(i)最初のi-1素数の合計になります。例えば、

prod(1) = 1
prod(2) = 2
prod(3) = 6
prod(4) = 30
etc

ましょうX整数です。ましょう{r_i}の残基X、つまり

r_i = X mod p_i

番目の素数p_iはどこですかi。これは1<i<=100私たちの場合です。

今、私たちはシーケンスを見つけるために、CRTを使用しようとしている{u_i}以上の合計ようにiのがprod(i) * u_iに等しいですX。それぞれu_iも技術的には残基であることに注意してくださいu_i < p_i。さらに、もしX < prod(i)それからu_i = 0。これは非常に重要です。つまり、後続のゼロを調べることで、RNSでr_i実際に表す必要がある残基の数を判断できるということXです。

のシーケンスを調べたい場合u_ipartial_chinese関数はu_iシーケンスを返します。

CRTをいじることで、u_i値の再帰式を見つけ、必要な残基の数を決定する問題を解決することができました。

式は次のとおりです。

u_i = [ r_i - SUM ] / prod(i)       (mod p_i)

どこSUMの総和であるj in [1,i)のはu_j * prod(i)

もちろん、prod(i)大きすぎるため実際には計算できない場合があります。この目的のために、phi_i関数を使用しました。この関数はを返しますprod(j) (mod p_i)。それmodはすべてのステップであるため、大きすぎるものを実際に計算することはありません。

この式の由来を知りたい場合は、ウィキペディアのページで見つけることができるCRTの例をいくつかお勧めします

最後に、各入力および出力について、u_iシーケンスを計算してから、後続ゼロを決定します。次にr_i、残基シーケンスの最後からその多くを捨てます。


「ゴルフ」コード、2621バイト

primes:=Primes{[1..100]};GetValueAsInt:=function(string,index)return IntChar(string[index])-48;end;digit_contribution := function(digit, integer, power)local result, i;result:=1;for i in [0..power-1] do result := ( result * (10 mod integer) ) mod integer;od;result:=(result*(digit mod integer) ) mod integer;return result;end;modulus:=function(string, integer)local i,result,digit,len;len:=Length(string);result:=0;for i in [1..len] do digit:= IntChar(string[i])-48;result:=(result+digit_contribution(digit,integer,len-i)) mod integer;od;return result;end;phi_i:=function(i,j)local index,result;result:=1;for index in [1..i-1] do result:=(result*primes[index] ) mod primes[j];od;return result;end;get_residues:=function(string) local p,result;result:=[];for p in primes do Add(result,modulus(string,p));od;return result;end;get_partial_i:=function(i,residues,previous_array)local index,result;result:=residues[i];for index in [1..Length(previous_array)] do result:=(result-previous_array[index]*phi_i(index,i) ) mod primes[i];od;result:=(result/phi_i(i,i)) mod primes[i];return result;end;partial_chinese:=function(string)local array,i,residues;residues:=get_residues(string);array:=[];for i in [1 .. Length(primes)] do Add(array,get_partial_i(i,residues,array));od;return array;end;partial_chinese_from_residues:=function(residues)local array,i;array:=[];for i in [1..Length(primes)] do Add(array,get_partial_i(i,residues,array));od;return array;end;get_size:=function(string)local array,i,len,result;array:=partial_chinese(string);len:=Length(array);for i in [0..len-1] do if not (array[len-i] = 0) then return len-i;fi;od;Print("ERROR: get_size().\n");return 0;end;get_size_from_residues:=function(residues)local array,i,len,result;array:=partial_chinese_from_residues(residues);len:=Length(array);for i in [0..len-1] do if not (array[len-i]=0) then return len-i;fi;od;Print("ERROR: get_size().\n");return 0;end;f:=function(in1,in2,opperation)local residues_1,residues_2,residues_result,i;residues_1:=get_residues(in1);residues_2:=get_residues(in2);residues_result:=[];if opperation = "+" then for i in [1..Length(primes)] do Add(residues_result,(residues_1[i]+residues_2[i] ) mod primes[i]);od;elif opperation = "*" then for i in [1..Length(primes)] do Add(residues_result,(residues_1[i]*residues_2[i])mod primes[i]);od;elif opperation = "-" then for i in [1..Length(primes)] do Add(residues_result,(residues_1[i]-residues_2[i]) mod primes[i]);od;fi;Print(residues_1{[1..get_size(in1)]}, " ", opperation, " ", residues_2{[1..get_size(in2)]}, " = ", residues_result{[1..get_size_from_residues(residues_result)]} );end;

通常のRNSでは必要に応じてディメンションが変更されないため混乱していますが、必要なディメンションのみを計算してから操作を実行するのではなく、入力から拡張100剰余数を計算してルールを曲げませんか?「最初に、各番号を上記のように RNS表現に変更します」とは、「RNS」番号には、何かを実行する前に必要な残基のみが含まれていることを意味します。
ライナス

@Linusに申し訳ありませんが、私はすでにこれに応答したと思いました。私はあなたに同意しますが、必要な変更(私が行う)は比較的些細なことだと思います。ご覧のとおり、操作を実行する前に入力の剰余長を計算するだけです。3つの数値すべてに対して100個の素数をすべて使用すると、すべての数値が上記の範囲にあるという事実を活用するだけです。
リアム

@Linusと最初の質問への回答では、通常、すべての数値は同じ数の残基を使用します。これにより、質問がはるかに簡単になります
リアム

2

ゴルフではないMathematica

rns[d_,l_]:=Table[Reap[
    FoldPairList[Sow@QuotientRemainder[10#+#2,Prime@i]&,0,d]
  ][[2,1,-1,2]],{i,l}];
plus[a_,b_]:=Mod[a+b,Prime@Range@Length@a];
subtract[a_,b_]:=Mod[a-b,Prime@Range@Length@a];
times[a_,b_]:=Mod[a b,Prime@Range@Length@a];
mag[f_]:=LengthWhile[FoldList[#/#2&,f,Prime@Range@100],#>1.1&];
ext[m_,n_,i_]:=Fold[Mod[1##,Prime@i]&,m,Prime@Range@n];
multi[e_,p_,t_]:=Tr@Position[Mod[e Range@p,p],p-t];
appx[d_] := N@FromDigits[{d~Take~UpTo[6], Length@d}]
  • 関数はrns[d_,l_]、基数10のd整数を長さのRNS整数に変換しlます。

  • 関数plus/ times/ subtract加算/乗算/ 1つのRNS整数を別のRNS整数から減算します。どちらも同じ長さです。

  • 関数は、RNS表現の長さの下限に関してmag[f_]、浮動小数点数のおおよその大きさを推定しfます。

  • 関数は、ext[m_,n_,i_]の積の除算から余り見つけ出しmPrime[Range@n]によってをPrime[i]

  • 関数multi[e_,p_,t_]はそれmを満たす最小の乗数を与えるDivisible[m*e+t,p]

  • 関数は、10進整数のappx[d_]最初の6桁を取り、その近似浮動小数点値を提供します。


上記の関数の助けを借りて、結果の長さを判断するという、厄介な問題を解決できるようになりました

まず、整数のRNS長さを決定するのは簡単な作業ではないことを明確にする必要があります。小さな整数については、素数の積と直接比較できます。ただし、非常に大きな整数の場合、素数の積を無限に正確に計算することは禁止されているため、このような比較は機能しなくなります。

例えば、素数の積と仮定1するが30あり3.16*10^46、周りの整数のRNSの長さは3.16*10^46、おそらくすることができ29、または30。機能がmag与える29両方のことを示し、これらの整数のための基準として29及び30可能です。

大きさがわかったら、その大きさに従って整数を直接表現し、真の長さを計算することを望みます。ここでのトリックは、元の番号にいくつかの新しい番号を追加し、その表現がすべてゼロになるまでRNS表現を変更することです。

例えば、mag[211.]であり4、その長さの4表現です{1, 1, 1, 1}

step 1:   {1,1,1,1} -> {0,2,2,2}  by adding  (1) * 1 = 1
step 2:   {0,2,2,2} -> {0,0,1,6}  by adding  (2) * 2 = 4
step 3:   {0,0,1,6} -> {0,0,0,2}  by adding  (2*3) * 4 = 24
step 4:   {0,0,0,2} -> {0,0,0,0}  by adding  (2*3*5) * 6 = 180
step 5:   calculate 211 + (1 + 4 + 24 + 180) ~ 420

数を追加することにより2112102*3*5*7)で割り切れる最小の数に増加します。そして今、私たちは、元の数がの「約」に等しいので210、よりも大きいと結論付けます。から始めると、最終的な数字が「およそ」であると想像するのは難しくありません。420210209210

関数length[f_,n_]は、浮動小数点値fを使用してマグニチュードを推定し、RNS表現に基づいて修正しますn

length[f_,n_]:=With[{g=mag@f},
    g+If[#==0,1,Round[(#+f)/Times@@Prime@Range@g]-1]&[
      FoldList[Times,1.,Prime[Range[g-1]]].
      FoldPairList[
        Block[{i=#2,m},
          {m=multi[ext[1,i-1,i],Prime@i,Part@##],rnsPlus[#,ext[m,i-1,#]&/@Range[g]]}
        ]&,n,Range[g]]]]

関数rnsOperation[a_,b_,op_,rnsop_]rnsop[a,b]op浮動小数点値に基づいて近似結果を取得するために使用される対応する通常の操作を提供します。

rnsOperation[a_,b_,op_,rnsop_]:=Block[{c=op[appx@a,appx@b],m},
    m=mag[c];m=length[c,rnsop[rns[a,m],rns[b,m]]];rnsop[rns[a,m],rns[b,m]]]

rnsOperation[
    IntegerDigits@1231725471982371298419823012819231982571923,
    IntegerDigits@1288488183,
    Times, times]
(* {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} *)

1
残念ながら、ヘルプセンターで概説されているルールでは、すべての提出物が使用中の勝利基準の真剣な候補者である必要があります。コードゴルフコンテストの場合、これはすべての提出物がゴルフされなければならないことを意味します。
デニス

@Dennisこのルールについて知っています。しかし、ゴルフをしなくても、この問題は非常に難しく複雑です。そのため、ゴルフではなくこの問題を解決することが私の目標です。
-njpipeorgan

これはゴルフではないかもしれませんが、私のJavaプログラム:Pと比較して非常に短いですが、私のプログラムはおそらくはるかに高速です。
うまくいけば

1
あなたはこれをゴルフすることができるように感じる
ローハンジュンジュンワラ

2

Python 3、435バイト

この挑戦はしばらくの間私のバケットリストにありましたが、それはごく最近のことです:a)実際に答えを試みることに時間と注意を注いだ。b)対数と中国の剰余定理のいくつかの不浄な組み合わせを使用して、数値のサイズ(および原始数のサイズと比較することで素数の数)を計算する私のアイデアを実際にテストしました。残念なことに、たとえばlarge_primorial + 3必要な素数の数を決定しようとしながら対数で作業すると、浮動小数点の問題を回避する方法を見つける必要がありました。

そして、これはリアムの答えのポートです。

オンラインでお試しください!

from functools import reduce as R
G=range
d=lambda s:[R(lambda z,c:(z*10+int(c))%q,s,0)for q in p]
h=lambda j,i:R(lambda z,q:z*q%p[i],p[:j],1)
def s(r):
 a=[];z=99
 for i in G(100):
  P=p[i];u=r[i]
  for j in G(len(a)):u=(u-a[j]*h(j,i))%P
  for k in G(1,P):
   if h(i,i)*k%P<2:break
  a+=u*k%P,
 while(a[z]<1)*z:z-=1
 return r[:z+1]
def f(a,b,n):u=d(a);v=d(b);print(s(u),n,s(v),'=',s([eval(str(u[i])+n+str(v[i]))%p[i]for i in G(100)]))

説明

Liamの答えを移植しようとしていたとき、私は個人的に与えられた説明のいくつかが混乱を招く言葉で書かれていることに気付いたので、これは彼のアルゴリズムを説明する私の試みです。

まず、の残基を取得nしてm

res1 = get_residues(n)
res2 = get_residues(m)

これには、入力文字列のすべての数字を回転させ、各素数を法とする数値に変換することが含まれます。たとえば、28の場合、 [(20 + 8) mod 2, (20 + 8) mod 3, (20 + 8) mod 5, etc]

def get_residues(string):
    result = []
    for p in primes:
        result.append(reduce(lambda z, c:(z*10+int(c)) % p, string, 0))

次に、以下を使用して、ペアで剰余を加算、乗算、または減算します。 eval()

result = []
for i in range(len(primes)):
    result.append((eval(str(res1[i]) + op + str(res2[i])) % primes[i])

次に、残基のサイズ、つまり必要な素数の最小数を取得します。

size1 = get_size(res1)
size2 = get_size(res2)
size3 = get_size(result)

サイズを取得することは、最もトリッキーで最もコード集約的な部分です。partial_chinese関数を使用して、シーケンスを取得してu_iサイズを決定します。すぐにu_i続きます。

def get_size(residues):
    array = partial_chinese(residues)
    size = len(residues)-1
    while array[size] == 0 and size:
        size -= 1
    return size+1  # to prevent off-by-one errors from 0-indexing

配列は、u_i各残基を取ることによって計算されr_i、合計を減算u_j * primorial(j) for j in [1, i)し、次にdividingによってprimorial(i)すべてのモジュロ、primes[i]。つまり、u_i = (r_i - SUM) / primorial(i)。原始関数と除算関数については、後ほど詳しく説明します。

def partial_chinese(residues):
    array = []
    for i in range(len(primes)):
        array.append(get_partial_i(i, residues, array))
    return array

def get_partial_i(i, residues, previous_array):
    result = residues[i]
    for j in range(len(previous_array)):
        result = (result - previous_array[j] * phi_i(j, i)) % primes[i]
    result = result * inverse(phi_i(i, i), primes[i]) % primes[i]
    return result

phi_i(j, i)計算しprimorial(j) mod primes[i]ます。部門はどのプライムモジュロp我々はすべての可能であることを確認することができますように容易、手動で逆数をチェックすることによって実現されu_iている0 <= u_i < pPに互いに素であることが保証されており、その逆数を保証されています。

def phi_i(j, i):
    return reduce(lambda z, q: z * q % primes[i], primes[:j], 1)

def inverse(n, p):
    for i in range(1, p):
        if n * i % p == 1:
            return i

すべて完了したら、文字列を出力して完了です。

print(res1[:size1], op, res2[:size2], "=", result[:size3])

次は何ですか

これは実装するのが楽しかったです。私はまだ別の答えで何らかの方法で対数を使用できるかどうかを見たいです。そして、このコードを実装するか、APLやJellyのような機能的なゴルフ言語のようなものを実装したいと思います。すべてのゴルフの提案と修正は大歓迎です!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.