Trong bối cảnh của những thách thức số lượng lớn, tôi nghĩ rằng điều này có thể thú vị.
Trong thử thách này, chúng tôi sẽ sử dụng Hệ thống số dư (RNS) để thực hiện phép cộng, phép trừ và phép nhân trên các số nguyên lớn.
RNS là gì
RNS là một trong nhiều cách mà mọi người đã phát triển để xác định số nguyên. Trong hệ thống này, các số được biểu thị bằng một chuỗi các dư lượng (là kết quả sau một hoạt động mô đun (tức là phần còn lại sau khi chia số nguyên)). Trong hệ thống này, mỗi số nguyên có nhiều biểu diễn. Để giữ cho mọi thứ đơn giản, chúng tôi sẽ giới hạn mọi thứ sao cho mỗi số nguyên được biểu diễn duy nhất. Tôi nghĩ rằng dễ dàng hơn để mô tả những gì đang xảy ra với một ví dụ cụ thể.
Chúng ta hãy xem xét ba số nguyên tố đầu tiên: 2, 3, 5. Trong hệ thống RNS, chúng ta có thể sử dụng ba số này để biểu thị duy nhất bất kỳ số nào nhỏ hơn 2 * 3 * 5 = 30 bằng cách sử dụng dư lượng. Mất 21:
21 nhỏ hơn 30, vì vậy chúng ta có thể biểu diễn nó bằng các kết quả sau khi sửa đổi 2, 3 và 5. (tức là phần còn lại sau khi chia số nguyên cho 2, 3 và 5)
Chúng tôi sẽ xác định 21 với chuỗi số nguyên sau:
21 ~ {21 mod 2, 21 mod 3, 21 mod 5} = {1, 0, 1}
Và trong hệ thống RNS của chúng tôi, thay vì "21", chúng tôi sẽ sử dụng {1,0,1}.
Nói chung, với một số nguyên n , chúng tôi biểu thị n là { n mod 2, ..., n mod p_k } trong đó p_k là số nguyên tố nhỏ nhất sao cho n nhỏ hơn tích của tất cả các số nguyên tố nhỏ hơn hoặc bằng p_k .
Một ví dụ khác, giả sử chúng ta có 3412. Chúng ta cần sử dụng 2,3,5,7,11,13 ở đây vì 2*3*5*7*11*13=30030
trong khi đó, 2*3*5*7*11=2310
quá nhỏ.
3412 ~ {3412 mod 2, 3412 mod 3, 3412, mod 5, ..., 3412 mod 13} = {0, 1, 2, 3, 2, 6}
Bạn nhận thấy rằng sử dụng hệ thống này, chúng tôi có thể đại diện cho số lượng rất lớn tương đối không đau. Sử dụng dư lượng {1, 2, 3, 4, 5, 6, 7, 8, ...}, chúng tôi có thể biểu thị các số lên tới {2, 6, 30, 210, 2310, 30030, 510510, 9699690 ...} tương ứng. ( Đây là loạt bài )
Nhiệm vụ của chúng ta
Chúng tôi sẽ sử dụng các dư lượng này để thực hiện +, - và * trên số lượng lớn. Tôi sẽ mô tả các quá trình dưới đây. Bây giờ ở đây là các thông số kỹ thuật đầu vào và đầu ra.
Đầu vào
Bạn sẽ được cung cấp hai số (có khả năng rất lớn) thông qua đối số stdin hoặc hàm. Chúng sẽ được đưa ra dưới dạng chuỗi gồm 10 chữ số cơ bản.
Đối với mục đích phác thảo vấn đề hơn nữa, chúng tôi gọi đầu vào đầu tiên n
và thứ hai m
. Giả sử n> m> = 0 .
Bạn cũng sẽ được cung cấp +
hoặc -
hoặc *
chỉ ra thao tác để thực hiện.
Đầu ra
Đặt x là một số nguyên. Chúng tôi sẽ sử dụng [ x ] để chỉ đại diện RNS được mô tả ở trên của x .
Bạn đang xuất [n] <operator> [m] = [result]
Cách thực hiện các thao tác trong RNS
Các thao tác này tương đối đơn giản. Cho hai số trong ký hiệu RNS, để cộng, trừ hoặc nhân chúng, chỉ cần thực hiện các thao tác thành phần đã cho là khôn ngoan và sau đó lấy mô đun.
I E
{1, 2, 3} + {1, 1, 4} = {(1 + 1) mod 2, (2 + 1) mod 3, (3 + 4) mod 5} = {0, 0, 2}
Lưu ý rằng nếu số lượng dư lượng được sử dụng để đại diện cho hai số khác nhau không giống nhau, khi thực hiện các thao tác, bạn sẽ cần phải mở rộng số "ngắn hơn" để có cùng số lượng dư lượng. Điều này tuân theo quy trình tương tự. Xem các trường hợp thử nghiệm cho một ví dụ.
Điều tương tự cũng xảy ra nếu kết quả yêu cầu nhiều dư lượng hơn đầu vào. Sau đó, cả hai đầu vào cần phải được "mở rộng".
Chi tiết quan trọng
Chúng tôi sẽ xử lý số lượng lớn ở đây, nhưng không lớn tùy tiện. Chúng tôi sẽ chịu trách nhiệm về số lượng lên tới sản phẩm của 100 số nguyên tố đầu tiên (xem bên dưới). Để kết thúc này, bạn được cung cấp 100 số nguyên tố đầu tiên miễn phí (không có chi phí byte) . Bạn có thể dán chúng trong một mảng được gọi
p
hoặc một cái gì đó thành ngữ với ngôn ngữ của bạn và sau đó trừ đi số byte được sử dụng để bắt đầu mảng này khỏi tổng số cuối cùng của bạn. Điều này tất nhiên có nghĩa là chúng có thể được mã hóa cứng hoặc bạn có thể sử dụng một tích hợp để tạo ra chúng.Nếu vì một lý do nào đó, đây là biểu diễn số nguyên mặc định được sử dụng trong ngôn ngữ của bạn. Điều đó là tốt.
Bạn không được sử dụng bất kỳ loại Số nguyên chính xác tùy ý trừ khi đó là mặc định của ngôn ngữ của bạn. Nếu nó là mặc định, bạn không thể sử dụng nó để lưu trữ các số nguyên thường không phù hợp với 64 bit.
Để rõ ràng, mỗi số nguyên sẽ luôn được đại diện với ít dư lượng nhất có thể. Điều này đi cho cả đầu vào và đầu ra.
Tôi nghĩ rằng các thông số kỹ thuật khác nên ngăn chặn điều này, nhưng để dự phòng: bạn có thể không thực hiện thao tác đã cho trên các đầu vào và sau đó thay đổi mọi thứ thành RNS và sau đó xuất ra. Bạn phải thay đổi đầu vào thành RNS và sau đó thực hiện các thao tác để tạo đầu ra.
Các trường hợp thử nghiệm
Đầu vào:
n = 10
m = 4
+
Đầu ra:
{ 0, 1, 0 } + { 0, 1 } = { 0, 2, 4 }
Giải trình:
Đầu tiên, thay đổi từng số thành biểu diễn RNS của nó như được mô tả ở trên:
10 ~ {0,1,0}
và 4 ~ {0,1}
. Lưu ý rằng khi chúng ta muốn thực hiện bổ sung thành phần, 10
có nhiều thành phần hơn 4
. Do đó, chúng ta phải "mở rộng" số ngắn hơn. Vì vậy, chúng tôi sẽ viết ngắn gọn 4 ~ {0,1} --> {0,1, 4 mod 5} = {0,1,4}
. Bây giờ chúng tôi tiến hành bổ sung và sau đó lấy mô-đun.
- Đầu vào
n=28
m=18
+
Đầu ra:
[ 0, 1, 3 ] + [0, 0, 3 ] = [ 0, 1, 1, 4 ]
- Đầu vào (tôi đập mặt trên bàn phím)
n=1231725471982371298419823012819231982571923
m=1288488183
*
Đầu ra (được chia thành các dòng riêng biệt để dễ đọc):
[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
yêu cầu 28 số nguyên tố. m
yêu cầu 10. n*m
yêu cầu 33.
- Đầu vào
n=8709668761379269784034173446876636639594408083936553641753483991897255703964943107588335040121154680170867105541177741204814011615930342030904704147856733048115934632145172739949220591246493529224396454328521288726490
m=1699412683745170450115957274739962577420086093042490863793456500767137147999161679589295549397604032154933975242548831536518655879433595016
-
Đầu ra:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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
sử dụng 100 số nguyên tố. m
sử dụng 70 số nguyên tố. n-m
sử dụng 99 số nguyên tố.
Tôi đã kiểm tra những điều này bằng cách sử dụng ChineseRem
triển khai tích hợp của định lý Phần còn lại của Trung Quốc trên GAP (về cơ bản lấy số RNS và thay đổi chúng thành số nguyên cơ bản 10). Tôi tin rằng họ là chính xác. Nếu một cái gì đó có vẻ tanh, xin vui lòng cho tôi biết.
Đối với những người quan tâm, sản phẩm của 100 số nguyên tố đầu tiên là:
471193079990618495316248783476026042202057477340967552018863483961641533584503
422120528925670554468197243910409777715799180438028421831503871944494399049257
9030720635990538452312528339864352999310398481791730017201031090
Con số này lớn hơn 1 so với số tối đa chúng ta có thể biểu thị bằng cách sử dụng hệ thống nhất định (và giới hạn 100 số nguyên tố).
(a,b,o)=>a.map((v,i)=>eval(v+o+b[i]))
trong ES6 chẳng hạn. Tôi nghĩ phần khó nhất có lẽ là tìm số lượng số nguyên tố cần thiết để biểu diễn kết quả mà không sử dụng số học chính xác tùy ý, mặc dù việc chuyển đổi tiếp theo thành RNS không chính xác.
1234,1234,+
) không?