Cách hiệu quả nhất để tìm tất cả các yếu tố của một số trong Python là gì?


142

Ai đó có thể giải thích cho tôi một cách hiệu quả để tìm tất cả các yếu tố của một số trong Python (2.7) không?

Tôi có thể tạo một thuật toán để làm điều này, nhưng tôi nghĩ rằng nó được mã hóa kém và mất quá nhiều thời gian để tạo ra kết quả cho một số lượng lớn.


3
Tôi không biết trăn. Nhưng trang này có thể hữu ích cho bạn en.wikipedia.org/wiki/Integer_factorization
Stan

3
Làm thế nào về việc sử dụng primefac? pypi.python.org/pypi/primefac
Zubo

Câu trả lời:


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)))

Điều này sẽ trả về tất cả các yếu tố, rất nhanh chóng, của một số n.

Tại sao căn bậc hai là giới hạn trên?

sqrt(x) * sqrt(x) = x. Vì vậy, nếu hai yếu tố giống nhau, chúng đều là căn bậc hai. Nếu bạn làm cho một yếu tố lớn hơn, bạn phải làm cho yếu tố kia nhỏ hơn. Điều này có nghĩa là một trong hai sẽ luôn nhỏ hơn hoặc bằng sqrt(x), vì vậy bạn chỉ phải tìm kiếm đến thời điểm đó để tìm một trong hai yếu tố phù hợp. Sau đó bạn có thể sử dụng x / fac1để có được fac2.

Việc reduce(list.__add__, ...)lấy các danh sách nhỏ [fac1, fac2]và kết hợp chúng lại với nhau trong một danh sách dài.

Giá trị [i, n/i] for i in range(1, int(sqrt(n)) + 1) if n % i == 0trả về một cặp nhân tố nếu phần còn lại khi bạn chia ncho phần tử nhỏ hơn bằng 0 (cũng không cần kiểm tra phần tử lớn hơn; nó chỉ có được điều đó bằng cách chia ncho phần tử nhỏ hơn.)

Bên set(...)ngoài đang loại bỏ các bản sao, điều này chỉ xảy ra đối với các ô vuông hoàn hảo. Đối với n = 4, điều này sẽ trở lại 2hai lần, vì vậy hãy setloại bỏ một trong số họ.


1
Tôi đã sao chép nó từ một danh sách các thuật toán trên máy tính của mình, tất cả những gì tôi đã làm là gói gọn sqrt- có lẽ từ trước khi mọi người thực sự nghĩ về việc hỗ trợ Python 3. Tôi nghĩ rằng trang web tôi đã dùng thử __iadd__và nó nhanh hơn . Tôi dường như nhớ một cái gì đó về x**0.5việc nhanh hơn sqrt(x)ở một số điểm mặc dù - và nó là cách dễ dàng hơn theo cách đó.
agf

7
Có vẻ để thực hiện nhanh hơn 15% nếu tôi sử dụng if not n % ithay vìif n % i == 0
dansalmo

3
@sthzg Chúng tôi muốn nó trả về một số nguyên, không phải là số float và trên Python 3 /sẽ trả về một số float ngay cả khi cả hai đối số đều là số nguyên và chúng có thể chia được chính xác, tức là 4 / 2 == 2.0không 2.
agf

7
Tôi biết đây là một câu hỏi cũ, nhưng trong Python 3.x bạn cần thêm from functools import reduceđể thực hiện công việc này.
nặc danh

5
@unseen_rider: Điều đó không đúng. Bạn có thể cung cấp bất cứ điều gì để sao lưu nó?
Ry-

55

Giải pháp được trình bày bởi @agf là tuyệt vời, nhưng người ta có thể đạt được thời gian chạy nhanh hơn ~ 50% cho một số lẻ tùy ý bằng cách kiểm tra tính chẵn lẻ. Vì các yếu tố của một số lẻ luôn luôn là số lẻ, nên không cần thiết phải kiểm tra các số này khi xử lý các số lẻ.

Tôi vừa mới bắt đầu giải câu đố Project Euler . Trong một số vấn đề, kiểm tra ước số được gọi bên trong hai forvòng lặp lồng nhau và do đó hiệu suất của chức năng này là rất cần thiết.

Kết hợp thực tế này với giải pháp tuyệt vời của agf, tôi đã kết thúc với chức năng này:

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)))

Tuy nhiên, trên các số nhỏ (~ <100), chi phí phụ từ sự thay đổi này có thể khiến chức năng mất nhiều thời gian hơn.

Tôi đã chạy một số bài kiểm tra để kiểm tra tốc độ. Dưới đây là mã được sử dụng. Để sản xuất các lô khác nhau, tôi đã thay đổi cho X = range(1,100,1)phù hợp.

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 = phạm vi (1.100,1) X = phạm vi (1.100,1)

Không có sự khác biệt đáng kể ở đây, nhưng với số lượng lớn hơn, lợi thế là rõ ràng:

X = phạm vi (1.100000,1000) (chỉ các số lẻ) X = phạm vi (1.100000,1000) (chỉ các số lẻ)

X = phạm vi (2.100000.100) (chỉ các số chẵn) X = phạm vi (2.100000.100) (chỉ các số chẵn)

X = phạm vi (1.100000.191) (chẵn lẻ xen kẽ) X = phạm vi (1.100000.191) (chẵn lẻ xen kẽ)


28

Câu trả lời của agf thực sự khá tuyệt. Tôi muốn xem liệu tôi có thể viết lại để tránh sử dụng reduce(). Đây là những gì tôi nghĩ ra:

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))

Tôi cũng đã thử một phiên bản sử dụng các chức năng tạo phức tạp:

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)

Tôi đã tính thời gian cho nó bằng máy tính:

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

Tôi đã chạy nó một lần để cho Python biên dịch nó, sau đó chạy nó theo lệnh thời gian (1) ba lần và giữ thời gian tốt nhất.

  • giảm phiên bản: 11,58 giây
  • phiên bản itertools: 11,49 giây
  • phiên bản khó: 11,12 giây

Lưu ý rằng phiên bản itertools đang xây dựng một tuple và chuyển nó tới flatten_iter (). Nếu tôi thay đổi mã để xây dựng một danh sách thay vào đó, nó sẽ chậm lại một chút:

  • phiên bản iterools (danh sách): 11,62 giây

Tôi tin rằng phiên bản hàm tạo phức tạp là nhanh nhất có thể trong Python. Nhưng nó không thực sự nhanh hơn nhiều so với phiên bản rút gọn, nhanh hơn khoảng 4% dựa trên số đo của tôi.


2
bạn có thể đơn giản hóa "phiên bản phức tạp" (loại bỏ không cần thiết 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

Một cách tiếp cận khác cho câu trả lời của 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

1
Bạn có thể giải thích phần div, mod không?
Ad Nam

3
divmod (x, y) trả về ((xx% y) / y, x% y), nghĩa là thương và phần còn lại của phép chia.
c4757p

Điều này không xử lý tốt các yếu tố trùng lặp - ví dụ thử 81.
phkahler

Câu trả lời của bạn rõ ràng hơn, vì vậy tôi đã có thể mò mẫm nó chỉ đủ để hiểu lầm. Tôi đã suy nghĩ về yếu tố chính trong đó bạn muốn gọi ra nhiều số 3. Điều này sẽ ổn, vì đó là những gì OP yêu cầu.
phkahler

Tôi xếp mọi thứ thành một dòng vì câu trả lời của agf đã làm như vậy. Tôi đã quan tâm xem liệu reduce()có nhanh hơn đáng kể hay không, vì vậy tôi đã làm mọi thứ khác ngoài reduce()phần giống như cách mà agf đã làm. Để dễ đọc, thật tuyệt khi thấy một lệnh gọi giống như is_even(n)thay vì một biểu thức như thế nào n % 2 == 0.
steveha

9

Đây là một giải pháp thay thế cho giải pháp của @ agf, thực hiện cùng một thuật toán theo kiểu pythonic hơn:

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)
    )

Giải pháp này hoạt động trong cả Python 2 và Python 3 mà không cần nhập và dễ đọc hơn nhiều. Tôi đã không kiểm tra hiệu suất của phương pháp này, nhưng về mặt triệu chứng thì nó sẽ giống nhau và nếu hiệu suất là một mối quan tâm nghiêm trọng, thì không có giải pháp nào là tối ưu.


7

Có một thuật toán ngành công nghiệp-sức mạnh trong SymPy gọi 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}

Điều này mất dưới một phút. Nó chuyển đổi giữa một loại cocktail phương pháp. Xem các tài liệu liên kết ở trên.

Với tất cả các yếu tố chính, tất cả các yếu tố khác có thể được xây dựng dễ dàng.


Lưu ý rằng ngay cả khi câu trả lời được chấp nhận được phép chạy đủ lâu (tức là vĩnh cửu) để tính số trên, đối với một số số lớn, nó sẽ thất bại, ví dụ như sau. Điều này là do sự cẩu thả int(n**0.5). Ví dụ, khi nào n = 10000000000000079**2, chúng ta có

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

10000000000000079 là số nguyên tố , thuật toán của câu trả lời được chấp nhận sẽ không bao giờ tìm thấy yếu tố này. Lưu ý rằng nó không chỉ là một trong một; đối với số lượng lớn hơn, nó sẽ được giảm thêm. Vì lý do này, tốt hơn là tránh các số có dấu phẩy động trong các thuật toán loại này.


2
Nó không tìm thấy tất cả các ước số mà chỉ là các thừa số nguyên tố nên nó không thực sự là một câu trả lời. Bạn nên chỉ ra cách tất cả các yếu tố khác có thể được xây dựng, không chỉ nói rằng nó dễ dàng! Nhân tiện, sympy.divisors có thể là một kết hợp tốt hơn để trả lời câu hỏi này.
Colin Pitrat

Và lưu ý rằng sympy.divisors không nhanh hơn nhiều so với giải pháp được chấp nhận.
Colin Pitrat

@ColinPitrat: Tôi nghi ngờ rằng điều đó sympy.divisorskhông nhanh hơn nhiều, đặc biệt là với các số có ít ước số. Có điểm chuẩn nào không?
Ry-

@Ry Tôi đã làm một khi tôi viết bình luận này một năm trước. Phải mất 2 phút để viết một cái vì vậy hãy kiểm tra lại.
Colin Pitrat

3
@ColinPitrat: Đã kiểm tra. Như mong đợi, câu trả lời được chấp nhận là có cùng tốc độ sympy.divisorsvới 100.000 và chậm hơn đối với mọi thứ cao hơn (khi tốc độ thực sự quan trọng). (Và, tất nhiên, sympy.divisorshoạt động trên các số như 10000000000000079**2.)
Ry-

7

Đối với n tối đa 10 ** 16 (thậm chí nhiều hơn một chút), đây là một giải pháp Python 3.6 thuần túy nhanh,

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))

6

Cải thiện hơn nữa cho giải pháp của afg & eryksun. Đoạn mã sau đây trả về một danh sách được sắp xếp của tất cả các yếu tố mà không thay đổi độ phức tạp tiệm cận thời gian chạy:

    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

Ý tưởng: Thay vì sử dụng hàm list.sort () để có được một danh sách được sắp xếp mang lại độ phức tạp nlog (n); Nó nhanh hơn nhiều khi sử dụng list.reverse () trên l2, độ phức tạp O (n). (Đó là cách python được tạo ra.) Sau l2.reverse (), l2 có thể được thêm vào l1 để lấy danh sách các yếu tố được sắp xếp.

Lưu ý, l1 chứa i -s đang tăng. l2 chứa q -s đang giảm. Đó là lý do đằng sau việc sử dụng ý tưởng trên.


Khá chắc chắn list.reverselà O (n) không phải O (1), không phải nó thay đổi độ phức tạp tổng thể.
agf

Vâng đúng vậy. Tôi đã phạm một sai lầm. Nên là O (n). (Tôi đã cập nhật câu trả lời ngay bây giờ cho đúng)
Pranjal Găngal

Nó chậm hơn khoảng 2 lần so với các giải pháp của @ steveha hoặc @ agf.
jfs

Bạn có thể nhận một cải thiện tốc độ nhỏ (2-3%) bằng cách quay lại l1 + l2.reversed()thay vì đảo ngược danh sách tại chỗ.
Rakurai

6

Tôi đã thử hầu hết các câu trả lời tuyệt vời này với thời gian để so sánh hiệu quả của chúng với chức năng đơn giản của tôi và tôi liên tục thấy tôi vượt trội hơn những câu được liệt kê ở đây. Tôi hình dung tôi sẽ chia sẻ nó và xem tất cả những gì bạn nghĩ.

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

Như đã viết, bạn sẽ phải nhập toán để kiểm tra, nhưng thay thế math.sqrt (n) bằng n **. 5 cũng sẽ hoạt động tốt. Tôi không bận tâm đến việc lãng phí thời gian để kiểm tra các bản sao vì các bản sao không thể tồn tại trong một tập hợp bất kể.


Công cụ tuyệt vời! Nếu bạn đặt int (math.sqrt (n)) + 1 bên ngoài vòng lặp for, bạn sẽ nhận được hiệu suất cao hơn một chút vì sẽ không phải tính lại nó mỗi lần lặp của vòng lặp for
Tristan Forward

3
@TristanForward: Đó không phải là cách các vòng lặp hoạt động trong Python. xrange(1, int(math.sqrt(n)) + 1)được đánh giá một lần.
Ry-

5

Đây là một thay thế khác mà không giảm mà thực hiện tốt với số lượng lớn. Nó sử dụng sumđể làm phẳng danh sách.

def factors(n):
    return set(sum([[i, n//i] for i in xrange(1, int(n**0.5)+1) if not n%i], []))

1
Điều này không, đó là thời gian bậc hai không cần thiết. Đừng sử dụng sumhoặc reduce(list.__add__)làm phẳng một danh sách.
juanpa.arrivillaga

4

Đảm bảo lấy số lớn hơn sqrt(number_to_factor)số bất thường như 99 có 3 * 3 * 11 và 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

1
Nó không tạo ra tất cả các yếu tố của một số. Nó tính các thừa số nguyên tố của một số, ví dụ x=8: dự kiến ​​: [1, 2, 4, 8], got:[2, 2, 2]
jfs

11 được tìm thấy khi 9 được comup trong mã được đưa ra bởi @agf. `i = 9 -> 99% 9 == 0 -> 9 và 99/9 = 11 được thêm vào.
Steinar Lima

4

Cách đơn giản nhất để tìm các yếu tố của một số:

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

2

Dưới đây là một ví dụ nếu bạn muốn sử dụng số nguyên tố để đi nhanh hơn rất nhiều. Những danh sách này rất dễ tìm thấy trên internet. Tôi đã thêm ý kiến ​​trong mã.

# 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)]

Tôi đã tạo một dự án trên Github: github.com/Pierre-Thibault/Factor .
Pierre Thibault

2

một thuật toán có khả năng hiệu quả cao hơn các thuật toán được trình bày ở đây (đặc biệt là nếu có các số nguyên tố nhỏ trong n). mẹo ở đây là điều chỉnh giới hạn mà phân chia thử nghiệm là cần thiết mỗi khi tìm thấy các yếu tố chính:

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

Tất nhiên đây vẫn là bộ phận thử nghiệm và không có gì lạ mắt hơn. và do đó vẫn còn rất hạn chế về hiệu quả của nó (đặc biệt đối với số lượng lớn không có ước số nhỏ).

đây là python3; sự phân chia //nên là điều duy nhất bạn cần để điều chỉnh cho python 2 (thêm from __future__ import division).


1

Việc sử dụng set(...)làm cho mã chậm hơn một chút và chỉ thực sự cần thiết khi bạn kiểm tra căn bậc hai. Đây là phiên bản của tôi:

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

Điều if sq*sq != num:kiện cần thiết cho các số như 12, trong đó căn bậc hai không phải là số nguyên, nhưng sàn của căn bậc hai là một yếu tố.

Lưu ý rằng phiên bản này không trả về số đó, nhưng đó là một sửa chữa dễ dàng nếu bạn muốn. Đầu ra cũng không được sắp xếp.

Tôi đã hẹn giờ nó chạy 10000 lần trên tất cả các số 1-200 và 100 lần cho tất cả các số 1-5000. Nó vượt trội hơn tất cả các phiên bản khác mà tôi đã thử nghiệm, bao gồm các giải pháp của dansalmo, Jason Schorn, oxrock, agf's, steveha và eryksun, mặc dù cho đến nay là gần nhất.


1

hệ số tối đa của bạn không nhiều hơn số của bạn, vì vậy, giả sử

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

    return factors

voilá!


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. 

0

Sử dụng một cái gì đó đơn giản như cách hiểu danh sách sau đây, lưu ý rằng chúng tôi không cần kiểm tra 1 và số chúng tôi đang cố gắng tìm:

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

Liên quan đến việc sử dụng căn bậc hai, giả sử chúng tôi muốn tìm các yếu tố của 10. Phần nguyên của sqrt(10) = 4do đó range(1, int(sqrt(10))) = [1, 2, 3, 4]và kiểm tra tối đa 4 sai rõ ràng 5.

Trừ khi tôi thiếu một cái gì đó tôi sẽ đề nghị, nếu bạn phải làm theo cách này, sử dụng int(ceil(sqrt(x))). Tất nhiên điều này tạo ra rất nhiều cuộc gọi không cần thiết cho các chức năng.


Vấn đề với giải pháp này là nó kiểm tra nhiều số có thể là các yếu tố - và nó kiểm tra mức cao hơn của từng cặp yếu tố khi bạn đã biết đó là một yếu tố sau khi tìm ra cặp nhân tố nhỏ hơn.
agf

1
@JasonSchorn: Khi bạn tìm thấy 2, bạn biết ngay rằng 10/2 = 5 cũng là một ước số, không cần phải kiểm tra 5 riêng biệt! :)
Moberg

0

Tôi nghĩ rằng khả năng đọc và tốc độ của giải pháp @ oxrock là tốt nhất, vì vậy đây là mã được viết lại cho 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

0

Tôi đã khá ngạc nhiên khi thấy câu hỏi này rằng không ai sử dụng numpy ngay cả khi numpy nhanh hơn các vòng python. Bằng cách triển khai giải pháp của @ agf với numpy và nó bật ra trung bình nhanh hơn gấp 8 lần . Tôi tin rằng nếu bạn thực hiện một số giải pháp khác trong numpy, bạn có thể có được thời gian tuyệt vời.

Đây là chức năng của tôi:

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))   

Lưu ý rằng các số của trục x không phải là đầu vào cho các hàm. Đầu vào của các hàm là 2 đến số trên trục x trừ đi 1. Vì vậy, mười là đầu vào sẽ là 2 ** 10-1 = 1023

Kết quả kiểm tra hiệu suất của việc sử dụng numpy thay vì cho các vòng lặp.


1
Nếu bạn sẽ sử dụng một thư viện, cũng có thể biến nó thành một thư viện đúng: SymPy, như đã thấy trong câu trả lời của 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;
}

Hầu như tất cả các thuật toán ở đây giới hạn phạm vi cho số * .5, nhưng thực sự phạm vi đó nhỏ hơn nhiều. sqrt thực sự của nó của số. nếu chúng ta có ước số thấp hơn, chúng ta có thể đưa anh ta lên trên một cách dễ dàng. vì nó chỉ là số / ước. trong 16 tôi nhận được 4 cho sqrt, sau đó lặp từ 1 đến 4. vì 2 là ước số giới hạn dưới của 16, chúng tôi lấy 16/2 để nhận 8. nếu chúng ta có 1 thì lấy 16 là (16/1). Tôi đã nghĩ ra điều này trong khi tìm hiểu về nhân tố chính vì vậy tôi không biết nó có được xuất bản ở nơi nào khác không, nhưng nó hoạt động ngay cả với số lượng lớn. Tôi có thể cung cấp một giải pháp python.
Tangang Atanga

-4

Tôi nghĩ đây là cách đơn giản nhất để làm điều đó:

    x = 23

    i = 1
    while i <= x:
      if x % i == 0:
        print("factor: %s"% i)
      i += 1

Câu trả lời của bạn, trong khi đưa ra kết quả đúng, rất không hiệu quả. Có một cái nhìn vào câu trả lời được chấp nhận. Một lời giải thích về cách giải quyết vấn đề luôn giúp câu trả lời hữu ích hơn.
Nick
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.