Sulla scia di grandi sfide, ho pensato che questo potesse essere interessante.
In questa sfida, utilizzeremo il Residue Number System (RNS) per eseguire addizioni, sottrazioni e moltiplicazioni su numeri interi di grandi dimensioni.
Cos'è l'RNS
L'RNS è uno dei molti modi in cui le persone hanno sviluppato per identificare numeri interi. In questo sistema, i numeri sono rappresentati da una sequenza di residui (che sono i risultati dopo un'operazione di modulo (cioè il resto dopo la divisione di numeri interi)). In questo sistema, ogni numero intero ha molte rappresentazioni. Per semplificare le cose, limiteremo le cose in modo che ogni numero intero sia rappresentato in modo univoco. Penso che sia più facile descrivere ciò che sta accadendo con un esempio concreto.
Esaminiamo i primi tre numeri primi: 2, 3, 5. Nel sistema RNS, possiamo usare questi tre numeri per rappresentare in modo univoco qualsiasi numero inferiore a 2 * 3 * 5 = 30 usando i residui. Prendi 21:
21 è inferiore a 30, quindi possiamo rappresentarlo utilizzando i risultati dopo la modifica per 2, 3 e 5. (ovvero il resto dopo la divisione di numeri interi per 2, 3 e 5)
Vorremmo identificare 21 con la seguente sequenza di numeri interi:
21 ~ {21 mod 2, 21 mod 3, 21 mod 5} = {1, 0, 1}
E così nel nostro sistema RNS, invece di "21", useremmo {1,0,1}.
In generale, dato un numero intero n , rappresentiamo n come { n mod 2, ..., n mod p_k } dove p_k è il primo più piccolo in modo che n sia inferiore al prodotto di tutti i numeri primi minore o uguale a p_k .
Un altro esempio, diciamo che abbiamo 3412. Qui dobbiamo usare 2,3,5,7,11,13 perché 2*3*5*7*11*13=30030
considerando 2*3*5*7*11=2310
che è troppo piccolo.
3412 ~ {3412 mod 2, 3412 mod 3, 3412, mod 5, ..., 3412 mod 13} = {0, 1, 2, 3, 2, 6}
Notate che usando questo sistema possiamo rappresentare numeri molto grandi relativamente indolore. Usando i residui di {1, 2, 3, 4, 5, 6, 7, 8, ...}, possiamo rappresentare numeri fino a {2, 6, 30, 210, 2310, 30030, 510510, 9699690 ...} rispettivamente. ( Ecco la serie )
Il nostro compito
Utilizzeremo questi residui per eseguire +, - e * su grandi numeri. Descriverò questi processi di seguito. Per ora ecco le specifiche di input e output.
Ingresso
Ti verranno dati due numeri (potenzialmente molto grandi) tramite uno stdin o un argomento di funzione. Verranno dati come stringhe di 10 cifre di base.
Ai fini di delineare ulteriormente il problema, chiamiamo il primo input n
e il secondo m
. Supponiamo n> m> = 0 .
Ti verrà anche dato +
o -
o *
per indicare l'operazione da eseguire.
Produzione
Lascia che x sia un numero intero. Useremo [ x ] per fare riferimento alla rappresentazione RNS descritta sopra di x .
Devi produrre [n] <operator> [m] = [result]
Come eseguire le operazioni in RNS
Queste operazioni sono relativamente semplici. Dati due numeri nella notazione RNS, per aggiungerli, sottrarli o moltiplicarli, eseguire semplicemente le operazioni indicate dal punto di vista componente e quindi prendere il modulo.
vale a dire
{1, 2, 3} + {1, 1, 4} = {(1 + 1) mod 2, (2 + 1) mod 3, (3 + 4) mod 5} = {0, 0, 2}
Si noti che se il numero di residui utilizzati per rappresentare due numeri diversi non è lo stesso, quando si eseguono operazioni, sarà necessario estendere il numero "più corto" in modo che abbia lo stesso numero di residui. Questo segue lo stesso processo. Vedi i casi di test per un esempio.
Lo stesso vale se il risultato richiede più residui di entrambi gli input. Quindi entrambi gli ingressi devono essere "estesi".
Dettagli importanti
Ci occuperemo di grandi numeri qui, ma non arbitrariamente di grandi dimensioni. Saremo responsabili per i numeri fino al prodotto dei primi 100 numeri primi (vedi sotto). A tal fine, ti vengono dati i primi 100 numeri primi gratuitamente (nessun costo in byte) . Puoi incollarli in un array chiamato
p
o qualcosa di idiomatico nella tua lingua e quindi sottrarre il numero di byte utilizzati per avviare questo array dal totale finale. Questo ovviamente significa che possono essere hardcoded oppure è possibile utilizzare un built-in per generarli.Se per qualcuno motivo questa è la rappresentazione intera predefinita utilizzata nella tua lingua. Questo va bene.
Non è possibile utilizzare alcun tipo di Arbitrary Precision Integer a meno che non sia l'impostazione predefinita della propria lingua. Se è il valore predefinito, non è possibile utilizzarlo per memorizzare numeri interi che in genere non si adattano a 64 bit.
Per essere chiari, ogni numero intero sarà sempre rappresentato con il minor numero possibile di residui. Questo vale sia per l'ingresso che per l'uscita.
Penso che le altre specifiche dovrebbero impedire questo, ma per essere ridondanti: potresti non eseguire l'operazione indicata sugli input e quindi cambiare tutto in RNS e quindi output. È necessario modificare gli input in RNS e quindi eseguire le operazioni per produrre l'output.
Casi test
Ingresso:
n = 10
m = 4
+
Produzione:
{ 0, 1, 0 } + { 0, 1 } = { 0, 2, 4 }
Spiegazione:
Innanzitutto, cambia ogni numero nella sua rappresentazione RNS come descritto sopra:
10 ~ {0,1,0}
e 4 ~ {0,1}
. Si noti che quando vogliamo fare un'aggiunta basata sui componenti, questo 10
ha più componenti di 4
. Pertanto dobbiamo "estendere" il numero più breve. Quindi scriveremo brevemente 4 ~ {0,1} --> {0,1, 4 mod 5} = {0,1,4}
. Ora procediamo con l'aggiunta e poi prendiamo il modulo.
- Ingresso
n=28
m=18
+
Produzione:
[ 0, 1, 3 ] + [0, 0, 3 ] = [ 0, 1, 1, 4 ]
- Input (mi schiaccio il viso sulla tastiera)
n=1231725471982371298419823012819231982571923
m=1288488183
*
Output (suddiviso in righe separate per leggibilità):
[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
richiede 28 numeri primi. m
richiede 10. n*m
richiede 33.
- Ingresso
n=8709668761379269784034173446876636639594408083936553641753483991897255703964943107588335040121154680170867105541177741204814011615930342030904704147856733048115934632145172739949220591246493529224396454328521288726490
m=1699412683745170450115957274739962577420086093042490863793456500767137147999161679589295549397604032154933975242548831536518655879433595016
-
Produzione:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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
usa 100 numeri primi. m
utilizza 70 numeri primi. n-m
usa 99 numeri primi.
ChineseRem
Li ho controllati usando l' implementazione integrata del teorema del residuo cinese su GAP (che sostanzialmente prende i numeri RNS e li cambia in base a 10 numeri interi). Credo che siano corretti. Se qualcosa sembra sospetto, per favore fatemi sapere.
Per chi se ne frega, il prodotto dei primi 100 numeri primi è:
471193079990618495316248783476026042202057477340967552018863483961641533584503
422120528925670554468197243910409777715799180438028421831503871944494399049257
9030720635990538452312528339864352999310398481791730017201031090
Questo numero è 1 più grande del numero massimo che possiamo rappresentare usando il sistema dato (e una limitazione di 100 primi).
(a,b,o)=>a.map((v,i)=>eval(v+o+b[i]))
in ES6 per esempio. Penso che la parte più difficile sia probabilmente trovare il numero di numeri primi necessari per rappresentare il risultato senza usare l'aritmetica di precisione arbitraria, sebbene la successiva conversione in RNS non sia esattamente banale.
1234,1234,+
)?