Tulis sebuah program untuk memfaktorkan bilangan semi-prima dalam waktu singkat.
Untuk tujuan pengujian, gunakan ini: 38! +1 (523022617466601111760007224100074291200000001)
Itu sama dengan: 14029308060317546154181 × 37280713718589679646221
fastest-code
primes
Soham Chowdhury
sumber
sumber
12259243
akan digunakan untuk menguji seberapa cepat program, hasilnya akan sangat kecil sehingga Anda tidak akan mendapatkan perbedaan yang signifikan secara statistik.Jawaban:
Python (w / PyPy JIT v1.9) ~ 1.9s
Menggunakan Multiple Polynomial Quadratic Sieve . Saya menganggap ini sebagai tantangan kode, jadi saya memilih untuk tidak menggunakan perpustakaan eksternal (selain
log
fungsi standar , saya kira). Saat menghitung waktu, JIT PyPy harus digunakan, karena menghasilkan timing 4-5 kali lebih cepat daripada cPython .Pembaruan (2013-07-29):
Sejak awalnya memposting, saya telah membuat beberapa perubahan kecil, tetapi signifikan yang meningkatkan kecepatan keseluruhan dengan faktor sekitar 2,5x.
Pembaruan (2014-08-27):
Karena posting ini masih menerima perhatian, saya telah memperbarui
my_math.py
memperbaiki dua kesalahan, untuk siapa saja yang mungkin menggunakannya:isqrt
rusak, terkadang menghasilkan output yang salah untuk nilai yang sangat dekat dengan kuadrat sempurna. Ini telah diperbaiki, dan kinerjanya meningkat dengan menggunakan benih yang jauh lebih baik.is_prime
telah diperbarui. Upaya saya sebelumnya untuk menghapus 2-sprps kuadrat sempurna adalah setengah hati, paling-paling. Saya telah menambahkan cek 3-sprp - teknik yang digunakan oleh Mathmatica - untuk memastikan bahwa nilai yang diuji bebas persegi.Pembaruan (2014-11-24):
Jika pada akhir perhitungan tidak ditemukan kongruensi non-sepele, progam sekarang menyaring polinomial tambahan. Ini sebelumnya ditandai dalam kode sebagai
TODO
.mpqs.py
my_math.py
Sampel I / O:
Catatan: tidak menggunakan
--verbose
opsi akan memberikan timing yang sedikit lebih baik:Konsep dasar
Secara umum, ayakan kuadrat didasarkan pada pengamatan berikut: setiap komposit n aneh dapat direpresentasikan sebagai:
Ini tidak terlalu sulit untuk dikonfirmasi. Karena n ganjil, jarak antara dua kofaktor n harus genap 2d , di mana x adalah titik tengah di antara mereka. Selain itu, hubungan yang sama berlaku untuk kelipatan n
Perhatikan bahwa jika ada x dan d tersebut dapat ditemukan, itu akan segera menghasilkan faktor (tidak harus prima) dari n , karena x + d dan x - d keduanya membagi n berdasarkan definisi. Hubungan ini selanjutnya dapat dilemahkan - dengan konsekuensi memungkinkan kongruensi sepele yang potensial - ke bentuk berikut:
Jadi secara umum, jika kita dapat menemukan dua kuadrat sempurna yang setara dengan mod n , maka sangat mungkin bahwa kita dapat langsung menghasilkan faktor n a la gcd (x ± d, n) . Tampaknya cukup sederhana, bukan?
Kecuali tidak. Jika kami bermaksud melakukan pencarian lengkap atas semua kemungkinan x , kita perlu mencari seluruh rentang dari [ √ n , √ ( 2n ) ], yang sedikit lebih kecil dari divisi percobaan penuh, tetapi juga membutuhkan
is_square
operasi mahal setiap iterasi untuk konfirmasi nilai d . Kecuali diketahui sebelumnya bahwa n memiliki faktor yang sangat dekat √ n , divisi percobaan kemungkinan akan lebih cepat.Mungkin kita bisa melemahkan hubungan ini lebih jauh lagi. Misalkan kita memilih x , sehingga untuk
faktorisasi utama penuh y sudah diketahui. Jika kita memiliki cukup hubungan seperti itu, kita harus dapat membangun sebuah d yang memadai , jika kita memilih sejumlah y sedemikian rupa sehingga produk mereka adalah kuadrat sempurna; yaitu, semua faktor prima digunakan beberapa kali. Bahkan, jika kita memiliki lebih banyak y daripada jumlah total faktor prima unik yang dikandungnya, sebuah solusi dijamin ada; Ini menjadi sistem persamaan linear. Pertanyaannya sekarang adalah, bagaimana kita memilih x seperti itu ? Di situlah pengayakan berperan.
Saringan
Pertimbangkan polinomialnya:
Kemudian untuk setiap prime p dan integer k , yang berikut ini benar:
Ini berarti bahwa setelah menyelesaikan akar dari polinomial mod p - yaitu, Anda telah menemukan x sehingga y (x) ≡ 0 (mod p) , ergo y dapat dibagi oleh p - maka Anda telah menemukan angka tak hingga dari x tersebut . Dengan cara ini, Anda dapat menyaring rentang x , mengidentifikasi faktor prima kecil y , semoga menemukan beberapa yang semua faktor prima kecil. Angka-angka seperti itu dikenal sebagai k-smooth , di mana k adalah faktor utama terbesar yang digunakan.
Namun, ada beberapa masalah dengan pendekatan ini. Tidak semua nilai x memadai, pada kenyataannya, hanya ada sangat sedikit dari mereka, berpusat di sekitar √ n . Nilai yang lebih kecil akan menjadi sebagian besar negatif (karena istilah -n ), dan nilai yang lebih besar akan menjadi terlalu besar, sehingga tidak mungkin bahwa faktorisasi utama mereka hanya terdiri dari bilangan prima kecil. Akan ada sejumlah x seperti itu , tetapi kecuali jika komposit yang Anda anjak sangat kecil, sangat tidak mungkin bahwa Anda akan menemukan cukup smooth untuk menghasilkan faktorisasi. Jadi, untuk n yang lebih besar , menjadi perlu untuk menyaring beberapa polinomial dari bentuk yang diberikan.
Banyak Polinomial
Jadi kita perlu lebih banyak polinomial untuk diayak? Bagaimana dengan ini:
Itu akan berhasil. Perhatikan bahwa A dan B secara harfiah bisa berupa nilai integer apa pun, dan matematika masih berlaku. Yang perlu kita lakukan adalah memilih beberapa nilai acak, menyelesaikan untuk akar polinomial, dan menyaring nilai mendekati nol. Pada titik ini kita bisa menyebutnya cukup baik: jika Anda melempar cukup banyak batu ke arah acak, Anda pasti akan merusak jendela cepat atau lambat.
Kecuali, ada masalah dengan itu juga. Jika kemiringan polinomial besar pada intersep x, yang akan terjadi jika tidak relatif datar, hanya akan ada beberapa nilai yang sesuai untuk ayakan per polinomial. Ini akan berhasil, tetapi pada akhirnya Anda akan menyaring banyak polinomial sebelum mendapatkan yang Anda butuhkan. Bisakah kita berbuat lebih baik?
Kita bisa melakukan yang lebih baik. Pengamatan, sebagai hasil dari Montgomery adalah sebagai berikut: jika A dan B dipilih sedemikian rupa sehingga ada beberapa C yang memuaskan
maka seluruh polinomial dapat ditulis ulang sebagai
Lebih lanjut, jika A dipilih menjadi kuadrat sempurna, suku A yang memimpin dapat diabaikan sementara pengayakan, menghasilkan nilai yang jauh lebih kecil, dan kurva yang jauh lebih rata. Agar solusi seperti itu ada, n harus merupakan mod residu kuadrat √ A , yang dapat segera diketahui dengan menghitung simbol Legendre : ( n | √A ) = 1 . Perhatikan bahwa untuk menyelesaikan B , faktorisasi lengkap lengkap √A perlu diketahui (untuk mengambil akar kuadrat modular √ n (mod √A) ), itulah sebabnya √A biasanya dipilih untuk menjadi prima.
Kemudian dapat ditunjukkan bahwa jika , maka untuk semua nilai x ∈ [ -M, M ] :
Dan sekarang, akhirnya, kita memiliki semua komponen yang diperlukan untuk menerapkan saringan kita. Atau apakah kita?
Kekuatan Primes sebagai Faktor
Saringan kami, seperti dijelaskan di atas, memiliki satu kelemahan utama. Hal ini dapat mengidentifikasi nilai-nilai x akan menghasilkan y dibagi dengan p , tetapi tidak dapat mengidentifikasi apakah atau tidak ini y habis dibagi oleh kekuatan dari p . Untuk menentukan itu, kita perlu melakukan pembagian percobaan pada nilai yang akan disaring, sampai tidak lagi dapat dibagi oleh p . Kami sepertinya telah mencapai jalan buntu: inti dari ayakan itu adalah agar kami tidak harus melakukan itu. Saatnya memeriksa buku pedoman.
Itu terlihat sangat berguna. Jika jumlah ln dari semua faktor prima kecil y mendekati nilai yang diharapkan dari ln (y) , maka hampir diberikan bahwa y tidak memiliki faktor lain. Selain itu, jika kita menyesuaikan sedikit nilai yang diharapkan, kita juga dapat mengidentifikasi nilai-nilai yang halus yang memiliki beberapa kekuatan bilangan prima sebagai faktor. Dengan cara ini, kita dapat menggunakan saringan sebagai proses 'pra-penyaringan', dan hanya memfaktorkan nilai-nilai yang cenderung lancar.
Ini memiliki beberapa keunggulan lain juga. Perhatikan bahwa bilangan prima kecil berkontribusi sangat sedikit ke ln sum, tetapi belum mereka membutuhkan waktu yang paling saringan. Penyaringan nilai 3 membutuhkan lebih banyak waktu daripada 11, 13, 17, 19, dan 23 digabungkan . Sebagai gantinya, kita bisa melewatkan beberapa bilangan prima pertama, dan menyesuaikan ambang batas sesuai, dengan asumsi persentase tertentu dari mereka akan berlalu.
Hasil lain, adalah bahwa sejumlah nilai akan diizinkan 'lolos', yang sebagian besar mulus, tetapi mengandung satu kofaktor besar. Kami hanya bisa membuang nilai-nilai ini, tetapi anggaplah kami menemukan nilai lain yang sebagian besar mulus, dengan kofaktor yang persis sama. Kita kemudian dapat menggunakan kedua nilai ini untuk membangun y yang dapat digunakan ; karena produk mereka akan mengandung kofaktor besar ini kuadrat, tidak perlu lagi dipertimbangkan.
Menyatukan semuanya
Hal terakhir yang perlu kita lakukan adalah menggunakan nilai-nilai dari y construct x dan d yang memadai . Misalkan kita hanya mempertimbangkan faktor-faktor non-kuadrat dari y , yaitu faktor utama dari kekuatan ganjil. Kemudian, masing-masing y dapat diekspresikan dengan cara berikut:
yang dapat diekspresikan dalam bentuk matriks:
Masalahnya kemudian menjadi menemukan vektor v sedemikian rupa sehingga vM = ⦳ (mod 2) , di mana ⦳ adalah vektor nol. Artinya, untuk memecahkan ruang nol sebelah kiri M . Hal ini dapat dilakukan dengan beberapa cara, yang paling sederhana adalah dengan melakukan Gaussian Elimination pada M T , menggantikan operasi penambahan baris dengan xor baris . Ini akan menghasilkan sejumlah vektor berbasis ruang nol, kombinasi apa pun yang akan menghasilkan solusi yang valid.
Konstruksi x cukup mudah. Ini hanyalah produk dari Ax + B untuk masing-masing y yang digunakan. Konstruksi d sedikit lebih rumit. Jika kita mengambil produk dari semua y , kita akan berakhir dengan nilai dengan 10s dari ribuan, jika tidak 100s dari ribuan digit, yang mana kita perlu menemukan akar kuadrat. Perhitungan ini tidak praktis mahal. Sebaliknya, kita bisa melacak kekuatan bahkan bilangan prima selama proses penyaringan, dan kemudian menggunakan dan dan XOR operasi pada vektor faktor non-square untuk merekonstruksi akar kuadrat.
Saya sepertinya telah mencapai batas 30000 karakter. Ahh, kurasa itu sudah cukup.
sumber
Nah, 38! +1 Anda memecahkan skrip php saya, tidak yakin mengapa. Bahkan, setiap semi-prime lebih dari 16 digit sudah lama mematahkan skrip saya.
Namun, dengan menggunakan 8980935344490257 (86028157 * 104395301) skrip saya mengatur waktu 25,963 detik pada komputer di rumah saya (AMD Phenom 9950 2,61GHz). Jauh lebih cepat daripada komputer kerja saya yang hampir 31 detik @ 2.93GHz Core 2 Duo.
php - 757 karakter termasuk baris baru
Saya akan tertarik untuk melihat algoritma yang sama ini di c atau bahasa lain yang dikompilasi.
sumber
lcm(2, 3, 5, 7) == 210
, pola angka yang dihilangkan oleh faktor-faktor ini akan berulang setiap 210 angka, dan hanya 48 yang tersisa. Dengan cara itu, Anda dapat menghilangkan 77% dari semua angka dari divisi percobaan, alih-alih 50% dengan hanya mengambil peluang.