В свете большого количества проблем я подумал, что этот может быть интересным.
В этой задаче мы будем использовать систему счисления остатков (RNS) для сложения, вычитания и умножения больших целых чисел.
Что такое RNS
RNS является одним из многих способов, которые люди разработали для идентификации целых чисел. В этой системе числа представлены последовательностью вычетов (которые являются результатами после операции модуля (то есть остатком после целочисленного деления)). В этой системе каждое целое число имеет много представлений. Для простоты мы собираемся ограничить вещи так, чтобы каждое целое число было уникально представлено. Я думаю, что проще описать, что происходит на конкретном примере.
Давайте посмотрим на первые три простых числа: 2, 3, 5. В системе RNS мы можем использовать эти три числа для уникального представления любого числа, которое меньше, чем 2 * 3 * 5 = 30, используя вычеты. Возьми 21:
21 меньше 30, поэтому мы можем представить его, используя результаты после модификации на 2, 3 и 5. (т. Е. Остаток после целочисленного деления на 2, 3 и 5)
Мы бы идентифицировали 21 со следующей последовательностью целых чисел:
21 ~ {21 мод 2, 21 мод 3, 21 мод 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 мод 2, 3412 мод 3, 3412, мод 5, ..., 3412 мод 13} = {0, 1, 2, 3, 2, 6}
Вы замечаете, что используя эту систему, мы можем представлять очень большие цифры относительно безболезненно. Используя остатки {1, 2, 3, 4, 5, 6, 7, 8, ...}, мы можем представить числа до {2, 6, 30, 210, 2310, 30030, 510510, 9699690 ...} соответственно. ( Вот серия )
Наша задача
Мы будем использовать эти остатки для выполнения +, - и * на больших числах. Я опишу эти процессы ниже. На данный момент вот спецификации ввода и вывода.
вход
Вам будет дано два (потенциально очень больших) числа через аргумент stdin или function. Они будут даны как строки из 10 цифр.
Для дальнейшего описания проблемы мы называем первый вход n
и второй m
. Предположим, что n> m> = 0 .
Вам также будет дано +
или -
или *
для указания операции для выполнения.
Выход
Пусть х целое число. Мы будем использовать [ x ] для ссылки на RNS-представление, описанное выше для x .
Вы должны вывести [n] <operator> [m] = [result]
Как выполнять операции в РНС
Эти операции относительно просты. Учитывая два числа в обозначениях 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, а затем выполнить операции для получения выходных данных.
Тестовые случаи
Входные данные:
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
Это число на 1 больше максимального числа, которое мы можем представить, используя данную систему (и ограничение 100 простых чисел).
(a,b,o)=>a.map((v,i)=>eval(v+o+b[i]))
в ES6, например. Я думаю, что самая сложная часть - это, вероятно, найти число простых чисел, необходимое для представления результата без использования арифметики произвольной точности, хотя последующее преобразование в RNS не совсем тривиально.
1234,1234,+
)?