Criticism of my prime_factors () function

I just wrote this function to split the number into its prime factors. Is an algorithm using a good one? How can I improve this feature? So far, this works well.

from copy import copy

# Decorator to memoize using the first argument only
@memoize_first
def prime_factors(n, prime_list=[None], prime_limit=[None]):
    """
    Get the prime factors of n.
    Returns a list of all factors
    Uses trial division
    """
    if n == 1: return None
    if is_prime(n): return None

    # Use mutability of default arguments to avoid recalculating the
    #  prime list unless we're called with a higher n than we've seen before
    if prime_limit == [None]:
        prime_limit[0] = n
    if prime_list == [None] or prime_limit[0] < n:
        prime_list.extend(list(primes(n))) # standard seive-based primes()
        prime_list.remove(None)
        prime_limit[0] = n

    factor_list = []

    # Copying because otherwise removing already tested 
    #  primes will mutate the cached list
    test_primes = copy(prime_list)

    while 1:
        # Pull first element because we mutate the list in the loop,
        #  so a for loop won't work
        prime = test_primes[0]
        # Prime list is ordered, so we've got a cached list larger 
        #  than the number to be tested
        if prime > n: break
        if n % prime == 0:
            remainder = n / prime # Relying on 2.6 floor division
            factor_list.append(prime)
            if is_prime(remainder):
                factor_list.append(remainder)
            else:
                factor_list.extend(prime_factors(remainder, test_primes))
            break
        else:
            test_primes.remove(prime)

    return filter(None, factor_list)

Edit

I accepted Alex's answer as it matches the purpose of where I started with the largest (memoizing). Each answer was very instructive, and there are definitely reasonable ways to do this. Thanks everyone!

+3
source share
4 answers

@msw : ( ), .

, factor - (, , , OP !), , , . . (: " " , , , - , , - ):

def factor(n, _memo={1: []}):
    """returns a list of the prime factors of integer n"""
    if n <= 0: raise ValueError("Can't factor %r" % n)
    localmemo = {}
    orgn = n
    p = primes.generator()  # yields an infinite iterable
    for x in p:
        if n in _memo:
            for k in localmemo:
                localmemo[k].extend(_memo[n])
            _memo.update(localmemo)
            return _memo[orgn]
        localmemo[n] = []
        if n % x == 0:
            n = n/x
            for k in localmemo:
                localmemo[k].append(x)
            p.send(x)  # get `x` again next time

: , - , send, , next ( for, x , ).

" " ( , , ), " " ( ), - , , @msw, ( , , gmpy, ;-) (, , ) .

: , , :

def factor(n, _memo={1: []}):
    """returns a list of the prime factors of integer n

       n must be > 0 (otherwise, raises ValueError).

       uses a stream of primes in increasing order generated
       by `primes.generator()` (q.v.).

       do **not** pass argument _memo, it used for memoization
       (holding the list of factors for all ints that have already
        been factorized by this function in this process past).
    """
    # get error cases out of the way first
    if n <= 0: raise ValueError("Can't factor %r" % n)
    # localmemo records all numbers which are being factorized
    # for the first time in this specific call to `factor`, each
    # with a list of corresponding factors found so far
    localmemo = {}
    # keep a copy of the original n since in the loop below n
    # gets decreased
    orgn = n
    p = primes.generator()  # yields an infinite iterable
    # look at each prime (the .send call below may cause a prime
    # to be looked at more than once since it assumed to work
    # as a "push back" for this specific generator)
    for x in p:
        if n in _memo:  # we've factorized n already in the past
            # (or n is 1, which is always a key in _memo!)
            # so we're all done, mop up
            # every list of factors in localmemo gets all n factors
            for k in localmemo:
                localmemo[k].extend(_memo[n])
            # add every localmemo list to _memo for future calls
            _memo.update(localmemo)
            # now orgn is in _memo (as it was in localmemo if it had
            # not already been in _memo it been added) so we can just
            # index to get the corresponding list of factors
            return _memo[orgn]
        # start with an empty list since we don't know n factors yet
        localmemo[n] = []
        if n % x == 0:  # x is a factor of n, so of everything we're factoring
            n = n/x
            for k in localmemo:
                localmemo[k].append(x)  # ...so add it to every entry in localmemo
            p.send(x)  # get `x` again next time (it might be a multiple factor!)

: pushback , ...:

def withpushback(aniterator):
  pushback = None
  while True:
    if pushback is not None:  # last client action was `send`
      to_yield = pushback
      while pushback is not None:
        pushback = yield to_yield  # iterate until a `next`!
    else:                     # last client action was `next`
      try: to_yield = next(aniterator)
      except StopIteration: break
    pushback = yield to_yield

# an example use...:
w = withpushback(iter(range(7)))
repetitions = {2: 3, 5: 2}
for p in w:
  print p,
  if repetitions.get(p):
    w.send(p)
    repetitions[p] -= 1
print

0 1 2 2 2 2 3 4 5 5 5 6 - .. 2 5, repetitions dict. ( next send, send ; -).

+4

"", . , , . , , , is None, -, . , , , , .

:

def factor(n):
    """returns a list of the prime factors of integer n"""
    factors = []
    p = primes.generator() # yields an infinite list
    while n > 1:
        x = p.next()
        while n % x == 0:
            n = n/x
            factors.append(x)
    return factors

primes, primes.factor.

:

primes.factor , GNU core-utils factor ~ 10 ^ 20. , , :

def generator():
    """returns a generator that yields an infinite list of primes"""
    return itertools.ifilter(
        lambda n, Primes=[]:
            all(n%p for p in
                itertools.takewhile(lambda p, s=n**0.5: p <= s, Primes)
            ) and not Primes.append(n),
        itertools.count(2)
    )

, , Alex Martelli Python Cookbook, , . , . unit test factor, , , , .

GNU factor - , , . , , Python. , "": -, , , ?

:

, , , , generator "" , , , . ( ), - , , - , , . , , "".

+4

- , , n <= int (sqrt (n))
, , , , . ( , )

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

def listfactorization(n):
    """ Returns a list of the prime factorization of n """
    pf = []
    for p in primeslist:      # this is my main point (different from OP & msw's)
      if p*p > n : break      # "if p > n: break", is a waste as in others answers
      while not n % p:        # only test divisibility up to sqrt(n)
        n /= p                
        pf.append(p)          # main point again (complementary of the above)
    if n > 1: pf.append(n)    # if n is still > 1 implies n is prime so append
    return pf                 

primeslist = primes1(10**6)   #to factorize numbers upto 10e12

#or 

primeslist = primes1(10**7)   #to factorize numbers upto 10e14

, , sqrt ().

10e14, primeslist = primes (10 ** 7)

, n, do primeslist = primes (int (n ** 0.5))

primeslist 10 ** 7

python, 10e14,
, , numpy.
, .

+4

, . () :

Python , C/++ .

Python . 183758673828099 0,27 :

time pyecm 183758673828099
Factoring 183758673828099:
3
775353054127
79

real    0m0.268s
user    0m0.252s
sys 0m0.008s
+2

Source: https://habr.com/ru/post/1755343/


All Articles