잔류 물 번호 시스템


26

수많은 도전의 맥락에서 나는 이것이 흥미로울 것이라고 생각했다.

이 과제에서, 우리는 RSN ( Residue Number System )을 사용하여 큰 정수에 더하기, 빼기 및 곱하기를 수행 할 것입니다.

RNS는 무엇입니까

RNS는 사람들이 정수를 식별하기 위해 개발 한 많은 방법 중 하나입니다. 이 시스템에서 숫자는 일련의 잔류 물로 표현됩니다 (이는 모듈러스 연산 후의 결과 (즉, 정수 나누기 후의 나머지)). 이 시스템에서 각 정수에는 많은 표현이 있습니다. 사물을 단순하게 유지하기 위해 각 정수가 고유하게 표시되도록 사물을 제한합니다. 구체적인 예에서 무슨 일이 일어나고 있는지 설명하는 것이 더 쉽다고 생각합니다.

처음 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 이하의 모든 소수의 곱보다 작습니다 .

또 다른 예로, 3412가 있다고 가정합니다. 여기에서는 2,3,5,7,11,13을 사용해야 합니다. 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 또는 함수 인수를 통해 두 개의 (잠재적으로 큰) 숫자가 제공됩니다. 기본 10 자리 문자열로 제공됩니다.

문제를 더 설명하기 위해 첫 번째 입력 n과 두 번째 입력을 호출합니다 m. n> m> = 0이라고 가정하십시오 .

또한 주어집니다 +하거나 -또는 *수행 할 작업을 나타냅니다.

산출

x를 정수로 하자 . 우리는 [사용하는 X 의 전술 한 RNS의 표현을 참조] X .

당신은 출력해야합니다 [n] <operator> [m] = [result]

RNS에서 작업을 수행하는 방법

이러한 작업은 비교적 간단합니다. RNS 표기법에 두 개의 숫자가 주어지면 더하거나 빼거나 곱하기 위해 주어진 연산을 구성 요소별로 수행 한 다음 모듈러스를 취하십시오.

{1, 2, 3} + {1, 1, 4} = {(1 + 1) mod 2, (2 + 1) mod 3, (3 + 4) mod 5} = {0, 0, 2}

두 개의 서로 다른 숫자를 나타내는 데 사용 된 잔기 수가 동일하지 않은 경우, 작업을 수행 할 때 "더 짧은"숫자를 확장하여 동일한 수의 잔기를 갖도록해야합니다. 이것은 동일한 과정을 따릅니다. 예제는 테스트 사례를 참조하십시오.

결과에 입력보다 더 많은 잔류 물이 필요한 경우에도 마찬가지입니다. 그런 다음 두 입력을 모두 "확장"해야합니다.

중요한 세부 사항

  • 여기서는 큰 숫자를 다루지 만 임의로 큰 것은 아닙니다. 우리는 처음 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 소수를 사용합니다.

나는 ChineseRem기본적으로 RNS 숫자를 취하고 기본 10 정수로 변경하는 GAP에 대한 기본 중국어 정리 정리 구현을 사용하여이를 확인 했습니다. 나는 그들이 옳다고 믿는다. 비린내가 있으면 알려주세요.


관심있는 사람들을 위해, 처음 100 프라임의 결과는 다음과 같습니다.

471193079990618495316248783476026042202057477340967552018863483961641533584503
422120528925670554468197243910409777715799180438028421831503871944494399049257
9030720635990538452312528339864352999310398481791730017201031090

이 수는 주어진 시스템을 사용하여 나타낼 수있는 최대 수보다 1이 큽니다 (100 개의 소수 제한).

다소 관련


나는 작업을 수행하는 것이 가장 어려운 부분이 아니라고 생각합니다.이 도전에 대해 이상하게 생각합니다.
njpipeorgan

@njpipeorgan 동의 (a,b,o)=>a.map((v,i)=>eval(v+o+b[i]))합니다. 예를 들어 작업 수행은 ES6에 있습니다. RNS 로의 후속 변환이 정확히 사소한 것은 아니지만 가장 어려운 부분은 임의의 정밀 산술을 사용하지 않고 결과를 나타내는 데 필요한 소수를 찾는 것입니다.
Neil

이런 입력을 할 수 있습니까 ( 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보다 큰 숫자를 가질 수 없으며 이는 100 번째 소수의 1보다 작습니다.

모든 잔류 물이 준비되면 주어진 작업과 mod각 항목을 다시 수행 할 수 있습니다 . 이제 결과에 대한 고유 지정자가 있지만 결과 및 각 입력을 나타내는 데 사용해야하는 최소 항목 수를 결정해야합니다.

실제로 얼마나 많은 잔류 물이 필요한지 알아내는 것이이 문제의 가장 어려운 부분입니다. 이를 결정하기 위해 CRT (Chinese Remainder Theorem)의 대부분의 단계를 수행합니다. 그러나 우리는 너무 큰 숫자로 끝나지 않도록 수정해야합니다.

하자 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. 이것은 매우 중요합니다. 이는 후행 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우리가 너무 커서 실제로 계산 아무거나 결코, 모든 단계에서이야.

이 수식의 출처가 궁금한 경우 Wikipedia 페이지 에서 찾을 수있는 몇 가지 CRT 예제를 작성하는 것이 좋습니다 .

마지막으로 출력뿐만 아니라 각 입력에 대해 u_i시퀀스를 계산 한 후 후행 0을 결정합니다. 그런 다음 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

@Linus 미안, 나는 이것에 이미 응답했다고 생각했다. 나는 당신에 동의하지만, 필요한 변경 (내가 만들 것)은 비교적 사소한 것이라고 생각합니다. 내가 알기로, 내가해야 할 일은 작업을 수행하기 전에 입력의 잔류 길이를 계산하는 것입니다. 세 숫자 모두에 100 개의 소수를 모두 사용하면 모든 숫자가 한계보다
Liam

@Linus와 첫 번째 질문에 대한 답으로 일반적으로 모든 숫자는 같은 수의 잔류 물을 사용합니다. 그러면 질문이 훨씬 간단 해집니다
Liam

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추가 / 곱하기 / 빼기 같은 길이입니다 둘 다 서로 / 정수 한 RNS.

  • 함수 는 RNS 표현 길이의 하한을 기준으로 mag[f_]부동 소수점 수의 대략적인 크기를 추정합니다 f.

  • 기능 ext[m_,n_,i_]의 제품 부문에서 나머지 알게 m하고 Prime[Range@n]로를 Prime[i].

  • 함수 multi[e_,p_,t_]는 다음을 m만족시키는 가장 작은 승수를 제공합니다.Divisible[m*e+t,p]

  • 함수 는 십진 정수 appx[d_]의 첫 번째 6숫자를 가져와 근사 부동 소수점 값을 제공합니다.


위의 함수를 사용하여 까다로운 문제를 해결 하여 결과의 ​​길이를 결정할 수 있습니다 .

먼저 정수의 RNS 길이를 결정하는 것은 쉬운 일이 아니라는 것을 분명히해야합니다. 작은 정수의 경우 소수의 곱과 직접 비교할 수 있습니다. 그러나 매우 큰 정수의 경우 소수의 곱을 무한정 정확하게 계산하는 것이 금지되므로 이러한 비교는 더 이상 작동하지 않습니다.

예를 들어, 소수의 제품 주어진 1에가 30있다 3.16*10^46, 주위 정수의 RNS 길이는 3.16*10^46가능 할 수 있습니다 29또는 30. 함수 mag줄 것이다 29모두 나타내는 것으로,이 정수에 대한 참조로 29하고 30도 가능하다.

크기를 알면 실제 길이를 계산하기를 희망하면서 해당 크기에 따라 정수를 직접 나타냅니다. 여기서 트릭은 표현이 모두 0이 될 때까지 원래 숫자에 새로운 숫자를 추가하고 RNS 표현을 수정하는 것입니다.

예를 들어 mag[211.]is 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

숫자를 더하면 ( )으로 211나눌 수있는 가장 작은 숫자로 증가 합니다 . 그리고 이제 우리는 원래의 숫자가 "대략"두 번 같기 때문에 보다 크다고 결론을 내립니다 . 에서 시작 하면 최종 숫자가 "대략"이라고 상상하기 어렵지 않습니다 .2102*3*5*7210420210209210

함수 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
유감스럽게도 Google 도움말 센터에 명시된 규칙에 따라 모든 제출물은 사용중인 우승 기준에 대한 심각한 경쟁자가되어야합니다. 코드 골프 경연 대회의 경우 모든 제출물을 골프로 처리해야합니다.
Dennis

@Dennis이 규칙에 대해 알고 있습니다. 그러나 골프가 없어도이 문제는 어렵고 복잡하므로 골프보다는이 문제를 해결하는 것이 나의 목표입니다.
njpipeorgan

이것은 내 골프 프로그램이 아마 훨씬 빠르지 만 골프 프로그램이 아니지만 Java 프로그램과 비교하여 짧습니다.
희망적으로 도움이되는

1
나는 당신이 이것을 골프 수 있다고 생각합니다
Rohan Jhunjhunwala

2

파이썬 3 , 435 바이트

이 과제는 잠시 동안 버킷리스트에 올라 왔지만 최근에는 다음과 같습니다. a) 실제로 답변을 시도하는 데 시간과주의를 기울입니다. 그리고 b) 실제로 로그와 중국 나머지 정리의 부정한 조합을 사용하여 숫자의 크기 (및 소수를 소수의 크기와 비교하여 소수의 수를 계산)에 대한 내 아이디어를 실제로 테스트했습니다. 불행히도, 예를 들어 large_primorial + 3필요한 소수를 결정하려고 할 때 로그 작업 을 수행하면 부동 소수점 문제를 해결할 수있는 방법을 찾아야했습니다.

그래서 이것은 Liam의 답변 포트입니다 .

온라인으로 사용해보십시오!

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 < p페이지에 서로 소 보장되고 그래서 역수를 보장됩니다.

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.