Cách tốt nhất để nhận được tất cả các ước của một số là gì?


105

Đây là cách rất ngu ngốc:

def divisorGenerator(n):
    for i in xrange(1,n/2+1):
        if n%i == 0: yield i
    yield n

Kết quả tôi muốn nhận được tương tự như kết quả này, nhưng tôi muốn một thuật toán thông minh hơn (cái này quá chậm và ngu ngốc :-)

Tôi có thể tìm ra các thừa số nguyên tố và tính bội của chúng đủ nhanh. Tôi đã có một trình tạo ra hệ số theo cách này:

(
yếu tố1, tính đa
hiệu1 ) (yếu tố2, tính đa hiệu2 ) (yếu tố3, tính đa dạng3)
, v.v.

tức là đầu ra của

for i in factorGenerator(100):
    print i

Là:

(2, 2)
(5, 2)

Tôi không biết điều này hữu ích bao nhiêu cho những gì tôi muốn làm (tôi đã viết mã nó cho các vấn đề khác), dù sao thì tôi muốn một cách thông minh hơn để làm

for i in divisorGen(100):
    print i

xuất cái này:

1
2
4
5
10
20
25
50
100

CẬP NHẬT: Rất cảm ơn Greg Hewgill và "cách thông minh" của anh ấy :) Tính toán tất cả các ước số 100000000 mất 0,01s với cách của anh ấy so với 39 mà cách ngu ngốc đã xảy ra trên máy của tôi, rất tuyệt: D

CẬP NHẬT 2: Ngừng nói rằng đây là một bản sao của bài đăng này . Tính số ước của một số đã cho không cần phải tính tất cả các ước. Đó là một vấn đề khác, nếu bạn nghĩ không phải vậy thì hãy tìm "Hàm số chia" trên wikipedia. Đọc câu hỏi và câu trả lời trước khi đăng, nếu bạn không hiểu chủ đề là gì, chỉ cần không thêm câu trả lời không hữu ích và đã có sẵn.


Lý do mà người ta cho rằng câu hỏi này gần như trùng lặp với "Thuật toán tính số ước của một số nhất định" là bước đầu tiên được đề xuất trong câu hỏi đó là tìm tất cả các ước , mà tôi tin là chính xác. bạn đang cố gắng làm gì
Andrew Edgecombe 5/10/08

4
Andrew để tìm có bao nhiêu ước số, bạn chỉ cần tìm các thừa số nguyên tố và sau đó sử dụng chúng để đếm xem có thể có bao nhiêu ước số. Tìm ước số không cần thiết trong trường hợp đó.
Loïc Faure-Lacroix

1
@Andrea Ambu, hãy sửa lại bạn hoạt động tên
khoáng sản

Câu trả lời:


77

Với factorGeneratorchức năng của bạn , đây là một chức năng divisorGensẽ hoạt động:

def divisorGen(n):
    factors = list(factorGenerator(n))
    nfactors = len(factors)
    f = [0] * nfactors
    while True:
        yield reduce(lambda x, y: x*y, [factors[x][0]**f[x] for x in range(nfactors)], 1)
        i = 0
        while True:
            f[i] += 1
            if f[i] <= factors[i][1]:
                break
            f[i] = 0
            i += 1
            if i >= nfactors:
                return

Hiệu quả tổng thể của thuật toán này sẽ phụ thuộc hoàn toàn vào hiệu quả của factorGenerator.


2
wow, phải mất 0,01 để tính toán tất cả các ước số 100000000 so với số 39 một cách ngu ngốc (dừng ở n / 2) rất tuyệt, cảm ơn bạn!
Andrea Ambu

47
Đối với những người không hiểu Pythonese, điều này thực sự đang làm gì?
Matthew Scharley 7/10/08

1
monoxide: tính toán tất cả các kết hợp nhân của các thừa số đã cho. Hầu hết nó nên được tự giải thích; dòng "sản lượng" giống như trả về nhưng vẫn tiếp tục sau khi trả về một giá trị. [0] * n yếu tố tạo danh sách các số không có độ dài n yếu tố. giảm (...) tính tích của các yếu tố.
Greg Hewgill 07/10/08

Ký hiệu rút gọn và lambda là những phần thực sự khiến tôi bối rối. Tôi đã cố gắng thực hiện một thuật toán để làm điều này trong C # sử dụng một hàm đệ quy để đi bộ hàng loạt các yếu tố và nhân chúng lại với nhau, nhưng nó dường như có hiệu suất khủng khiếp về con số như năm 1024 có nhiều yếu tố
Matthew Scharley

3
Điều này tất nhiên là tốt hơn đáng kể so với việc chia cho mọi số lên đến n / 2 hoặc thậm chí sqrt (n), nhưng cách triển khai cụ thể này có hai nhược điểm: khá kém hiệu quả: hàng tấn phép nhân và lũy thừa, nhân nhiều lần cùng một lũy thừa, v.v. Trông Pythonic, nhưng tôi không nghĩ Python là về giết chết hiệu suất. Vấn đề hai: các ước số không được trả về theo thứ tự.
Tomasz Gandor

34

Để mở rộng những gì Shimi đã nói, bạn chỉ nên chạy vòng lặp của mình từ 1 đến căn bậc hai của n. Sau đó, để tìm cặp, làm n / i, và điều này sẽ bao gồm toàn bộ không gian vấn đề.

Như cũng đã lưu ý, đây là một bài toán NP hay còn gọi là 'khó'. Tìm kiếm toàn diện, theo cách bạn đang làm, càng tốt càng tốt cho các câu trả lời đảm bảo. Thực tế này được sử dụng bởi các thuật toán mã hóa và những thứ tương tự để giúp bảo mật chúng. Nếu ai đó giải quyết vấn đề này, hầu hết nếu không phải tất cả các giao tiếp 'an toàn' hiện tại của chúng tôi sẽ không an toàn.

Mã Python:

import math

def divisorGenerator(n):
    large_divisors = []
    for i in xrange(1, int(math.sqrt(n) + 1)):
        if n % i == 0:
            yield i
            if i*i != n:
                large_divisors.append(n / i)
    for divisor in reversed(large_divisors):
        yield divisor

print list(divisorGenerator(100))

Cái nào sẽ xuất ra một danh sách như:

[1, 2, 4, 5, 10, 20, 25, 50, 100]

2
Bởi vì, khi bạn có danh sách các phần tử từ 1..10, bạn có thể tạo bất kỳ phần tử nào trong khoảng 11..100 trivialy. Bạn nhận được {1, 2, 4, 5, 10}. Chia 100 cho mỗi phần tử này và bạn {100, 50, 20, 25, 10}.
Matthew Scharley 5/10/08

2
Các yếu tố LUÔN được tạo ra theo từng cặp, theo định nghĩa. Bằng cách chỉ tìm kiếm đến sqrt (n), bạn đang cắt công việc của mình theo lũy thừa 2.
Matthew Scharley

Nó rất nhanh hơn so với phiên bản trong bài đăng của tôi, nhưng vẫn quá chậm so với phiên bản sử dụng hệ số chính
Andrea Ambu

Tôi đồng ý rằng đây không phải là giải pháp tốt nhất. Tôi chỉ đơn giản là chỉ ra một cách 'tốt hơn' để thực hiện tìm kiếm 'ngu ngốc' đã tiết kiệm được rất nhiều thời gian.
Matthew Scharley 5/10/08

Dữ liệu hóa không được chứng minh là khó NP. vi.wikipedia.org/wiki/Integer_factorization Và vấn đề là tìm tất cả các ước số mà các thừa số nguyên tố (phần khó) đã được tìm thấy.
Jamie

19

Mặc dù đã có nhiều giải pháp cho vấn đề này, nhưng tôi thực sự phải đăng bài này :)

Cái này là:

  • đọc được
  • ngắn
  • độc lập, sao chép và dán sẵn sàng
  • nhanh (trong trường hợp có nhiều thừa số nguyên tố và ước số, nhanh hơn gấp 10 lần so với giải pháp được chấp nhận)
  • tuân thủ python3, python2 và pypy

Mã:

def divisors(n):
    # get factors and their counts
    factors = {}
    nn = n
    i = 2
    while i*i <= nn:
        while nn % i == 0:
            factors[i] = factors.get(i, 0) + 1
            nn //= i
        i += 1
    if nn > 1:
        factors[nn] = factors.get(nn, 0) + 1

    primes = list(factors.keys())

    # generates factors from primes[k:] subset
    def generate(k):
        if k == len(primes):
            yield 1
        else:
            rest = generate(k+1)
            prime = primes[k]
            for factor in rest:
                prime_to_i = 1
                # prime_to_i iterates prime**i values, i being all possible exponents
                for _ in range(factors[prime] + 1):
                    yield factor * prime_to_i
                    prime_to_i *= prime

    # in python3, `yield from generate(0)` would also work
    for factor in generate(0):
        yield factor

Tôi sẽ thay thế while i*i <= nnbằng while i <= limit, nơilimit = math.sqrt(n)
Rafa0809

17

Tôi nghĩ bạn có thể dừng lại ở math.sqrt(n)thay vì n / 2.

Tôi sẽ cho bạn ví dụ để bạn có thể hiểu nó một cách dễ dàng. Bây giờ sqrt(28)5.29như vậy ceil(5.29)sẽ là 6. Vì vậy, nếu tôi dừng lại ở 6 thì tôi sẽ có được tất cả các ước. Làm sao?

Đầu tiên hãy xem mã và sau đó xem hình ảnh:

import math
def divisors(n):
    divs = [1]
    for i in xrange(2,int(math.sqrt(n))+1):
        if n%i == 0:
            divs.extend([i,n/i])
    divs.extend([n])
    return list(set(divs))

Bây giờ, hãy xem hình ảnh bên dưới:

Hãy nói rằng tôi đã thêm 1vào danh sách ước của tôi và tôi bắt đầu với i=2quá

Số chia của 28

Vì vậy, vào cuối tất cả các lần lặp khi tôi đã thêm thương và số chia vào danh sách của mình, tất cả các ước của 28 đều được điền.

Nguồn: Cách xác định ước của một số


2
Đẹp đẹp!! math.sqrt(n) instead of n/2là bắt buộc đối với sự thanh lịch
Rafa0809

Điều này là không đúng. Bạn quên n là chia hết cho chính nó.
jasonleonhard

1
Câu trả lời rất hay. Đơn giản và rõ ràng. Nhưng đối với python 3, có 2 thay đổi cần thiết: n / i nên được nhập bằng int (n / i) vì n / i tạo ra số thực. Ngoài ra, rangex không được dùng trong python 3 và đã được thay thế bằng dải.
Geoffroy CALA

7

Tôi thích giải pháp Greg, nhưng tôi ước nó giống với giải pháp python hơn. Tôi cảm thấy nó sẽ nhanh hơn và dễ đọc hơn; vì vậy sau một thời gian viết mã, tôi đã đưa ra cái này.

Hai chức năng đầu tiên là cần thiết để tạo ra sản phẩm cacte của danh sách. Và có thể được sử dụng lại bất cứ khi nào vấn đề này phát sinh. Nhân tiện, tôi đã phải tự lập trình điều này, nếu ai biết giải pháp tiêu chuẩn cho vấn đề này, vui lòng liên hệ với tôi.

"Factorgenerator" bây giờ trả về một từ điển. Và sau đó từ điển được đưa vào "ước số", người sử dụng nó để tạo ra một danh sách đầu tiên, trong đó mỗi danh sách là danh sách các thừa số có dạng p ^ n với p nguyên tố. Sau đó, chúng tôi tạo ra tích cacte của các danh sách đó, và cuối cùng chúng tôi sử dụng giải pháp của Greg để tạo ra ước số. Chúng tôi sắp xếp chúng và trả lại chúng.

Tôi đã thử nghiệm nó và nó có vẻ nhanh hơn một chút so với phiên bản trước. Tôi đã thử nghiệm nó như một phần của một chương trình lớn hơn, vì vậy tôi không thể nói nó nhanh hơn bao nhiêu.

Pietro Speroni (pietrosperoni chấm nó)

from math import sqrt


##############################################################
### cartesian product of lists ##################################
##############################################################

def appendEs2Sequences(sequences,es):
    result=[]
    if not sequences:
        for e in es:
            result.append([e])
    else:
        for e in es:
            result+=[seq+[e] for seq in sequences]
    return result


def cartesianproduct(lists):
    """
    given a list of lists,
    returns all the possible combinations taking one element from each list
    The list does not have to be of equal length
    """
    return reduce(appendEs2Sequences,lists,[])

##############################################################
### prime factors of a natural ##################################
##############################################################

def primefactors(n):
    '''lists prime factors, from greatest to smallest'''  
    i = 2
    while i<=sqrt(n):
        if n%i==0:
            l = primefactors(n/i)
            l.append(i)
            return l
        i+=1
    return [n]      # n is prime


##############################################################
### factorization of a natural ##################################
##############################################################

def factorGenerator(n):
    p = primefactors(n)
    factors={}
    for p1 in p:
        try:
            factors[p1]+=1
        except KeyError:
            factors[p1]=1
    return factors

def divisors(n):
    factors = factorGenerator(n)
    divisors=[]
    listexponents=[map(lambda x:k**x,range(0,factors[k]+1)) for k in factors.keys()]
    listfactors=cartesianproduct(listexponents)
    for f in listfactors:
        divisors.append(reduce(lambda x, y: x*y, f, 1))
    divisors.sort()
    return divisors



print divisors(60668796879)

Tái bút đây là lần đầu tiên tôi đăng bài lên stackoverflow. Tôi đang mong chờ bất kỳ phản hồi nào.


Trong Python 2.6 có itertools.product ().
jfs 17/12/08

Phiên bản sử dụng trình tạo thay vì list.append ở mọi nơi có thể rõ ràng hơn.
jfs 17/12/08

Sieve of Eratosthenes có thể được sử dụng để tạo các số nguyên tố nhỏ hơn hoặc bằng sqrt (n) stackoverflow.com/questions/188425/project-euler-problem#193605
jfs 17/12/08

1
Kiểu mã hóa: exponents = [k ** x cho k, v trong factor.items () cho x trong dải (v + 1)]
jfs

Đối với listexponents: [[k ** x cho x trong phạm vi (v + 1)] cho k, v trong
factor.items

3

Đây là một cách thông minh và nhanh chóng để làm điều đó cho các số lên đến và khoảng 10 ** 16 trong Python 3.6 thuần túy,

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

Tên của thuật toán dùng để tìm số nguyên tố và phân tích nhân tử là gì? Bởi vì tôi muốn thực hiện điều này trong C # ..
Kyu96

2

Phỏng theo CodeReview , đây là một biến thể hoạt động với num=1!

from itertools import product
import operator

def prod(ls):
   return reduce(operator.mul, ls, 1)

def powered(factors, powers):
   return prod(f**p for (f,p) in zip(factors, powers))


def divisors(num) :

   pf = dict(prime_factors(num))
   primes = pf.keys()
   #For each prime, possible exponents
   exponents = [range(i+1) for i in pf.values()]
   return (powered(primes,es) for es in product(*exponents))

1
Tôi dường như nhận được một lỗi: NameError: global name 'prime_factors' is not defined. Không có câu trả lời nào khác, cũng như câu hỏi ban đầu, xác định điều này làm gì.
AnnanFay

2

Tôi chỉ sẽ thêm một phiên bản sửa đổi một chút của Anivarth (vì tôi tin rằng nó là giống trăn nhất) để tham khảo trong tương lai.

from math import sqrt

def divisors(n):
    divs = {1,n}
    for i in range(2,int(sqrt(n))+1):
        if n%i == 0:
            divs.update((i,n//i))
    return divs

1

Câu hỏi cũ, nhưng đây là ý kiến ​​của tôi:

def divs(n, m):
    if m == 1: return [1]
    if n % m == 0: return [m] + divs(n, m - 1)
    return divs(n, m - 1)

Bạn có thể proxy bằng:

def divisorGenerator(n):
    for x in reversed(divs(n, n)):
        yield x

LƯU Ý: Đối với các ngôn ngữ hỗ trợ, điều này có thể là đệ quy đuôi.


0

Giả sử rằng factorshàm trả về các thừa số của n (ví dụ, factors(60)trả về danh sách [2, 2, 3, 5]), đây là một hàm để tính các ước của n :

function divisors(n)
    divs := [1]
    for fact in factors(n)
        temp := []
        for div in divs
            if fact * div not in divs
                append fact * div to temp
        divs := divs + temp
    return divs

Đó có phải là con trăn không? Dù sao, nó không phải là python 3.x chắc chắn.
GinKin

Đó là mã giả, phải đơn giản để dịch sang python.
user448810

Trễ 3 năm, muộn còn hơn không :) IMO, đây là mã đơn giản nhất, ngắn nhất để làm điều này. Tôi không có bảng so sánh, nhưng tôi có thể phân tích và tính toán các ước số lên đến một triệu trong 1 giây trên máy tính xách tay i5 di động của mình.
Riyaz Mansoor

0

Đây là giải pháp của tôi. Nó có vẻ hơi ngu ngốc nhưng hoạt động tốt ... và tôi đã cố gắng tìm tất cả các ước số thích hợp để vòng lặp bắt đầu từ i = 2.

import math as m 

def findfac(n):
    faclist = [1]
    for i in range(2, int(m.sqrt(n) + 2)):
        if n%i == 0:
            if i not in faclist:
                faclist.append(i)
                if n/i not in faclist:
                    faclist.append(n/i)
    return facts

lỗi đánh máy: trả về sự thật => danh sách trả lại
Jonath P

0

Nếu bạn chỉ quan tâm đến việc sử dụng khả năng hiểu danh sách và không có gì khác quan trọng với bạn!

from itertools import combinations
from functools import reduce

def get_devisors(n):
    f = [f for f,e in list(factorGenerator(n)) for i in range(e)]
    fc = [x for l in range(len(f)+1) for x in combinations(f, l)]
    devisors = [1 if c==() else reduce((lambda x, y: x * y), c) for c in set(fc)]
    return sorted(devisors)

0

Nếu PC của bạn có nhiều bộ nhớ, một dòng đơn thô bạo có thể đủ nhanh với numpy:

N = 10000000; tst = np.arange(1, N); tst[np.mod(N, tst) == 0]
Out: 
array([      1,       2,       4,       5,       8,      10,      16,
            20,      25,      32,      40,      50,      64,      80,
           100,     125,     128,     160,     200,     250,     320,
           400,     500,     625,     640,     800,    1000,    1250,
          1600,    2000,    2500,    3125,    3200,    4000,    5000,
          6250,    8000,   10000,   12500,   15625,   16000,   20000,
         25000,   31250,   40000,   50000,   62500,   78125,   80000,
        100000,  125000,  156250,  200000,  250000,  312500,  400000,
        500000,  625000, 1000000, 1250000, 2000000, 2500000, 5000000])

Mất ít hơn 1 giây trên PC chậm của tôi.


0

Giải pháp của tôi thông qua chức năng trình tạo là:

def divisor(num):
    for x in range(1, num + 1):
        if num % x == 0:
            yield x
    while True:
        yield None

-1
return [x for x in range(n+1) if n/x==int(n/x)]

3
Người hỏi yêu cầu một thuật toán tốt hơn, không chỉ là một định dạng đẹp hơn.
Veedrac

4
Bạn cần sử dụng phạm vi (1, n + 1) để tránh chia cho số không. Ngoài ra, bạn cần phải sử dụng phao (n) cho bộ phận đầu tiên nếu sử dụng Python 2.7, đây 1/2 = 0
Jens Munk

-1

Đối với tôi, điều này hoạt động tốt và cũng sạch sẽ (Python 3)

def divisors(number):
    n = 1
    while(n<number):
        if(number%n==0):
            print(n)
        else:
            pass
        n += 1
    print(number)

Không nhanh lắm nhưng trả về các ước số từng dòng như bạn muốn, bạn cũng có thể thực hiện list.append (n) và list.append (number) nếu bạn thực sự muốn

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.