Dapatkah seseorang menjelaskan kepada saya cara yang efisien untuk menemukan semua faktor angka dalam Python (2.7)?
Saya dapat membuat algoritma untuk melakukan ini, tetapi saya pikir ini kode yang buruk dan membutuhkan waktu terlalu lama untuk menghasilkan hasil untuk jumlah yang besar.
primefac
? pypi.python.org/pypi/primefacJawaban:
Ini akan mengembalikan semua faktor, dengan sangat cepat, dari suatu angka
n
.Mengapa akar kuadrat sebagai batas atas?
sqrt(x) * sqrt(x) = x
. Jadi jika kedua faktor itu sama, keduanya adalah akar kuadrat. Jika Anda membuat satu faktor lebih besar, Anda harus membuat faktor lainnya lebih kecil. Ini berarti bahwa salah satu dari keduanya akan selalu kurang dari atau sama dengansqrt(x)
, jadi Anda hanya perlu mencari sampai titik itu untuk menemukan salah satu dari dua faktor yang cocok. Anda dapat menggunakannyax / fac1
untuk mendapatkanfac2
.Mereka
reduce(list.__add__, ...)
mengambil daftar kecil[fac1, fac2]
dan bergabung bersama dalam satu daftar panjang.The
[i, n/i] for i in range(1, int(sqrt(n)) + 1) if n % i == 0
pengembalian sepasang faktor jika sisa ketika Anda membagin
dengan yang lebih kecil adalah nol (tidak perlu memeriksa lebih besar juga; itu hanya akan bahwa dengan membagin
dengan yang lebih kecil.)Di
set(...)
luar menghilangkan duplikat, yang hanya terjadi untuk kotak yang sempurna. Karenan = 4
, ini akan kembali2
dua kali, jadiset
singkirkan salah satunya.sumber
sqrt
- mungkin dari sebelum orang benar-benar berpikir tentang mendukung Python 3. Saya pikir situs saya mendapatkannya dari mencobanya__iadd__
dan itu lebih cepat . Saya sepertinya ingat sesuatu tentangx**0.5
menjadi lebih cepat daripadasqrt(x)
pada titik tertentu - dan itu lebih mudah dari itu.if not n % i
bukannyaif n % i == 0
/
akan mengembalikan float bahkan jika kedua argumen adalah integer dan keduanya dapat dibagi, yaitu4 / 2 == 2.0
tidak2
.from functools import reduce
untuk membuatnya berfungsi.Solusi yang disajikan oleh @agf sangat bagus, tetapi orang dapat mencapai ~ 50% waktu berjalan lebih cepat untuk nomor ganjil sewenang-wenang dengan memeriksa paritas. Karena faktor angka ganjil selalu ganjil sendiri, tidak perlu memeriksa ini ketika berhadapan dengan angka ganjil.
Saya baru saja mulai memecahkan teka-teki Project Euler sendiri. Dalam beberapa masalah, pemeriksaan pembagi disebut di dalam dua
for
loop bersarang , dan kinerja fungsi ini sangat penting.Menggabungkan fakta ini dengan solusi agf yang luar biasa, saya berakhir dengan fungsi ini:
Namun, pada angka kecil (~ <100), overhead tambahan dari perubahan ini dapat menyebabkan fungsi lebih lama.
Saya menjalankan beberapa tes untuk memeriksa kecepatan. Di bawah ini adalah kode yang digunakan. Untuk menghasilkan plot yang berbeda, saya mengubah yang
X = range(1,100,1)
sesuai.X = rentang (1.100,1)
Tidak ada perbedaan signifikan di sini, tetapi dengan jumlah yang lebih besar, keuntungannya jelas:
X = rentang (1.100.000.1000) (hanya angka ganjil)
X = rentang (2.100000.100) (hanya angka genap)
X = rentang (1,100000,1001) (paritas bolak-balik)
sumber
jawaban agf sangat keren. Saya ingin melihat apakah saya bisa menulis ulang untuk menghindari penggunaan
reduce()
. Inilah yang saya pikirkan:Saya juga mencoba versi yang menggunakan fungsi generator rumit:
Saya menghitungnya dengan menghitung:
Saya menjalankannya sekali untuk membiarkan Python mengkompilasinya, kemudian menjalankannya di bawah perintah waktu (1) tiga kali dan menjaga waktu terbaik.
Perhatikan bahwa versi itertools sedang membangun tuple dan meneruskannya ke flatten_iter (). Jika saya mengubah kode untuk membuat daftar, itu sedikit melambat:
Saya percaya bahwa versi fungsi generator yang rumit adalah yang tercepat di Python. Tapi itu tidak jauh lebih cepat daripada versi pengurangan, kira-kira 4% lebih cepat berdasarkan pengukuran saya.
sumber
for tup in
):factors = lambda n: {f for i in range(1, int(n**0.5)+1) if n % i == 0 for f in [i, n//i]}
Pendekatan alternatif untuk jawaban AGF:
sumber
reduce()
secara signifikan lebih cepat, jadi saya cukup banyak melakukan segala sesuatu selainreduce()
bagian seperti yang dilakukan agf. Untuk keterbacaan, alangkah baiknya untuk melihat fungsi panggilan sepertiis_even(n)
daripada ekspresi sepertin % 2 == 0
.Inilah alternatif untuk solusi @ agf yang mengimplementasikan algoritma yang sama dengan gaya yang lebih pythonic:
Solusi ini berfungsi baik di Python 2 dan Python 3 tanpa impor dan jauh lebih mudah dibaca. Saya belum menguji kinerja dari pendekatan ini, tetapi asimtotis itu harus sama, dan jika kinerja menjadi perhatian serius, tidak ada solusi yang optimal.
sumber
Ada algoritma industri-kekuatan di SymPy disebut factorint :
Ini membutuhkan waktu kurang dari satu menit. Ia beralih di antara berbagai metode. Lihat dokumentasi yang ditautkan di atas.
Mengingat semua faktor prima, semua faktor lain dapat dibangun dengan mudah.
Perhatikan bahwa meskipun jawaban yang diterima diizinkan berjalan cukup lama (yaitu selamanya) untuk memperhitungkan angka di atas, untuk beberapa angka besar itu akan gagal, seperti contoh berikut. Ini karena ceroboh
int(n**0.5)
. Misalnya, kapann = 10000000000000079**2
, sudahKarena 1000000000000007979 adalah yang utama , algoritma jawaban yang diterima tidak akan pernah menemukan faktor ini. Perhatikan bahwa ini bukan hanya satu per satu; untuk jumlah yang lebih besar akan dimatikan lebih banyak. Untuk alasan ini, lebih baik untuk menghindari angka floating-point dalam algoritma semacam ini.
sumber
sympy.divisors
tidak jauh lebih cepat, untuk angka dengan beberapa pembagi pada khususnya. Punya tolok ukur?sympy.divisors
100.000 dan lebih lambat untuk apa pun yang lebih tinggi (ketika kecepatan sebenarnya penting). (Dan, tentu saja,sympy.divisors
bekerja pada angka-angka seperti10000000000000079**2
.)Untuk n hingga 10 ** 16 (bahkan mungkin sedikit lebih), berikut ini adalah solusi Python 3.6 murni yang cepat,
sumber
Perbaikan lebih lanjut untuk solusi afg & eryksun. Bagian kode berikut mengembalikan daftar semua faktor yang diurutkan tanpa mengubah kompleksitas asimtotik run time:
Ide: Alih-alih menggunakan fungsi list.sort () untuk mendapatkan daftar yang diurutkan yang memberikan kompleksitas nlog (n); Jauh lebih cepat menggunakan list.reverse () pada l2 yang membutuhkan O (n) kerumitan. (Begitulah cara python dibuat.) Setelah l2.reverse (), l2 dapat ditambahkan ke l1 untuk mendapatkan daftar faktor yang diurutkan.
Perhatikan, l1 berisi i -s yang meningkat. l2 berisi q -s yang berkurang. Itulah alasan di balik penggunaan ide di atas.
sumber
list.reverse
adalah O (n) bukan O (1), bukan berarti itu mengubah keseluruhan kompleksitas.l1 + l2.reversed()
daripada membalik daftar di tempat.Saya sudah mencoba sebagian besar jawaban indah ini dengan timeit untuk membandingkan efisiensinya versus fungsi sederhana saya, namun saya terus-menerus melihat saya mengungguli yang tercantum di sini. Saya pikir saya akan membagikannya dan melihat apa yang Anda pikirkan.
Seperti yang tertulis, Anda harus mengimpor matematika untuk diuji, tetapi mengganti math.sqrt (n) dengan n **. 5 harus bekerja dengan baik. Saya tidak repot-repot menghabiskan waktu untuk memeriksa duplikat karena duplikat tidak dapat ada dalam satu set terlepas dari.
sumber
xrange(1, int(math.sqrt(n)) + 1)
dievaluasi satu kali.Berikut ini adalah alternatif lain tanpa mengurangi yang berkinerja baik dengan jumlah besar. Ini digunakan
sum
untuk meratakan daftar.sumber
sum
ataureduce(list.__add__)
untuk meratakan daftar.Pastikan untuk mengambil nomor yang lebih besar daripada
sqrt(number_to_factor)
angka yang tidak biasa seperti 99 yang memiliki 3 * 3 * 11 danfloor sqrt(99)+1 == 10
.sumber
x=8
diharapkan[1, 2, 4, 8]
[2, 2, 2]
Cara paling sederhana untuk menemukan faktor-faktor nomor:
sumber
Berikut adalah contoh jika Anda ingin menggunakan nomor primes untuk lebih cepat. Daftar ini mudah ditemukan di internet. Saya menambahkan komentar dalam kode.
sumber
algoritma yang berpotensi lebih efisien daripada yang disajikan di sini sudah (terutama jika ada faktor prima kecil di
n
). Kuncinya di sini adalah untuk menyesuaikan batas hingga divisi percobaan diperlukan setiap kali faktor prima ditemukan:ini tentu saja masih pembagian sidang dan tidak ada yang lebih mewah. dan karena itu masih sangat terbatas dalam efisiensinya (terutama untuk jumlah besar tanpa pembagi kecil).
ini adalah python3; pembagian
//
harus menjadi satu-satunya hal yang Anda butuhkan untuk beradaptasi dengan python 2 (tambahkanfrom __future__ import division
).sumber
Menggunakan
set(...)
membuat kode sedikit lebih lambat, dan hanya benar-benar diperlukan ketika Anda memeriksa akar kuadrat. Ini versi saya:The
if sq*sq != num:
Kondisi ini diperlukan untuk nomor seperti 12, di mana akar kuadrat bukan bilangan bulat, tapi lantai akar kuadrat adalah faktor.Perhatikan bahwa versi ini tidak mengembalikan nomor itu sendiri, tetapi itu adalah perbaikan yang mudah jika Anda menginginkannya. Outputnya juga tidak diurutkan.
Saya menghitung waktu menjalankan 10000 kali pada semua angka 1-200 dan 100 kali pada semua angka 1-5000. Ini mengungguli semua versi lain yang saya uji, termasuk solusi dansalmo, Jason Schorn, oxrock, agf, steveha, dan eryksun, meskipun oxrock sejauh ini yang paling dekat.
sumber
faktor maks Anda tidak lebih dari jumlah Anda, jadi, katakanlah
voila!
sumber
sumber
Gunakan sesuatu yang sederhana seperti pemahaman daftar berikut, dengan catatan bahwa kita tidak perlu menguji 1 dan angka yang kita coba temukan:
Mengacu pada penggunaan akar kuadrat, katakanlah kita ingin menemukan faktor 10. Bagian bilangan bulat dari
sqrt(10) = 4
iturange(1, int(sqrt(10))) = [1, 2, 3, 4]
dan menguji hingga 4 dengan jelas melewatkan 5.Kecuali saya kehilangan sesuatu yang saya sarankan, jika Anda harus melakukannya dengan cara ini, gunakan
int(ceil(sqrt(x)))
. Tentu saja ini menghasilkan banyak panggilan ke fungsi yang tidak perlu.sumber
Saya pikir untuk keterbacaan dan solusi kecepatan @ oxrock adalah yang terbaik, jadi di sini adalah kode yang ditulis ulang untuk python 3+:
sumber
Saya cukup terkejut ketika saya melihat pertanyaan ini bahwa tidak ada yang menggunakan numpy bahkan ketika numpy jauh lebih cepat daripada loop python. Dengan mengimplementasikan solusi @ agf dengan numpy dan ternyata rata-rata 8x lebih cepat . Saya percaya bahwa jika Anda menerapkan beberapa solusi lain di numpy Anda bisa mendapatkan waktu yang luar biasa.
Inilah fungsi saya:
Perhatikan bahwa angka-angka dari sumbu x bukan input ke fungsi. Input ke fungsi adalah 2 ke angka pada sumbu x minus 1. Jadi di mana sepuluh adalah input akan menjadi 2 ** 10-1 = 1023
sumber
sumber
Saya rasa ini adalah cara paling sederhana untuk melakukan itu:
sumber