Papan peringkat
User Language Score
=========================================
Ell C++11 293,619,555
feersum C++11 100,993,667
Ell C++11 78,824,732
Geobits Java 27,817,255
Ell Python 27,797,402
Peter Taylor Java 2,468
<reference> Julia 530
Latar Belakang
Saat bekerja pada kisi-kisi integer 2-D, Anda terkadang ingin tahu apakah dua vektor (dengan komponen bilangan bulat) memiliki besaran yang sama. Tentu saja, dalam geometri Euclidean besarnya vektor (x,y)
diberikan oleh
√(x² + y²)
Jadi implementasi yang naif mungkin menghitung nilai ini untuk kedua vektor dan membandingkan hasilnya. Tidak hanya itu menimbulkan perhitungan akar kuadrat yang tidak perlu, itu juga menyebabkan masalah dengan ketidakakuratan titik mengambang, yang mungkin menghasilkan positif palsu: vektor yang besarnya berbeda, tetapi di mana angka signifikan dalam representasi titik mengambang semuanya identik.
Untuk keperluan tantangan ini, kami mendefinisikan false positive sebagai sepasang pasangan koordinat (a,b)
dan (c,d)
yang:
- Besarnya kuadrat berbeda ketika diwakili sebagai integer 64-bit unsigned.
- Besarnya identik ketika direpresentasikan sebagai angka titik mengambang biner 64-bit dan dihitung melalui akar kuadrat 64-bit (sesuai IEEE 754 ).
Sebagai contoh, menggunakan representasi 16-bit (bukan 64), 1 pasangan vektor terkecil yang menghasilkan false positive adalah
(25,20) and (32,0)
Besarnya kuadrat kuadrat adalah 1025
dan 1024
. Mengambil hasil akar kuadrat
32.01562118716424 and 32.0
Tetapi dalam 16-bit mengapung keduanya terpotong 32.0
.
Demikian juga, pasangan 2 terkecil yang menghasilkan false positive untuk representasi 32-bit adalah
(1659,1220) and (1951,659)
1 "Terkecil" yang diukur dengan besarnya floating point 16-bit.
2 "Terkecil" yang diukur dengan besarnya floating point 32-bit.
Terakhir, berikut adalah beberapa kasus 64-bit yang valid:
(51594363,51594339) and (54792160,48184783)
(54356775,54353746) and (54620742,54088476)
(54197313,46971217) and (51758889,49645356)
(67102042, 956863) and (67108864, 6) *
*
Kasus terakhir adalah salah satu dari beberapa dengan besaran terkecil yang mungkin untuk false positive 64-bit.
Tantangan
Dalam kurang dari 10.000 byte kode, menggunakan satu utas, Anda akan menemukan banyak false positive untuk angka floating point 64-bit (biner) dalam rentang koordinat 0 ≤ y ≤ x
(yaitu, hanya dalam oktan pertama bidang Euclidian) sedemikian rupa sehingga dalam 10 menitx² + y² ≤ 253
. Jika dua pengajuan mengikat untuk jumlah pasangan yang sama, pemecah dasi adalah waktu yang sebenarnya diambil untuk menemukan pasangan yang terakhir.
Program Anda tidak boleh menggunakan lebih dari 4 GB memori kapan saja (untuk alasan praktis).
Harus dimungkinkan untuk menjalankan program Anda dalam dua mode: satu yang menghasilkan setiap pasangan saat menemukannya, dan satu yang hanya menampilkan jumlah pasangan yang ditemukan di akhir. Yang pertama akan digunakan untuk memverifikasi validitas pasangan Anda (dengan melihat beberapa sampel output) dan yang kedua akan digunakan untuk menentukan waktu pengiriman Anda. Perhatikan bahwa pencetakan harus menjadi satu - satunya perbedaan. Secara khusus, program penghitungan tidak dapat meng-hardcode jumlah pasangan yang dapat ditemukannya. Itu masih harus melakukan loop yang sama yang akan digunakan untuk mencetak semua angka, dan hanya menghilangkan pencetakan itu sendiri!
Saya akan menguji semua pengiriman pada laptop Windows 8 saya, jadi silakan tanyakan di komentar jika Anda ingin menggunakan bahasa yang tidak terlalu umum.
Perhatikan bahwa pasangan tidak boleh boleh dihitung dua kali dengan beralih dari pasangan koordinat pertama dan kedua.
Perhatikan juga bahwa saya akan menjalankan proses Anda melalui pengontrol Ruby, yang akan mematikan proses Anda jika belum selesai setelah 10 menit. Pastikan untuk menampilkan jumlah pasangan yang ditemukan saat itu. Anda dapat melacak waktu sendiri dan mencetak hasilnya sesaat sebelum 10 menit berlalu, atau Anda hanya menampilkan jumlah pasangan yang ditemukan secara sporadis, dan saya akan mengambil angka terakhir sebagai skor Anda.
sumber
Jawaban:
C ++, 275.000.000+
Kami akan merujuk pada pasangan yang besarnya dapat diwakili secara akurat, seperti (x, 0) , sebagai pasangan yang jujur dan untuk semua pasangan lainnya sebagai pasangan yang tidak jujur dari besarnya m , di mana m adalah besarnya pasangan yang dilaporkan salah. Program pertama pada posting sebelumnya menggunakan satu set pasangan yang terkait erat pasangan jujur dan tidak jujur:
(x, 0) dan (x, 1) , masing-masing, untuk x yang cukup besar. Program kedua menggunakan pasangan pasangan yang tidak jujur yang sama tetapi memperluas pasangan pasangan yang jujur dengan mencari semua pasangan jujur yang besarnya tidak terpisahkan. Program tidak berhenti dalam sepuluh menit, tetapi menemukan sebagian besar hasilnya sangat awal, yang berarti bahwa sebagian besar runtime menjadi sia-sia. Alih-alih terus mencari pasangan jujur yang semakin jarang, program ini menggunakan waktu luang untuk melakukan hal logis berikutnya: memperluas perangkat tidak jujur pasangan .
Dari posting sebelumnya kita tahu bahwa untuk semua bilangan bulat besar cukup r , sqrt (r 2 + 1) = r , di mana sqrt adalah floating-point fungsi akar kuadrat. Rencana kami dari serangan adalah untuk menemukan pasangan P = (x, y) sehingga x 2 + y 2 = r 2 + 1 untuk beberapa besar cukup bilangan bulat r . Itu cukup sederhana untuk dilakukan, tetapi secara naif mencari pasangan seperti itu terlalu lambat untuk menarik. Kami ingin menemukan pasangan ini secara massal, seperti yang kami lakukan untuk pasangan jujur di program sebelumnya.
Biarkan { v , w } menjadi pasangan vektor ortonormal. Untuk semua skalar nyata r , || r v + w || 2 = r 2 + 1 . Dalam ℝ 2 , ini adalah akibat langsung dari teorema Pythagoras:
Kami sedang mencari vektor v dan w sedemikian rupa sehingga ada bilangan r yang x dan y juga bilangan bulat. Sebagai catatan tambahan, perhatikan bahwa himpunan pasangan tidak jujur yang kami gunakan dalam dua program sebelumnya hanyalah kasus khusus ini, di mana { v , w } adalah basis standar dari ℝ 2 ; kali ini kami ingin menemukan solusi yang lebih umum. Di sinilah kembar tiga Pythagoras (bilangan bulat bilangan bulat (a, b, c) memuaskan a 2 + b 2 = c 2, yang kami gunakan dalam program sebelumnya) membuat comeback mereka.
Biarkan (a, b, c) menjadi triplet Pythagoras. Vektor v = (b / c, a / c) dan w = (-a / c, b / c) (dan juga
w = (a / c, -b / c) ) adalah ortonormal, karena mudah untuk memverifikasi . Ternyata, untuk sembarang pilihan triplet Pythagoras, ada bilangan r sehingga x dan y adalah bilangan bulat. Untuk membuktikan ini, dan untuk secara efektif menemukan r dan P , kita memerlukan sedikit teori / kelompok; Saya akan menyimpan rinciannya. Either way, misalkan kita memiliki r integral , x dan y . Kami masih kekurangan beberapa hal: kami perlu rcukup besar dan kami ingin metode cepat untuk mendapatkan lebih banyak pasangan serupa dari yang satu ini. Untungnya, ada cara sederhana untuk mencapai ini.
Perhatikan bahwa proyeksi P ke v adalah r v , maka r = P · v = (x, y) · (b / c, a / c) = xb / c + ya / c , semua ini untuk mengatakan bahwa xb + ya = rc . Akibatnya, untuk semua bilangan bulat n , (x + bn) 2 + (y + an) 2 = (x 2 + y 2 ) + 2 (xb + ya) n + (a 2 + b 2 ) n 2 = ( r 2 + 1) + 2 (rc) n + (c 2 ) n 2 = (r + cn) 2 + 1. Dengan kata lain, besarnya kuadrat pasangan bentuk
(x + bn, y + an) adalah (r + cn) 2 + 1 , yang merupakan jenis pasangan yang kami cari! Untuk n yang cukup besar , ini adalah pasangan magnitudo r + cn yang tidak jujur .
Selalu menyenangkan untuk melihat contoh konkret. Jika kita mengambil triplet Pythagoras (3, 4, 5) , maka pada r = 2 kita memiliki P = (1, 2) (Anda dapat memeriksa bahwa (1, 2) · (4/5, 3/5) = 2 dan, jelas, 1 2 + 2 2 = 2 2 + 1. ) Menambahkan 5 ke r dan (4, 3) ke P membawa kita ke r '= 2 + 5 = 7 dan P' = (1 + 4, 2 + 3) = (5, 5) . Lihatlah, 5 2 + 5 2 = 7 2 + 1. Koordinat berikutnya adalahr '' = 12 dan P '' = (9, 8) , dan lagi, 9 2 + 8 2 = 12 2 +1 , dan seterusnya, dan seterusnya ...
Setelah r cukup besar, kita mulai mendapatkan pasangan yang tidak jujur dengan kenaikan 5 . Itu kira-kira 27.797.402 / 5 pasangan tidak jujur.
Jadi sekarang kita memiliki banyak pasangan tidak jujur yang besarnya tidak terpisahkan. Kita dapat dengan mudah memasangkan mereka dengan pasangan jujur dari program pertama untuk membentuk false-positive, dan dengan hati-hati kita juga dapat menggunakan pasangan jujur dari program kedua. Inilah yang pada dasarnya dilakukan oleh program ini. Seperti program sebelumnya, ia juga menemukan sebagian besar hasilnya sangat awal --- ia mendapatkan 200.000.000 positif palsu dalam beberapa detik --- dan kemudian melambat jauh.
Kompilasi dengan
g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3
. Untuk memverifikasi hasil, tambahkan-DVERIFY
(ini akan lebih lambat.)Jalankan dengan
flspos
. Argumen baris perintah apa pun untuk mode verbose.sumber
Python, 27.797.402
Hanya untuk mengatur bar sedikit lebih tinggi ...
Sangat mudah untuk memverifikasi bahwa untuk semua 67.108.864 <= x <= 94.906.265 = lantai (sqrt (2 53 )) pasangan (x, 0) dan (x, 1) adalah positif palsu.
Mengapa ini berhasil : 67.108.864 = 2 26 . Karenanya, semua angka x dalam rentang di atas adalah dari bentuk 2 26 + x ' untuk beberapa 0 <= x' <2 26 . Untuk semua e positif , (x + e) 2 = x 2 + 2xe + e 2 = x 2 + 2 27 e + 2x'e + e 2 . Jika kita ingin memiliki
(x + e) 2 = x 2 + 1 kita membutuhkan setidaknya 2 27 e <= 1 , yaitu, e <= 2 -27 Namun, karena mantissa angka floating point presisi ganda adalah lebar 52-bit, e terkecil sehingga x + e> x . Karena mode pembulatan IEEE-754 default adalah bulat-ke-terdekat; ties-to-even, itu akan selalu membulatkan ke x dan tidak pernah ke x + 2 -26 (di mana tie-break benar-benar hanya relevan untuk x = 67.108.864is e = 2 26 - 52 = 2 -26 . Dengan kata lain, angka keterwakilan terkecil yang lebih besar dari x adalah x + 2 -26 sedangkan hasil sqrt (x 2 + 1) paling banyak adalah x + 2 -27, jika sama sekali. Angka yang lebih besar akan membulatkan ke x terlepas).
C ++, 75.000.000+
Ingat bahwa 3 2 + 4 2 = 5 2 . Ini artinya bahwa titik (4, 3) terletak pada lingkaran jari-jari 5 yang berpusat di sekitar titik asal. Sebenarnya, untuk semua bilangan bulat n , (4n, 3n) terletak pada lingkaran jari-jari 5n . Untuk n yang cukup besar (yaitu, sehingga 5n> = 2 26 ), kita sudah tahu false-positive untuk semua poin pada lingkaran ini: (5n, 1) . Bagus! Itu yang lain 27.797.402 / 5 pasangan positif-palsu gratis di sana! Tetapi mengapa berhenti di sini? (3, 4, 5) bukan satu-satunya triplet tersebut.
Program ini terlihat untuk semua kembar tiga bilangan bulat positif (a, b, c) sehingga suatu 2 + b 2 = c 2 , dan jumlah false-positif dalam mode ini. Ia mencapai 70.000.000 positif palsu cukup cepat tetapi kemudian melambat ketika jumlahnya bertambah.
Kompilasi dengan
g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3
. Untuk memverifikasi hasil, tambahkan-DVERIFY
(ini akan lebih lambat.)Jalankan dengan
flspos
. Argumen baris perintah apa pun untuk mode verbose.sumber
2**53
batas dipilih untuk mengesampingkan hal ini, tetapi saya rasa tidak.C ++ 11 - 100.993.667
EDIT: Program baru.
Yang lama menggunakan terlalu banyak memori. Ini membagi dua penggunaan memori dengan menggunakan array vektor raksasa, bukan tabel hash. Itu juga menghilangkan cruft utas acak.
Jalankan dengan a
-P
argumen untuk mencetak poin, bukan nomornya.Bagi saya dibutuhkan kurang dari 2 menit dalam mode penghitungan dan sekitar 5 menit dengan pencetakan diarahkan ke file (~ 4 GB), sehingga tidak menjadi I / O terbatas.
Program asli saya rapi, tetapi saya membuang sebagian besar karena hanya bisa menghasilkan urutan 10 ^ 5 hasil. Apa yang dilakukannya adalah mencari parameterisasi bentuk (x ^ 2 + Ax + B, x ^ 2 + Cx + D), (x ^ 2 + ax + b, x ^ 2 + cx + d) sedemikian rupa sehingga untuk setiap x, (x ^ 2 + Kapak + B) ^ 2 + (x ^ 2 + Cx + D) ^ 2 = (x ^ 2 + kapak + b) ^ 2 + (x ^ 2 + cx + d) ^ 2 + 1. Ketika ia menemukan seperangkat parameter {a, b, c, d, A, B, C, D} itu dilanjutkan untuk memeriksa semua nilai x di bawah maksimum. Sambil melihat output debug saya dari program ini, saya melihat parameterisasi tertentu dari parameterisasi yang memungkinkan saya menghasilkan banyak angka dengan mudah. Saya memilih untuk tidak mencetak nomor Ell karena saya punya banyak nomor. Mudah-mudahan sekarang seseorang tidak akan mencetak kedua set angka kami dan mengklaim sebagai pemenang :)
sumber
g++ (GCC) 4.8.1
. Oke, saya menghapus bit utasnya, tetapi masih tidak mengenalistricmp
karena beberapa alasan.Pemindaian lingkaran Java, Bresenham-esque
Secara heuristik saya berharap untuk mendapatkan lebih banyak tabrakan dengan mulai dari ujung annulus yang lebih luas. Saya berharap mendapatkan beberapa peningkatan dengan melakukan satu pemindaian untuk setiap tabrakan, mencatat nilai yang
surplus
ada di antaranya0
danr2max - r2
inklusif, tetapi dalam pengujian saya yang terbukti lebih lambat daripada versi ini. Demikian pula upaya untuk menggunakanint[]
buffer tunggal daripada banyak pembuatan array dan daftar dua elemen. Optimalisasi kinerja memang binatang yang aneh.Jalankan dengan argumen baris perintah untuk output dari pasangan, dan tanpa untuk penghitungan sederhana.
sumber
Jawa - 27.817.255
Sebagian besar sama dengan apa yang ditunjukkan Ell , dan sisanya didasarkan pada
(j,0) (k,l)
. Untuk masing-masingj
, saya berjalan mundur beberapa kotak dan memeriksa apakah sisanya memberikan hasil positif palsu. Ini pada dasarnya mengambil seluruh waktu dengan hanya 25rb (sekitar 0,1%) lebih dari hanya(j,0) (j,1)
, tetapi keuntungan adalah keuntungan.Ini akan selesai dalam waktu kurang dari sepuluh menit pada mesin saya, tetapi saya tidak tahu apa yang Anda miliki. Karena alasan, jika tidak selesai sebelum waktu habis, skornya akan jauh lebih buruk. Dalam hal ini, Anda dapat mengubah pembagi pada baris 8 sehingga selesai tepat waktu (ini hanya menentukan seberapa jauh berjalannya untuk masing-masing
j
). Untuk beberapa pembagi yang berbeda, nilainya adalah:Untuk mengaktifkan output untuk setiap pertandingan (dan, Tuhan, lambat jika Anda melakukannya), cukup batalkan komentar pada baris 10 dan 19.
Untuk referensi, 20 output pertama yang diberikan (untuk pembagi = 7, tidak termasuk
(j,0)(j,1)
jenis) adalah:sumber
Julia, 530 positif salah
Berikut ini adalah pencarian brute force yang sangat naif, yang dapat Anda lihat sebagai implementasi referensi.
Anda dapat mencetak pasangan (dan besarnya kuadrat persisnya) dengan menghapus komentar pada
@printf
garis.Pada dasarnya ini memulai pencarian pada
x = y = 6e7
pasangan koordinat pertama dan memindai sekitar 1% dari jalan ke sumbu x sebelum mengurangi x. Kemudian untuk setiap pasangan koordinat seperti itu, ia memeriksa seluruh busur dengan besaran yang sama (membulatkan ke atas dan ke bawah) untuk tabrakan.Kode mengasumsikan bahwa itu dijalankan pada sistem 64-bit, sehingga tipe integer dan floating-point default adalah 64-bit (jika tidak, Anda dapat membuatnya dengan
int64()
danfloat64()
konstruktor).Itu menghasilkan sedikit hasil 530.
sumber