Teorema del resto cinese


21

Il teorema del resto cinese ci dice che possiamo sempre trovare un numero che produce i resti necessari sotto diversi moduli primi. Il tuo obiettivo è scrivere codice per generare un numero simile in tempo polinomiale. Il codice più corto vince.

Ad esempio, supponiamo che ci vengano dati questi vincoli ( %rappresenta mod):

n % 7  == 2
n % 5  == 4
n % 11 == 0

Una soluzione è n=44. Il primo vincolo è soddisfatto perché 44 = 6*7 + 2, e così 44ha resto 2quando diviso per 7, e quindi 44 % 7 == 2. Anche gli altri due vincoli sono soddisfatti. Esistono altre soluzioni, come n=814e n=-341.

Ingresso

Un elenco non vuoto di coppie (p_i,a_i), in cui ciascun modulo p_iè un primo distinto e ogni target a_iè un numero naturale nell'intervallo 0 <= a_i < p_i. Puoi prendere input in qualunque forma sia conveniente; non deve essere in realtà un elenco di coppie. Non si può presumere che l'input sia ordinato.

Produzione

Un numero intero ntale n % p_i == a_iper ciascun indice i. Non deve essere il valore più piccolo, e può essere negativo.

Limitazione temporale polinomiale

Per evitare che le soluzioni a basso costo che cercano solo n=0, n=1, n=2, e così via, il codice deve essere eseguito in tempo polinomiale nella lunghezza dell'input . Si noti che un numero mnell'input ha lunghezza Θ(log m), quindi di per msé non è polinomiale nella sua lunghezza. Ciò significa che non è possibile contare mo eseguire i mtempi di un'operazione , ma è possibile calcolare le operazioni aritmetiche sui valori.

Non puoi usare un formato di input inefficiente come unario per aggirare questo.

Altri divieti

Gli incorporamenti per eseguire le seguenti operazioni non sono consentiti: implementare il teorema del resto cinese, risolvere equazioni o numeri di fattori.

Puoi usare i built-in per trovare mod ed eseguire addizioni, sottrazioni, moltiplicazioni ed esponenziali modulari (con esponente di numero naturale). Non è possibile utilizzare altre operazioni modulari integrate, tra cui inversa, divisione e ricerca di ordini modulari.

Casi test

Questi forniscono la più piccola soluzione non negativa. La tua risposta potrebbe essere diversa. Probabilmente è meglio se controlli direttamente che l'output soddisfi ogni vincolo.

[(5, 3)] 
3

[(7, 2), (5, 4), (11, 0)]
44

[(5, 1), (73, 4), (59, 30), (701, 53), (139, 112)]
1770977011

[(982451653, 778102454), (452930477, 133039003)]
68121500720666070

Perché nessuna divisione?
jimmy23013,

@ user23013 Nessuna divisione modulare, poiché è sostanzialmente inversa modulare.
xnor

L'inversione di matrice conta come risoluzione di equazioni?
flawr

@flawr: la penso così.
Alex A.

@xnor: che ne pensi? E le funzioni di ottimizzazione?
flawr

Risposte:


9

Mathematica, 55 51 45

È vietato l'inverso modulare, ma è consentita l'espiazione modulare. Con il piccolo teorema di Fermat, n^(-1) % p == n^(p-2) % p.

(PowerMod[x=1##&@@#/#,#-2,#]x).#2&@@Thread@#&

Esempio:

In[1]:= f = (PowerMod[x=1##&@@#/#,#-2,#]x).#2&@@Thread@#&;

In[2]:= f[{{5, 3}}]

Out[2]= 3

In[3]:= f[{{7, 2}, {5, 4}, {11, 0}}]

Out[3]= 1584

In[4]:= f[{{5, 1}, {73, 4}, {59, 30}, {701, 53}, {139, 112}}]

Out[4]= 142360350966

Solo per divertimento:

ChineseRemainder@@Reverse@Thread@#&

1
Puoi salvare un byte scambiando l'ordine degli argomenti della funzione più interna, in modo tale da poterlo usare PowerMod[#2,#-2,#]e non penso che ci sia un requisito per nominare la funzione, portandola a 48.
Martin Ender

Sì, le funzioni senza nome sono OK.
xnor

6

Python 2, 165 101 99 98 85 byte

Usando il piccolo teorema di Fermat come le altre risposte. Non si preoccupa di mantenere la somma finale all'interno dell'intervallo modulare, poiché non siamo interessati alla soluzione più piccola. Grazie Volatilità per aver salvato 13 byte.

l=input();x=reduce(lambda a,b:a*b[0],l,1)
print sum(x/a*b*pow(x/a,a-2,a)for a,b in l)

[(5, 3)]
3
[(7, 2), (5, 4), (11, 0)]
1584
[(5, 1), (73, 4), (59, 30), (701, 53), (139, 112)]
142360350966

1
È possibile rimuovere lo spazio prima for.
Isaacg,

1
x/a*b*pow(x/a,a-2,a)for a,b in ldovrebbe funzionare.
Volatilità il

Punto eccellente! Stavo cercando di sbarazzarmi dell'ovvia ridondanza lì, ma ho dimenticato che potevo semplicemente decomprimere.
Uri Granta,

4

Pyth, 40 37 36 29

M*G.^G-H2Hsm*edg/u*GhHQ1hdhdQ

Usa il piccolo teorema di Fermat, grazie ad alephalpha. Calcola utilizzando questa formula .


3

Ruby, 129

Bene, compagni, sembra che le soluzioni di Ruby debbano essere più lunghe perché l'esponenziazione modulare non è disponibile senza caricare la libreria openssl e fare conversioni in OpenSSL :: BN. Comunque, mi sono divertito a scriverlo:

require("openssl")
z=eval(gets)
x=1
z.map{|a,b|x*=a}
s=0
z.map{|a,b|e=a.to_bn;s+=(x/a).to_bn.mod_exp(e-2,e).to_i*b*x/a}
puts(s)

Non è necessario le parentesi quando si chiama require, evalo puts.
Tutleman,

2

Python 2, 61

n=P=1
for p,a in input():n+=P*(a-n)*pow(P,p-2,p);P*=p
print n

Questo impiega una variazione della costruzione del prodotto utilizzata da altre risposte.

L'idea è di passare in rassegna i vincoli e aggiornare la soluzione nper soddisfare il vincolo attuale senza incasinare quelli precedenti. Per fare ciò, seguiamo il prodotto Pdei numeri primi visti fino ad ora e osserviamo che l'aggiunta di un multiplo di Pnon ha alcun effetto su un modulo già visto.

Quindi, dobbiamo solo cambiare nper soddisfare n%p == aaggiungendo il giusto multiplo di P. Risolviamo per il coefficiente c:

(n + P*c) % p == a

Ciò richiede che c = (a-n) * P^(-1), laddove l'inverso sia preso modulo p. Come altri notano, l'inverso può essere calcolato dal Piccolo Teorema di Fermat come P^(-1) = pow(P,p-2,p). Quindi, c = (a-n) * pow(P,p-2,p)e aggiorniamo nda n+= P * (a-n) * pow(P,p-2,p).


1

Haskell, 68 100 byte

f l=sum[p#(m-2)*n*p|(m,n)<-l,let a#0=1;a#n=(a#div n 2)^2*a^mod n 2`mod`m;p=product(map fst l)`div`m]

Utilizzo: f [(5,1), (73,4), (59,30), (701,53), (139,112)]-> 142360350966.

Modifica: ora con una veloce funzione "power / mod". Vecchia versione (68 byte) con funzione di alimentazione integrata:

f l=sum[l#m^(m-2)`mod`m*n*l#m|(m,n)<-l]
l#m=product(map fst l)`div`m

Ho il sospetto che la tua implementazione di power-mod non sia un tempo polinomiale poiché l'esponente produce un numero enorme prima della mod. Hai provato l'ultimo test case?
xnor

@xnor: l'ultimo caso di test esaurisce la memoria dopo alcuni secondi sul mio computer da 2 GB. Ho aggiunto una funzione power / mod veloce.
nimi,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.