Apa cara paling efisien untuk menemukan semua faktor angka dalam Python?

142

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.

Adnan
sumber
3
Saya tidak tahu python. Tetapi halaman ini mungkin berguna bagi Anda en.wikipedia.org/wiki/Integer_factorization
Stan
3
Bagaimana kalau menggunakan primefac? pypi.python.org/pypi/primefac
Zubo

Jawaban:

265
from functools import reduce

def factors(n):    
    return set(reduce(list.__add__, 
                ([i, n//i] for i in range(1, int(n**0.5) + 1) if n % i == 0)))

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 dengan sqrt(x), jadi Anda hanya perlu mencari sampai titik itu untuk menemukan salah satu dari dua faktor yang cocok. Anda dapat menggunakannya x / fac1untuk mendapatkan fac2.

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 == 0pengembalian sepasang faktor jika sisa ketika Anda membagi ndengan yang lebih kecil adalah nol (tidak perlu memeriksa lebih besar juga; itu hanya akan bahwa dengan membagi ndengan yang lebih kecil.)

Di set(...)luar menghilangkan duplikat, yang hanya terjadi untuk kotak yang sempurna. Karena n = 4, ini akan kembali 2dua kali, jadi setsingkirkan salah satunya.

agf
sumber
1
Saya menyalin-menempelkan ini dari daftar algoritma di komputer saya, semua yang saya lakukan adalah merangkum 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 tentang x**0.5menjadi lebih cepat daripada sqrt(x)pada titik tertentu - dan itu lebih mudah dari itu.
agf
7
Tampaknya melakukan 15% lebih cepat jika saya menggunakan if not n % ibukannyaif n % i == 0
dansalmo
3
@sthzg Kami ingin mengembalikan integer, bukan float, dan pada Python 3 /akan mengembalikan float bahkan jika kedua argumen adalah integer dan keduanya dapat dibagi, yaitu 4 / 2 == 2.0tidak 2.
agf
7
Saya tahu ini adalah pertanyaan lama, tetapi dengan Python 3.x Anda perlu menambahkan from functools import reduceuntuk membuatnya berfungsi.
anonim memilih
5
@unseen_rider: Itu kedengarannya tidak benar. Bisakah Anda memberikan sesuatu untuk mendukungnya?
Ry-
55

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 forloop bersarang , dan kinerja fungsi ini sangat penting.

Menggabungkan fakta ini dengan solusi agf yang luar biasa, saya berakhir dengan fungsi ini:

from math import sqrt
def factors(n):
        step = 2 if n%2 else 1
        return set(reduce(list.__add__,
                    ([i, n//i] for i in range(1, int(sqrt(n))+1, step) if n % i == 0)))

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.

import timeit
from math import sqrt
from matplotlib.pyplot import plot, legend, show

def factors_1(n):
    step = 2 if n%2 else 1
    return set(reduce(list.__add__,
                ([i, n//i] for i in range(1, int(sqrt(n))+1, step) if n % i == 0)))

def factors_2(n):
    return set(reduce(list.__add__,
                ([i, n//i] for i in range(1, int(sqrt(n)) + 1) if n % i == 0)))

X = range(1,100000,1000)
Y = []
for i in X:
    f_1 = timeit.timeit('factors_1({})'.format(i), setup='from __main__ import factors_1', number=10000)
    f_2 = timeit.timeit('factors_2({})'.format(i), setup='from __main__ import factors_2', number=10000)
    Y.append(f_1/f_2)
plot(X,Y, label='Running time with/without parity check')
legend()
show()

X = rentang (1.100,1) 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 (1.100.000.1000) (hanya angka ganjil)

X = rentang (2.100000.100) (hanya angka genap) X = rentang (2.100000.100) (hanya angka genap)

X = rentang (1,100000,1001) (paritas bolak-balik) X = rentang (1,100000,1001) (paritas bolak-balik)

Steinar Lima
sumber
28

jawaban agf sangat keren. Saya ingin melihat apakah saya bisa menulis ulang untuk menghindari penggunaan reduce(). Inilah yang saya pikirkan:

import itertools
flatten_iter = itertools.chain.from_iterable
def factors(n):
    return set(flatten_iter((i, n//i) 
                for i in range(1, int(n**0.5)+1) if n % i == 0))

Saya juga mencoba versi yang menggunakan fungsi generator rumit:

def factors(n):
    return set(x for tup in ([i, n//i] 
                for i in range(1, int(n**0.5)+1) if n % i == 0) for x in tup)

Saya menghitungnya dengan menghitung:

start = 10000000
end = start + 40000
for n in range(start, end):
    factors(n)

Saya menjalankannya sekali untuk membiarkan Python mengkompilasinya, kemudian menjalankannya di bawah perintah waktu (1) tiga kali dan menjaga waktu terbaik.

  • kurangi versi: 11,58 detik
  • Versi itertools: 11,49 detik
  • versi rumit: 11,12 detik

Perhatikan bahwa versi itertools sedang membangun tuple dan meneruskannya ke flatten_iter (). Jika saya mengubah kode untuk membuat daftar, itu sedikit melambat:

  • versi iterools (daftar): 11,62 detik

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.

steveha
sumber
2
Anda dapat menyederhanakan "versi rumit" (hapus yang tidak perlu 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]}
jfs
11

Pendekatan alternatif untuk jawaban AGF:

def factors(n):    
    result = set()
    for i in range(1, int(n ** 0.5) + 1):
        div, mod = divmod(n, i)
        if mod == 0:
            result |= {i, div}
    return result
Eryk Sun
sumber
1
Bisakah Anda menjelaskan bagian div, mod?
Adnan
3
divmod (x, y) mengembalikan ((xx% y) / y, x% y), yaitu hasil bagi dan sisa divisi.
c4757p
Ini tidak menangani faktor duplikat dengan baik - coba misalnya 81.
phkahler
Jawaban Anda lebih jelas, jadi saya bisa mengatakannya cukup untuk salah paham. Saya sedang memikirkan faktorisasi utama di mana Anda ingin memanggil beberapa 3. Ini harus baik-baik saja, karena itulah yang diminta OP.
phkahler
Saya menumpuk semuanya menjadi satu baris karena jawaban agf melakukannya. Saya tertarik untuk melihat apakah reduce()secara signifikan lebih cepat, jadi saya cukup banyak melakukan segala sesuatu selain reduce()bagian seperti yang dilakukan agf. Untuk keterbacaan, alangkah baiknya untuk melihat fungsi panggilan seperti is_even(n)daripada ekspresi seperti n % 2 == 0.
steveha
9

Inilah alternatif untuk solusi @ agf yang mengimplementasikan algoritma yang sama dengan gaya yang lebih pythonic:

def factors(n):
    return set(
        factor for i in range(1, int(n**0.5) + 1) if n % i == 0
        for factor in (i, n//i)
    )

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.

Julian
sumber
7

Ada algoritma industri-kekuatan di SymPy disebut factorint :

>>> from sympy import factorint
>>> factorint(2**70 + 3**80) 
{5: 2,
 41: 1,
 101: 1,
 181: 1,
 821: 1,
 1597: 1,
 5393: 1,
 27188665321L: 1,
 41030818561L: 1}

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, kapan n = 10000000000000079**2, sudah

>>> int(n**0.5)
10000000000000078L

Karena 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.

Evgeni Sergeev
sumber
2
Itu tidak menemukan semua pembagi tetapi hanya faktor utama sehingga itu bukan jawaban. Anda harus menunjukkan bagaimana semua faktor lain dapat dibangun, bukan hanya mengatakan itu mudah! Omong-omong, sympy.divisors mungkin lebih cocok untuk menjawab pertanyaan ini.
Colin Pitrat
Dan perhatikan bahwa sympy.divisors tidak jauh lebih cepat daripada solusi yang diterima.
Colin Pitrat
@ColinPitrat: Saya agak ragu itu sympy.divisorstidak jauh lebih cepat, untuk angka dengan beberapa pembagi pada khususnya. Punya tolok ukur?
Ry-
@Ry Saya melakukan satu ketika saya menulis komentar ini setahun yang lalu. Diperlukan 2 menit untuk menulis satu sehingga merasa bebas untuk memeriksa ulang.
Colin Pitrat
3
@ColinPitrat: Diperiksa. Seperti yang diharapkan, jawaban yang diterima adalah tentang kecepatan yang sama dengan sympy.divisors100.000 dan lebih lambat untuk apa pun yang lebih tinggi (ketika kecepatan sebenarnya penting). (Dan, tentu saja, sympy.divisorsbekerja pada angka-angka seperti 10000000000000079**2.)
Ry-
7

Untuk n hingga 10 ** 16 (bahkan mungkin sedikit lebih), berikut ini adalah solusi Python 3.6 murni yang cepat,

from itertools import compress

def primes(n):
    """ Returns  a list of primes < n for n > 2 """
    sieve = bytearray([True]) * (n//2)
    for i in range(3,int(n**0.5)+1,2):
        if sieve[i//2]:
            sieve[i*i//2::i] = bytearray((n-i*i-1)//(2*i)+1)
    return [2,*compress(range(3,n,2), sieve[1:])]

def factorization(n):
    """ Returns a list of the prime factorization of n """
    pf = []
    for p in primeslist:
      if p*p > n : break
      count = 0
      while not n % p:
        n //= p
        count += 1
      if count > 0: pf.append((p, count))
    if n > 1: pf.append((n, 1))
    return pf

def divisors(n):
    """ Returns an unsorted list of the divisors of n """
    divs = [1]
    for p, e in factorization(n):
        divs += [x*p**k for k in range(1,e+1) for x in divs]
    return divs

n = 600851475143
primeslist = primes(int(n**0.5)+1) 
print(divisors(n))
Bruno Astrolino
sumber
6

Perbaikan lebih lanjut untuk solusi afg & eryksun. Bagian kode berikut mengembalikan daftar semua faktor yang diurutkan tanpa mengubah kompleksitas asimtotik run time:

    def factors(n):    
        l1, l2 = [], []
        for i in range(1, int(n ** 0.5) + 1):
            q,r = n//i, n%i     # Alter: divmod() fn can be used.
            if r == 0:
                l1.append(i) 
                l2.append(q)    # q's obtained are decreasing.
        if l1[-1] == l2[-1]:    # To avoid duplication of the possible factor sqrt(n)
            l1.pop()
        l2.reverse()
        return l1 + l2

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.

Pranjal Mittal
sumber
Cukup yakin list.reverseadalah O (n) bukan O (1), bukan berarti itu mengubah keseluruhan kompleksitas.
agf
Ya itu betul. Saya membuat kesalahan. Seharusnya O (n). (Saya telah memperbarui jawabannya sekarang ke jawaban yang benar)
Pranjal Mittal
Ini sekitar 2 kali lebih lambat daripada solusi @ steveha atau @ agf.
jfs
Anda dapat mengambil peningkatan kecepatan kecil (2-3%) dengan mengembalikan l1 + l2.reversed()daripada membalik daftar di tempat.
Rakurai
6

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.

def factors(n):
    results = set()
    for i in xrange(1, int(math.sqrt(n)) + 1):
        if n % i == 0:
            results.add(i)
            results.add(int(n/i))
    return results

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.

oxrock
sumber
Barang bagus! Jika Anda meletakkan int (math.sqrt (n)) +1 di luar for for Anda harus mendapatkan kinerja yang lebih banyak karena tidak perlu menghitung ulang setiap iterasi dari for for
Tristan Forward
3
@ ChristristForward: Bukan itu cara kerja loop dengan Python. xrange(1, int(math.sqrt(n)) + 1)dievaluasi satu kali.
Ry-
5

Berikut ini adalah alternatif lain tanpa mengurangi yang berkinerja baik dengan jumlah besar. Ini digunakan sumuntuk meratakan daftar.

def factors(n):
    return set(sum([[i, n//i] for i in xrange(1, int(n**0.5)+1) if not n%i], []))
dansalmo
sumber
1
Ini tidak, ini adalah waktu kuadratik yang tidak perlu. Jangan gunakan sumatau reduce(list.__add__)untuk meratakan daftar.
juanpa.arrivillaga
4

Pastikan untuk mengambil nomor yang lebih besar daripada sqrt(number_to_factor)angka yang tidak biasa seperti 99 yang memiliki 3 * 3 * 11 dan floor sqrt(99)+1 == 10.

import math

def factor(x):
  if x == 0 or x == 1:
    return None
  res = []
  for i in range(2,int(math.floor(math.sqrt(x)+1))):
    while x % i == 0:
      x /= i
      res.append(i)
  if x != 1: # Unusual numbers
    res.append(x)
  return res
mbowden
sumber
1
Itu tidak menghasilkan semua faktor angka. Ini menghitung faktor prima dari angka misalnya, untuk yang x=8diharapkan[1, 2, 4, 8][2, 2, 2]
:,
11 ditemukan ketika 9 comupted dalam kode yang diberikan oleh @agf. `i = 9 -> 99% 9 == 0 -> 9 dan 99/9 = 11 ditambahkan.
Steinar Lima
4

Cara paling sederhana untuk menemukan faktor-faktor nomor:

def factors(x):
    return [i for i in range(1,x+1) if x%i==0]
GooDeeJaY
sumber
2

Berikut adalah contoh jika Anda ingin menggunakan nomor primes untuk lebih cepat. Daftar ini mudah ditemukan di internet. Saya menambahkan komentar dalam kode.

# http://primes.utm.edu/lists/small/10000.txt
# First 10000 primes

_PRIMES = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 
        31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 
        73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 
        127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 
        179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 
        233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 
        283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 
        353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 
        419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 
        467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 
        547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 
        607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 
        661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 
        739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 
        811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 
        877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 
        947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 
# Mising a lot of primes for the purpose of the example
)


from bisect import bisect_left as _bisect_left
from math import sqrt as _sqrt


def get_factors(n):
    assert isinstance(n, int), "n must be an integer."
    assert n > 0, "n must be greather than zero."
    limit = pow(_PRIMES[-1], 2)
    assert n <= limit, "n is greather then the limit of {0}".format(limit)
    result = set((1, n))
    root = int(_sqrt(n))
    primes = [t for t in get_primes_smaller_than(root + 1) if not n % t]
    result.update(primes)  # Add all the primes factors less or equal to root square
    for t in primes:
        result.update(get_factors(n/t))  # Add all the factors associted for the primes by using the same process
    return sorted(result)


def get_primes_smaller_than(n):
    return _PRIMES[:_bisect_left(_PRIMES, n)]
Pierre Thibault
sumber
Saya membuat proyek di Github: github.com/Pierre-Thibault/Factor .
Pierre Thibault
2

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:

def factors(n):
    '''
    return prime factors and multiplicity of n
    n = p0^e0 * p1^e1 * ... * pk^ek encoded as
    res = [(p0, e0), (p1, e1), ..., (pk, ek)]
    '''

    res = []

    # get rid of all the factors of 2 using bit shifts
    mult = 0
    while not n & 1:
        mult += 1
        n >>= 1
    if mult != 0:
        res.append((2, mult))

    limit = round(sqrt(n))
    test_prime = 3
    while test_prime <= limit:
        mult = 0
        while n % test_prime == 0:
            mult += 1
            n //= test_prime
        if mult != 0:
            res.append((test_prime, mult))
            if n == 1:              # only useful if ek >= 3 (ek: multiplicity
                break               # of the last prime) 
            limit = round(sqrt(n))  # adjust the limit
        test_prime += 2             # will often not be prime...
    if n != 1:
        res.append((n, 1))
    return res

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 (tambahkan from __future__ import division).

hiro protagonis
sumber
1

Menggunakan set(...)membuat kode sedikit lebih lambat, dan hanya benar-benar diperlukan ketika Anda memeriksa akar kuadrat. Ini versi saya:

def factors(num):
    if (num == 1 or num == 0):
        return []
    f = [1]
    sq = int(math.sqrt(num))
    for i in range(2, sq):
        if num % i == 0:
            f.append(i)
            f.append(num/i)
    if sq > 1 and num % sq == 0:
        f.append(sq)
        if sq*sq != num:
            f.append(num/sq)
    return f

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.

HamsterBoo
sumber
1

faktor maks Anda tidak lebih dari jumlah Anda, jadi, katakanlah

def factors(n):
    factors = []
    for i in range(1, n//2+1):
        if n % i == 0:
            factors.append (i)
    factors.append(n)

    return factors

voila!

Polina Novikova
sumber
1
 import math

    '''
    I applied finding prime factorization to solve this. (Trial Division)
    It's not complicated
    '''


    def generate_factors(n):
        lower_bound_check = int(math.sqrt(n))  # determine lowest bound divisor range [16 = 4]
        factors = set()  # store factors
        for divisors in range(1, lower_bound_check + 1):  # loop [1 .. 4]
            if n % divisors == 0:
                factors.add(divisors)  # lower bound divisor is found 16 [ 1, 2, 4]
                factors.add(n // divisors)  # get upper divisor from lower [ 16 / 1 = 16, 16 / 2 = 8, 16 / 4 = 4]
        return factors  # [1, 2, 4, 8 16]


    print(generate_factors(12)) # {1, 2, 3, 4, 6, 12} -> pycharm output

 Pierre Vriens hopefully this makes more sense. this is an O(nlogn) solution. 
Tangang Atanga
sumber
0

Gunakan sesuatu yang sederhana seperti pemahaman daftar berikut, dengan catatan bahwa kita tidak perlu menguji 1 dan angka yang kita coba temukan:

def factors(n):
    return [x for x in range(2, n//2+1) if n%x == 0]

Mengacu pada penggunaan akar kuadrat, katakanlah kita ingin menemukan faktor 10. Bagian bilangan bulat dari sqrt(10) = 4itu range(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.

Jason Schorn
sumber
Masalah dengan solusi ini adalah bahwa ia memeriksa banyak angka yang tidak mungkin menjadi faktor - dan memeriksa lebih tinggi dari masing-masing pasangan faktor secara terpisah ketika Anda sudah tahu itu adalah faktor setelah menemukan yang lebih kecil dari pasangan faktor.
agf
1
@JasonSchorn: Ketika Anda menemukan 2, Anda segera tahu bahwa 10/2 = 5 adalah pembagi juga, tidak perlu memeriksa 5 secara terpisah! :)
Moberg
0

Saya pikir untuk keterbacaan dan solusi kecepatan @ oxrock adalah yang terbaik, jadi di sini adalah kode yang ditulis ulang untuk python 3+:

def num_factors(n):
    results = set()
    for i in range(1, int(n**0.5) + 1):
        if n % i == 0: results.update([i,int(n/i)])
    return results
Nic Scozzaro
sumber
0

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:

import numpy as np
def b(n):
    r = np.arange(1, int(n ** 0.5) + 1)
    x = r[np.mod(n, r) == 0]
    return set(np.concatenate((x, n / x), axis=None))   

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

Hasil tes kinerja menggunakan numpy bukan untuk loop.

Benedikt Vilji Magnússon
sumber
1
Jika Anda akan menggunakan perpustakaan, mungkin buatlah itu yang benar: SymPy, seperti yang terlihat dalam jawaban Evgeni Sergeev.
Ry-
0
import 'dart:math';
generateFactorsOfN(N){
  //determine lowest bound divisor range
  final lowerBoundCheck = sqrt(N).toInt();
  var factors = Set<int>(); //stores factors
  /**
   * Lets take 16:
   * 4 = sqrt(16)
   * start from 1 ...  4 inclusive
   * check mod 16 % 1 == 0?  set[1, (16 / 1)]
   * check mod 16 % 2 == 0?  set[1, (16 / 1) , 2 , (16 / 2)]
   * check mod 16 % 3 == 0?  set[1, (16 / 1) , 2 , (16 / 2)] -> unchanged
   * check mod 16 % 4 == 0?  set[1, (16 / 1) , 2 , (16 / 2), 4, (16 / 4)]
   *
   *  ******************* set is used to remove duplicate
   *  ******************* case 4 and (16 / 4) both equal to 4
   *  return factor set<int>.. this isn't ordered
   */

  for(var divisor = 1; divisor <= lowerBoundCheck; divisor++){
    if(N % divisor == 0){
      factors.add(divisor);
      factors.add(N ~/ divisor); // ~/ integer division 
    }
  }
  return factors;
}
Tangang Atanga
sumber
Hampir semua algoritma di sini membatasi rentang ke angka * .5, tetapi sebenarnya kisaran itu jauh lebih kecil. sebenarnya sqrt dari nomor tersebut. jika kita memiliki pembagi yang lebih rendah, kita bisa mendapatkannya dengan mudah. karena itu hanya angka / pembagi. untuk 16 saya mendapatkan 4 untuk sqrt, kemudian loop dari 1 ke 4. karena 2 adalah pembagi batas bawah dari 16 kita mengambil 16/2 untuk mendapatkan 8. jika kita memiliki 1 maka untuk mendapatkan 16 adalah (16/1). Saya datang ini sambil belajar tentang faktorisasi prima jadi saya tidak tahu apakah itu diterbitkan di tempat lain, tetapi berfungsi bahkan untuk jumlah besar. Saya dapat memberikan solusi python.
Tangang Atanga
-4

Saya rasa ini adalah cara paling sederhana untuk melakukan itu:

    x = 23

    i = 1
    while i <= x:
      if x % i == 0:
        print("factor: %s"% i)
      i += 1
DeDude
sumber
Jawaban Anda, sambil memberikan hasil yang tepat, sangat tidak efisien. Lihatlah jawaban yang diterima. Penjelasan tentang cara memecahkan masalah selalu membantu jawaban menjadi lebih bermanfaat.
Nick