ระบบหมายเลขสารตกค้าง


26

ในหลอดเลือดดำของความท้าทายจำนวนมากฉันคิดว่าสิ่งนี้อาจน่าสนใจ

ในความท้าทายนี้เราจะใช้ระบบหมายเลขสารตกค้าง (RNS) เพื่อดำเนินการบวกลบและคูณกับจำนวนเต็มขนาดใหญ่

RNS คืออะไร

RNS เป็นหนึ่งในหลาย ๆ วิธีที่ผู้คนพัฒนาขึ้นเพื่อระบุจำนวนเต็ม ในระบบนี้ตัวเลขจะถูกแทนด้วยลำดับของสิ่งตกค้าง (ซึ่งเป็นผลลัพธ์หลังจากการดำเนินการโมดูลัส (เช่นส่วนที่เหลือหลังจากการหารจำนวนเต็ม)) ในระบบนี้จำนวนเต็มแต่ละตัวมีการแทนจำนวนมาก เพื่อให้ง่ายขึ้นเราจะ จำกัด สิ่งต่าง ๆ เพื่อให้จำนวนเต็มแต่ละค่ามีค่าไม่ซ้ำกัน ฉันคิดว่าการอธิบายสิ่งที่เกิดขึ้นกับตัวอย่างที่เป็นรูปธรรมนั้นง่ายกว่า

ให้เราดูที่แรกสามตัวเลขที่สำคัญ: 2, 3, 5 ในระบบอาร์เอ็นเอเราสามารถใช้ทั้งสามตัวเลขที่จะไม่ซ้ำกันแทนจำนวนที่น้อยกว่า 2 * 3 * 5 = 30 ใช้สารตกค้าง รับ 21:

21 มีค่าน้อยกว่า 30 ดังนั้นเราจึงสามารถใช้แทนผลลัพธ์หลังจาก modding ด้วย 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

อีกตัวอย่างหนึ่งบอกว่าเรามี 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 สมมติ n> ม> = 0m

นอกจากนี้คุณยังจะได้รับ+หรือ-หรือ*เพื่อแสดงให้เห็นการดำเนินการที่จะดำเนินการ

เอาท์พุต

ให้xเป็นจำนวนเต็ม เราจะใช้ [ x ] หมายถึงการเป็นตัวแทนอาร์เอ็นเอที่อธิบายข้างต้นของ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หรือสิ่งที่เป็นสำนวนภาษาของคุณแล้วลบจำนวนไบต์ที่ใช้ในการเริ่มต้นอาร์เรย์นี้จากผลรวมสุดท้ายของคุณ หลักสูตรนี้หมายความว่าพวกเขาอาจจะฮาร์ดโค้ดหรือคุณอาจใช้บิวด์อินเพื่อสร้างมัน

  • ถ้าด้วยเหตุผลบางอย่างนี่เป็นการแสดงค่าจำนวนเต็มเริ่มต้นที่ใช้ในภาษาของคุณ นั่นเป็นเรื่องปกติ

  • คุณไม่สามารถใช้งาน Arbitrary Precision Integer ประเภทใดก็ได้เว้นแต่จะเป็นค่าเริ่มต้นของภาษาของคุณ หากเป็นค่าเริ่มต้นคุณไม่สามารถใช้เพื่อเก็บจำนวนเต็มที่โดยทั่วไปจะไม่พอดีกับ 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] 

nต้อง 28 ช่วงเวลา mต้องการ 10. 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]

nใช้ 100 ครั้ง mใช้ 70 ครั้ง n-mใช้ 99 ครั้ง

ฉันตรวจสอบสิ่งเหล่านี้โดยใช้การChineseRemดำเนินการตามทฤษฎีบท Remainder ภาษาจีนในตัวบน GAP (ซึ่งโดยทั่วไปใช้หมายเลข RNS และเปลี่ยนเป็นฐาน 10 จำนวนเต็ม) ฉันเชื่อว่าพวกเขาถูกต้อง หากสิ่งที่ดูเหมือนคาวโปรดแจ้งให้เราทราบ


สำหรับผู้ที่สนใจผลิตภัณฑ์ของ 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 ใช่ฟังก์ชั่นเป็นที่ยอมรับเช่นกัน
เลียม

"เพียงดำเนินการตามองค์ประกอบการดำเนินการที่ได้รับ" - แล้วส่วนประกอบพิเศษในเอาต์พุตมาจากที่ใด
smls

คำตอบ:


6

GAP

ความเป็นมาบางอย่าง:ฉันจะยอมรับว่าเมื่อฉันสร้างคำถามนี้เมื่อหลายเดือนก่อนฉันไม่มีวิธีแก้ไขส่วนที่ยากของคำถามนี้: การกำหนดจำนวนเฉพาะให้ใช้ เรามีคนที่ฉลาดมาก ๆ ในไซต์นี้และฉันคาดหวังจริงๆว่าจะมีคนคิดวิธีที่จะทำมันได้อย่างรวดเร็ว อย่างไรก็ตามเนื่องจากสิ่งนี้ไม่ได้เกิดขึ้นฉันไม่แน่ใจด้วยซ้ำว่ามันเป็นไปได้อย่างแท้จริงในการแก้ปัญหานี้ ดังนั้นฉันต้องใช้เวลาในการคิดวิธีการ ฉันเชื่อว่าสิ่งที่ฉันทำไม่ได้ละเมิดกฎใด ๆ ในการท้าทายนี้แน่นอนว่าฉันจะรักสิ่งนี้เพื่อตรวจสอบข้อเท็จจริง

ฉันยังเสียใจที่เลือกเล็กน้อยเนื่องจากโซลูชันมีความลึกมากกว่าเล็กน้อยกว่าปกติจะพอดีกับรูปแบบแท็ก ต้องบอกว่าเพื่อปฏิบัติตามกฎของไซต์มีโซลูชันของฉัน "golfed" ที่ด้านล่างของโพสต์นี้


รหัส

### 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ซึ่งเป็น 1 น้อยกว่าไพร์มที่ 1 ที่ 100

หลังจากเรามีสิ่งตกค้างทั้งหมดแล้วเราสามารถดำเนินการตามที่กำหนดและ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แล้วนี่เป็นสิ่งสำคัญอย่างยิ่ง นั่นหมายความว่าโดยการตรวจสอบค่าศูนย์ต่อท้ายเราสามารถกำหนดจำนวนของสารตกค้างที่r_iเราต้องการนำเสนอจริงXใน RNS

หากคุณสนใจที่จะตรวจสอบลำดับของบางอย่างu_iที่partial_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)ฟังก์ชั่นนี้ผลตอบแทนมันmods ในทุกขั้นตอนเพื่อให้เราไม่เคยอะไรคำนวณจริงที่มีขนาดใหญ่เกินไป

หากคุณอยากรู้ว่าสูตรนี้มาจากไหนฉันอยากจะแนะนำตัวอย่างของ CRT สองสามข้อซึ่งสามารถพบได้ในหน้าวิกิพีเดียหน้าวิกิพีเดีย

ในที่สุดสำหรับแต่ละอินพุตเช่นเดียวกับการส่งออกของเราเราจะคำนวณu_iลำดับและจากนั้นกำหนดค่าศูนย์ต่อท้าย จากนั้นเราก็โยนมันออกไปมากมายr_iจากจุดสิ้นสุดของลำดับที่ตกค้าง


รหัส "Golfed" 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 ฉันคิดว่าฉันตอบกลับมาแล้ว ฉันเห็นด้วยกับคุณ แต่ฉันคิดว่าการเปลี่ยนแปลงที่จำเป็น (ซึ่งฉันจะทำ) นั้นค่อนข้างเล็กน้อย เท่าที่ฉันเห็นมันทั้งหมดที่ฉันต้องทำคือการคำนวณความยาวที่เหลือของอินพุตก่อนที่จะดำเนินการ การใช้ทั้ง 100 ช่วงเวลาสำหรับทั้งสามหมายเลขเพียงยกระดับความจริงที่ว่าตัวเลขทั้งหมดมีขอบเขตด้านบน
เลียม

@Lusus และเพื่อตอบคำถามแรกของคุณโดยปกติแล้วตัวเลขทั้งหมดจะใช้จำนวนที่เหลืออยู่เท่ากัน นั่นจะทำให้คำถามง่ายขึ้นมาก
เลียม

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 จำนวนเต็มเป็นจำนวนเต็มอาร์เอ็นเอของความยาวdl

  • ฟังก์ชั่นplus/ times/ subtractเพิ่ม / ทวีคูณ / ลบหนึ่งจำนวนเต็ม RNS หนึ่งไปยัง / จากทั้งสองซึ่งมีความยาวเดียวกัน

  • ฟังก์ชั่นmag[f_]ประมาณขนาดประมาณของจำนวนจุดลอยตัวfในแง่ของขอบเขตล่างของความยาวของการเป็นตัวแทน RNS ของมัน

  • ฟังก์ชั่น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ความยาวอาร์เอ็นเอของจำนวนเต็มรอบ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

โดยการเพิ่มจำนวนเราจะเพิ่ม211จำนวนที่น้อยที่สุดที่หารด้วย210( 2*3*5*7) และตอนนี้เราสรุปได้ว่าจำนวนเดิมที่มีค่ามากกว่า210เนื่องจาก420เท่ากับ "ประมาณ" 210สองเท่าของ มันไม่ได้เป็นเรื่องยากที่จะจินตนาการว่าถ้าเราเริ่มต้นจาก209จำนวนสุดท้ายคือ 210"ประมาณ"

ฟังก์ชั่นlength[f_,n_]ใช้เวลาค่าจุดลอยเพื่อประเมินขนาดและฐานมันถูกต้องในการเป็นตัวแทนของอาร์เอ็นเอfn

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
ฉันรู้สึกว่าคุณมีความสามารถในการเล่นกอล์ฟนี้
Rohan Jhunjhunwala

2

Python 3 , 435 ไบต์

ความท้าทายนี้อยู่ในรายชื่อฝากข้อมูลของฉันอยู่พักหนึ่ง แต่เมื่อไม่นานมานี้: ก) ฉันใช้เวลาและความสนใจในการพยายามหาคำตอบจริง ๆ และ 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)]))

คำอธิบาย

ในขณะที่พยายามตอบคำถามของเลียมผมเองพบว่าคำอธิบายบางคำที่ได้รับนั้นเป็นคำที่สับสนดังนั้นนี่คือความพยายามของฉันในการอธิบายอัลกอริทึมของเขา

ครั้งแรกที่เราได้รับการตกค้างของและnm

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รับประกันได้ว่าจะ coprime ไปที่ 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.