На думку великих викликів, я вважав, що це може бути цікавим.
У цьому виклику ми будемо використовувати систему номерів залишків (RNS) для виконання додавання, віднімання та множення на великі цілі числа.
Що таке RNS
RNS - це один із багатьох способів, які люди розробили для ідентифікації цілих чисел. У цій системі числа представлені послідовністю залишків (які є результатами після операції з модулем (тобто решта після цілого поділу)). У цій системі кожне ціле число має багато уявлень. Щоб все було просто, ми обмежимось так, щоб кожне ціле число було унікально представлене. Я думаю, що простіше описати те, що відбувається конкретним прикладом.
Давайте розглянемо перші три простих числа: 2, 3, 5. У системі RNS ми можемо використовувати ці три числа, щоб однозначно представити будь-яке число, яке менше 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 - найменший простий, такий, що n менше, ніж добуток усіх простих чисел p = p .
Інший приклад, скажімо, у нас 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, а потім виконати операції з отримання виводу.
Випробування
Вхід:
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
вбудовану реалізацію китайської теореми Remainder на 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,+
)?