Pyth, 83 82 byte
=eAQM.^GHQKf%=/H=2;1=gftgT/Q;1HJg~gGHh/H2WtG=*J=gT^2t-K=Kfq1gG^2T1=%*G=^T2Q;hS%_BJ
Suite uji
Program ini mengimplementasikan algoritma Tonelli-Shanks . Saya menulisnya dengan cermat mengikuti halaman Wikipedia. Dibutuhkan sebagai input (n, p)
.
Tidak adanya akar kuadrat dilaporkan oleh kesalahan berikut:
TypeError: pow() 3rd argument not allowed unless all arguments are integers
Ini adalah kode golf yang sangat rumit, ditulis dengan gaya imperatif, berbeda dengan gaya fungsional Pyth yang lebih umum.
Aspek halus Pyth yang saya gunakan adalah =
, yang, jika tidak segera diikuti oleh variabel, mencari maju dalam program untuk variabel berikutnya, kemudian memberikan hasil dari ekspresi berikut untuk variabel itu, kemudian mengembalikan hasil itu. Saya akan merujuk seluruh penjelasan ke halaman wikipedia: Algoritma Tonelli-Shanks , karena itulah algoritma yang saya terapkan.
Penjelasan:
=eAQ
A
mengambil 2-tuple sebagai input, dan memberikan nilai masing G
- H
masing, dan mengembalikan inputnya. Q
adalah input awal. e
mengembalikan elemen terakhir dari suatu urutan. Setelah cuplikan ini, G
adalah n
, dan H
dan Q
sedang p
.
M.^GHQ
M
mendefinisikan fungsi input 2 g
, di mana input berada G
dan H
. .^
adalah fungsi eksponensial modular cepat Pyth. Cuplikan ini didefinisikan g
sebagai mod eksponensial Q
.
Kf%=/H=2;1
f
mendefinisikan pengulangan sampai false loop, dan mengembalikan jumlah iterasi yang dijalankannya, diberikan 1
sebagai inputnya. Selama setiap iterasi loop, kami membaginya H
dengan 2, set H
ke nilai itu, dan memeriksa apakah hasilnya aneh. Setelah itu, kita berhenti. K
menyimpan jumlah iterasi yang dibutuhkan.
Satu hal yang sangat sulit adalah =2;
bagiannya. =
mencari di depan untuk variabel berikutnya, yaitu T
, jadi T
diatur ke 2. Namun, T
di dalam f
loop adalah penghitung iterasi, jadi kami gunakan ;
untuk mendapatkan nilai dari T
lingkungan global. Ini dilakukan untuk menyimpan beberapa byte spasi putih yang jika tidak akan diperlukan untuk memisahkan angka.
Setelah potongan ini, K
adalah S
dari artikel wikipedia (wiki), dan H
adalah Q
dari wiki, dan T
ini 2
.
=gftgT/Q;1H
Sekarang, kita perlu menemukan mod nonresid kuadratik p
. Kami akan memaksa ini menggunakan kriteria Euler. /Q2
adalah (p-1)/2
, karena /
adalah divisi lantai, sehingga ftgT/Q;1
menemukan bilangan bulat pertama di T
mana T ^ ((p-1)/2) != 1
, seperti yang diinginkan. Ingat itu ;
lagi menarik T
dari lingkungan global, yang masih 2. Hasil ini z
dari wiki.
Selanjutnya, untuk membuat c
dari wiki, kita perlu z^Q
, jadi kita bungkus di atas g ... H
dan tetapkan hasilnya T
. Sekarang T
adalah c
dari wiki.
Jg~gGHh/H2
Mari kita memisahkan ini: ~gGH
. ~
seperti =
, tetapi mengembalikan nilai asli variabel, bukan nilai baru. Jadi, ia kembali G
, yang n
dari wiki.
Ini memberikan J
nilai n^((Q+1)/2)
, yaitu R
dari wiki.
Sekarang, yang berikut ini berlaku:
~gGH
Ini memberikan G
nilai n^Q
, yang berasal t
dari wiki.
Sekarang, kita telah mengatur variabel loop kami. M, c, t, R
dari wiki adalah K, T, G, J
.
Tubuh loop rumit, jadi saya akan menyajikannya dengan spasi putih, cara saya menulisnya:
WtG
=*J
=
gT^2
t-
K
=Kfq1gG^2T1
=%*G=^T2Q;
Pertama, kita periksa apakah G
1. Jika demikian, kita keluar dari loop.
Kode berikutnya yang berjalan adalah:
=Kfq1gG^2T1
Di sini, kami mencari nilai pertama i
sedemikian rupa G^(2^i) mod Q = 1
, mulai dari 1. Hasilnya disimpan K
.
=gT^2t-K=Kfq1gG^2T1
Di sini, kita mengambil nilai lama K
, kurangi nilai baru K
, kurangi 1, naikkan 2 ke daya itu, lalu naikkan T
ke mod daya itu Q
, lalu tetapkan hasilnya T
. Ini T
sama dengan b
dari wiki.
Ini juga merupakan garis yang mengakhiri loop dan gagal jika tidak ada solusi, karena dalam hal ini nilai baru K
akan sama dengan nilai lama K
, 2 akan dinaikkan ke -1
, dan eksponensial modular akan menimbulkan kesalahan.
=*J
Selanjutnya, kita kalikan J
dengan hasil di atas dan simpan kembali J
, tetap R
perbarui.
=^T2
Lalu kami menyiku T
dan menyimpan hasilnya kembali T
, mengatur T
kembali ke c
dari wiki.
=%*G=^T2Q
Lalu kita kalikan G
dengan hasil itu, ambil modnya Q
dan simpan hasilnya kembali G
.
;
Dan kami mengakhiri loop.
Setelah loop selesai, J
adalah akar kuadrat dari n
mod p
. Untuk menemukan yang terkecil, kami menggunakan kode berikut:
hS%_BJ
_BJ
membuat daftar J
dan negasinya, %
secara implisit mengambil Q
sebagai argumen kedua, dan menggunakan perilaku default Pyth untuk diterapkan % ... Q
ke setiap anggota urutan. Kemudian S
mengurutkan daftar dan h
mengambil anggota pertamanya, minimum.