숫자의 모든 제수를 구하는 가장 좋은 방법은 무엇입니까?


106

다음은 매우 멍청한 방법입니다.

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

내가 얻고 싶은 결과는 이것과 비슷하지만 더 똑똑한 알고리즘을 원합니다 (이건 너무 느리고 멍청합니다 :-)

나는 소인수와 그 다중성을 충분히 빠르게 찾을 수 있습니다. 이런 식으로 요소를 생성하는 생성기가 있습니다.

(인수 1, 다중도 1) (인수 2, 다중도 2)
(인수
3, 다중도 3)
등 ...

즉, 출력

for i in factorGenerator(100):
    print i

is :

(2, 2)
(5, 2)

내가 원하는 일에 얼마나 유용한 지 모르겠습니다 (다른 문제에 대해 코딩했습니다) 어쨌든 더 똑똑한 방법을 원합니다.

for i in divisorGen(100):
    print i

이것을 출력하십시오 :

1
2
4
5
10
20
25
50
100

업데이트 : Greg Hewgill과 그의 "스마트 한 방법"에게 많은 감사를드립니다 . :) 100000000의 모든 제수를 계산하는 데 0.01 초가 걸렸습니다.

업데이트 2 : 게시물 의 중복이라고 말하지 마세요. 주어진 숫자의 제수를 계산할 때 모든 제수를 계산할 필요는 없습니다. 다른 문제입니다. 그렇지 않다고 생각한다면 위키피디아에서 "제수 함수"를 찾으십시오. 게시하기 전에 질문과 답변을 읽으십시오. 주제가 무엇인지 이해하지 못하는 경우 유용하지 않고 이미 제공된 답변을 추가하지 마십시오.


그것은이 질문은 "주어진 수의 약수의 개수를 계산하는 알고리즘"의 거의 중복 것을 제안 된 이유는 그 질문에 제안 된 첫 번째 단계는 것이었다이었다 약수의 모든 찾을 내가 정확히 믿고, 무엇을하려고 하셨나요?
Andrew Edgecombe

4
Andrew가 몇 개의 제수를 찾으려면 소인수를 찾은 다음이를 사용하여 얼마나 많은 제수가있을 수 있는지 계산하면됩니다. 이 경우 제수를 찾을 필요가 없습니다.
Loïc Faure-Lacroix

1
@Andrea 암부, 당신은 이름 기능을 수정하시기 바랍니다
미네랄

답변:


77

귀하의 factorGenerator기능이 주어지면 다음 divisorGen은 작동해야합니다.

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

이 알고리즘의 전반적인 효율성은 전적으로 factorGenerator.


2
와우 멍청한 길 (n / 2에서 멈춤)을 취한 39에 대해 100000000의 모든 제수를 계산하는 데 0.01이 걸렸습니다. 감사합니다!
Andrea Ambu

47
Pythonese를 이해하지 못하는 사람들을 위해 이것은 실제로 무엇을 하는가?
Matthew Scharley

1
일산화물 : 주어진 인자의 모든 곱셈 조합을 계산합니다. 대부분은 자명해야합니다. "yield"라인은 반환과 비슷하지만 값을 반환 한 후에도 계속 진행됩니다. [0] * nfactors는 길이 nfactor의 0 목록을 생성합니다. reduce (...)는 인자의 곱을 계산합니다.
Greg Hewgill

축소 및 람다 표기법은 실제로 나를 혼란스럽게하는 부분이었습니다. 재귀 함수를 사용하여 여러 요소를 살펴보고 모두 함께 곱하는 알고리즘을 C #에서 구현하려고 시도했지만 많은 요소가있는 1024와 같은 숫자에서 끔찍한 성능을 발휘하는 것 같습니다
Matthew Scharley

3
물론 이것은 n / 2 또는 sqrt (n)까지의 모든 숫자로 나누는 것보다 훨씬 낫지 만,이 특별한 구현에는 두 가지 단점이 있습니다. 매우 무감합니다 : 수많은 곱셈과 지수화, 반복적으로 같은 거듭 제곱 등을 곱합니다. Pythonic처럼 보입니다. 하지만 저는 파이썬이 성능을 죽이는 것이라고 생각하지 않습니다. 문제 2 : 제수가 순서대로 반환되지 않습니다.
Tomasz Gandor 2014

34

Shimi가 말한 내용을 확장하려면 1에서 n의 제곱근까지만 루프를 실행해야합니다. 그런 다음 쌍을 찾으려면을 수행하십시오. 그러면 n / i전체 문제 공간이 포함됩니다.

또한 언급했듯이 이것은 NP 또는 '어려운'문제입니다. 철저한 검색은 보장 된 답변을 얻는 것만큼이나 좋습니다. 이 사실은 암호화 알고리즘 등에서 보안을 위해 사용됩니다. 누군가가이 문제를 해결한다면, 현재의 '보안'커뮤니케이션 중 대부분은 안전하지 않은 상태가 될 것입니다.

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

다음과 같은 목록을 출력해야합니다.

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

2
1..10 사이의 요소 목록이 있으면 11..100 사이의 모든 요소를 ​​상식적으로 생성 할 수 있기 때문입니다. {1, 2, 4, 5, 10}을 얻습니다. 100을 각 요소로 나누면 {100, 50, 20, 25, 10}이됩니다.
Matthew Scharley

2
요인은 정의에 따라 항상 쌍으로 생성됩니다. sqrt (n)으로 만 검색하면 작업을 2의 거듭 제곱으로자를 수 있습니다.
Matthew Scharley

그것은 내 게시물의 버전보다 매우 빠르다,하지만 소인수를 사용하여 여전히 버전보다 너무 느린
안드레아 암부

이것이 최선의 해결책이 아니라는 데 동의합니다. 이미 많은 시간을 절약 할 수있는 '멍청한'검색을 수행하는 '더 나은'방법을 간단히 지적했습니다.
Matthew Scharley

Factorization은 NP-hard로 표시되지 않았습니다. en.wikipedia.org/wiki/Integer_factorization 문제는 소인수 (어려운 부분)가 이미 발견 되었기 때문에 모든 제수를 찾는 것이 었습니다.
Jamie

19

이것에 대한 많은 해결책이 이미 있지만 정말 이것을 게시해야합니다 :)

이것은 :

  • 읽을 수있는
  • 짧은
  • 자체 포함, 복사 및 붙여 넣기 준비
  • 빠름 (소인수와 제수가 많은 경우 허용 된 솔루션보다 10 배 이상 빠름)
  • python3, python2 및 pypy 호환

암호:

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

나는 대체 할 while i*i <= nnwhile i <= limit어디limit = math.sqrt(n)
Rafa0809

17

math.sqrt(n)n / 2 대신에 멈출 수 있다고 생각합니다 .

쉽게 이해할 수 있도록 예를 들어 드리겠습니다. 지금은 sqrt(28)이다 5.29그래서 ceil(5.29)그때 나는 모든 약수를 얻을 수있는 것이다 6시에 중단됩니다 6. 그래서 일 것이다. 어떻게?

먼저 코드를 확인한 다음 이미지를 확인합니다.

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

이제 아래 이미지를 참조하십시오.

수 있습니다 내가 이미 추가 한 말을 1내 제수 목록에 내가 시작 i=2그래서

28의 제수

따라서 모든 반복이 끝날 때 몫과 제수를 목록에 추가하면 28의 모든 제수가 채워집니다.

출처 : 숫자의 제수를 결정하는 방법


2
좋아 좋아!! math.sqrt(n) instead of n/2우아함을 위해 필수입니다
Rafa0809

이것은 올바르지 않습니다. n은 그 자체로 나눌 수 있다는 것을 잊었습니다.
jasonleonhard

1
좋은 대답입니다. 간단하고 명확합니다. 그러나 파이썬 3의 경우 두 가지 필요한 변경 사항이 있습니다. 또한 rangex는 파이썬 3에서 더 이상 사용되지 않으며 범위로 대체되었습니다.
Geoffroy CALA

7

나는 Greg 솔루션을 좋아하지만 파이썬처럼 더 좋았 으면 좋겠다. 더 빠르고 가독성이 좋을 것 같습니다. 그래서 얼마간의 코딩 후에 나는 이것을 나왔습니다.

처음 두 함수는 목록의 데카르트 곱을 만드는 데 필요합니다. 그리고이 문제가 발생할 때마다 재사용 할 수 있습니다. 그건 그렇고,이 문제에 대한 표준 솔루션을 아는 사람이 있으면 저에게 연락 주시기 바랍니다.

"Factorgenerator"는 이제 사전을 반환합니다. 그런 다음 사전은 "제수"에 입력되어 먼저 목록 목록을 생성하는 데 사용합니다. 여기서 각 목록은 p 소수를 갖는 p ^ n 형식의 인수 목록입니다. 그런 다음 해당 목록의 데카르트 곱을 만들고 마지막으로 Greg의 솔루션을 사용하여 제수를 생성합니다. 우리는 그들을 분류하고 반환합니다.

나는 그것을 테스트했으며 이전 버전보다 조금 더 빠른 것 같습니다. 더 큰 프로그램의 일부로 테스트했기 때문에 얼마나 더 빠른지 말할 수는 없습니다.

Pietro Speroni (pietrosperoni dot it)

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)

추신 내가 stackoverflow에 게시하는 것은 처음입니다. 피드백을 기다리고 있습니다.


Python 2.6에는 itertools.product ()가 있습니다.
jfs

모든 곳에서 list.append 대신 생성기를 사용하는 버전이 더 깨끗할 수 있습니다.
jfs

에라토스테네스의 체를 사용하여 sqrt (n) 이하의 소수를 생성 할 수 있습니다. stackoverflow.com/questions/188425/project-euler-problem#193605
jfs

1
코딩 스타일 : exponents = [k의 경우 k ** x, 범위 (v + 1)의 x에 대한 factor.items ()의 v]
jfs

listexponents : [[k ** x for x in range (v + 1)] for k, v in
factor.items

3

다음은 순수 Python 3.6에서 최대 10 ** 16의 숫자에 대해 스마트하고 빠른 방법입니다.

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

소수를 찾고 인수 분해하는 데 사용되는 알고리즘의 이름은 무엇입니까? C #으로 구현하고 싶기 때문에 ..
Kyu96

2

CodeReview 에서 채택한 다음은 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
오류가 발생하는 것 같습니다 NameError: global name 'prime_factors' is not defined.. 다른 답변이나 원래 질문은 이것이 무엇을하는지 정의하지 않습니다.
AnnanFay

2

나는 향후 참조를 위해 Anivarth의 약간 수정 된 버전 (가장 비단뱀이라고 생각하는)을 추가 할 것입니다.

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

오래된 질문이지만 여기에 내 의견이 있습니다.

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

다음으로 프록시 할 수 있습니다.

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

참고 : 지원하는 언어의 경우 꼬리 재귀적일 수 있습니다.


0

한다고 가정 factors함수 반환 요소 n은 (예를 들어, factors(60)목록이 반환한다 [2, 2, 3, 5), 여기서 제수를 계산하는 함수 , 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

그게 파이썬인가요? 어쨌든, 확실히 파이썬 3.x가 아닙니다.
GinKin 2014 년

파이썬으로 번역하기가 간단해야하는 의사 코드입니다.
user448810 2014 년

3 년 후, 결코 늦지 않았습니다. :) IMO,이 작업을 수행하는 가장 간단하고 짧은 코드입니다. 비교표는 없지만 i5 휴대용 노트북에서 최대 백만 분의 1에 대해 제수를 인수 분해하고 계산할 수 있습니다.
Riyaz Mansoor

0

여기 내 해결책이 있습니다. 바보 같지만 잘 작동합니다 ... 모든 적절한 제수를 찾으려고 했으므로 루프가 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

오타 : 사실 반환 => faclist 반환
P

0

목록 이해력 사용에만 관심이 있고 다른 것은 중요하지 않은 경우!

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

PC에 메모리가 많은 경우 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])

느린 PC에서 1 초 미만이 걸립니다.


0

생성기 기능을 통한 내 솔루션은 다음과 같습니다.

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
질문자는 단순한 형식이 아닌 더 나은 알고리즘을 요구했습니다.
Veedrac

4
0으로 나누지 않으려면 range (1, n + 1)을 사용해야합니다. 또한 Python 2.7을 사용하는 경우 첫 번째 분할에 float (n)을 사용해야합니다. 여기서 1/2 = 0
Jens Munk

-1

나를 위해 이것은 잘 작동하고 깨끗합니다 (Python 3)

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

매우 빠르지는 않지만 원하는대로 줄 단위로 제수를 반환합니다. 또한 원하는 경우 list.append (n) 및 list.append (number)를 수행 할 수도 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.