Teorema Sisa Tiongkok

21

The Chinese Remainder Theorem memberitahu kita bahwa kita selalu dapat menemukan sejumlah yang menghasilkan setiap sisanya diperlukan bawah modulus prima yang berbeda. Tujuan Anda adalah menulis kode untuk menghasilkan angka seperti itu dalam waktu polinomial. Kode terpendek menang.

Misalnya, kita diberi batasan ini ( %mewakili mod):

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

Salah satu solusinya adalah n=44. Batasan pertama dipenuhi karena 44 = 6*7 + 2, dan 44masih ada 2ketika dibagi oleh 7, dan dengan demikian 44 % 7 == 2. Dua kendala lainnya terpenuhi juga. Ada solusi lain, seperti n=814dan n=-341.

Memasukkan

Daftar pasangan yang tidak kosong (p_i,a_i), di mana setiap modulus p_iadalah bilangan prima yang berbeda dan setiap target a_iadalah bilangan alami dalam kisaran 0 <= a_i < p_i. Anda dapat mengambil input dalam bentuk apa pun yang nyaman; itu tidak harus benar-benar menjadi daftar pasangan. Anda tidak boleh berasumsi bahwa input diurutkan.

Keluaran

Integer nsedemikian rupa sehingga n % p_i == a_iuntuk setiap indeks i. Itu tidak harus menjadi nilai terkecil, dan mungkin negatif.

Pembatasan waktu polinomial

Untuk mencegah solusi murah yang hanya mencoba n=0, n=1, n=2, dan sebagainya, kode Anda harus berjalan dalam waktu polinomial dalam panjang input . Perhatikan bahwa angka mdalam input memiliki panjang Θ(log m), jadi mitu sendiri tidak polinomial dalam panjangnya. Ini berarti bahwa Anda tidak dapat menghitung hingga matau melakukan waktu operasi m, tetapi Anda dapat menghitung operasi aritmatika pada nilai-nilai.

Anda tidak boleh menggunakan format input yang tidak efisien seperti unary untuk menyiasati ini.

Larangan lainnya

Built-in untuk melakukan hal-hal berikut tidak diperbolehkan: Menerapkan teorema Sisa Cina, menyelesaikan persamaan, atau nomor faktor.

Anda dapat menggunakan built-in untuk menemukan mod dan melakukan penambahan, pengurangan, penggandaan, dan eksponensial modular (dengan eksponen angka alami). Anda tidak boleh menggunakan operasi modular bawaan lainnya, termasuk pembalikan modular, pembagian, dan pencarian pesanan.

Uji kasus

Ini memberikan solusi non-negatif terkecil. Jawaban Anda mungkin berbeda. Mungkin lebih baik jika Anda memeriksa secara langsung bahwa output Anda memenuhi setiap kendala.

[(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
Tidak
sumber
Kenapa tidak ada pembagian?
jimmy23013
@ user23013 Tidak ada pembagian modular, karena pada dasarnya modular terbalik.
xnor
Apakah inversi matriks dianggap sebagai penyelesaian persamaan?
flawr
@ flawr: Saya kira begitu.
Alex A.
@ xnor: Bagaimana menurut Anda? Lalu bagaimana dengan fungsi optimasi?
flawr

Jawaban:

9

Mathematica, 55 51 45

Pembalikan modular dilarang, tetapi eksponensial modular diizinkan. Dengan teorema kecil Fermat n^(-1) % p == n^(p-2) % p,.

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

Contoh:

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

Hanya untuk bersenang-senang:

ChineseRemainder@@Reverse@Thread@#&
alephalpha
sumber
1
Anda dapat menyimpan satu byte dengan menukar urutan argumen dari fungsi terdalam, sehingga Anda dapat menggunakan PowerMod[#2,#-2,#]dan saya juga tidak berpikir ada persyaratan untuk fungsi yang akan dinamai, membawanya ke 48.
Martin Ender
Ya, fungsi yang tidak disebutkan namanya OK.
xnor
6

Python 2, 165 101 99 98 85 byte

Menggunakan teorema kecil Fermat seperti jawaban lainnya. Jangan repot-repot menjaga jumlah akhir dalam jangkauan modular, karena kami tidak tertarik dengan solusi terkecil. Terima kasih Volatilitas untuk menghemat 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
Uri Granta
sumber
1
Anda dapat menghapus ruang sebelumnya for.
isaacg
1
x/a*b*pow(x/a,a-2,a)for a,b in lharus bekerja.
Volatilitas
Poin luar biasa! Saya mencoba untuk menyingkirkan redundansi yang jelas di sana tetapi lupa bahwa saya hanya bisa membongkar.
Uri Granta
4

Pyth, 40 37 36 29

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

Menggunakan teorema kecil Fermat, terima kasih kepada alephalpha. Menghitung menggunakan rumus ini .

orlp
sumber
3

Ruby, 129

Nah, kawan-kawan, sepertinya solusi Ruby harus lebih panjang karena eksponensial modular tidak tersedia tanpa memuat perpustakaan openssl dan melakukan konversi ke OpenSSL :: BN. Tetap saja senang menulisnya:

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)
Atsby
sumber
Anda tidak perlu parens saat memanggil require, evalatau 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

Ini menggunakan variasi konstruksi produk yang digunakan jawaban lain.

Idenya adalah untuk mengulangi kendala dan memperbarui solusi nuntuk memenuhi kendala saat ini tanpa mengacaukan yang sebelumnya. Untuk melakukannya, kami melacak produk Pbilangan prima yang terlihat sampai sekarang, dan mengamati bahwa menambahkan kelipatan Ptidak memiliki efek modulo pada prime yang sudah terlihat.

Jadi, kita hanya perlu mengubah nuntuk memuaskan n%p == adengan menambahkan kelipatan yang tepat P. Kami memecahkan untuk koefisien c:

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

Ini mengharuskan c = (a-n) * P^(-1), di mana invers diambil modulo p. Seperti yang dicatat orang lain, kebalikannya dapat dihitung oleh Teorema Kecil Fermat sebagai P^(-1) = pow(P,p-2,p). Jadi,, c = (a-n) * pow(P,p-2,p)dan kami memperbarui noleh n+= P * (a-n) * pow(P,p-2,p).

Tidak
sumber
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]

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

Edit: sekarang dengan fungsi "power / mod" yang cepat. Versi lama (68 byte) dengan fungsi daya bawaan:

f l=sum[l#m^(m-2)`mod`m*n*l#m|(m,n)<-l]
l#m=product(map fst l)`div`m
nimi
sumber
Saya menduga implementasi power-mod Anda bukan polinomial-waktu karena eksponen menghasilkan sejumlah besar sebelum mod. Sudahkah Anda mencoba test case terakhir?
xnor
@ xnor: test case terakhir kehabisan memori setelah beberapa detik pada mesin 2GB saya. Saya telah menambahkan fungsi daya / mod yang cepat.
nimi