简体   繁体   中英

Python : Primes in numbers

Given a positive number n > 1 find the prime factor decomposition of n. The result will be a string with the following form :

"(p1**n1)(p2**n2)...(pk**nk)"

with the p(i) in increasing order and n(i) empty if n(i) is 1.

Example: n = 86240 should return "(2**5)(5)(7**2)(11)"

This is my code , but i have problem with time for n>250000

def primeFactors(n):
    a=""
    i=2
    while i<=n:
        #part of identifying if number i is prime
        w1=5
        w2=2
        gg=0
        while w1*w1<=i:
            if i%w1==0:
                gg=1
                break
            w1+=w2
            w2=6-w2
        #checking if previous loop has been broken
         if gg:
            i+=1
            continue
        #countig how many thimes we can divide n on i
        c=0
        for j in range(1,n):
            if n%i!=0: break
            c+=1
            n=n//i
        #if we can divide n on i only once
        if c==1:
            a+="("+str(i)+")"
        elif c>1:
            a+="("+str(i)+"**"+str(c)+")"
        i+=1
    return a

is there a way to fix this problem?

The problem isn't prime factorization, which the program does, it's getting Python to do it for very large numbers in a very small amount of time. Specifically, my understanding is we need to be able to calculate:

for n in range(10 ** 9, 10 ** 9 + 10):
    print(n, '=', primeFactors(n))

in <= 120ms or less, ie <= 12ms per number. The OP's code doesn't get past 10 ** 9 + 4 before milliseconds turn into seconds, seconds into minutes. My first impression of your code is you need to separate your prime logic from the rest of the code, to make it understandable, and then clean up and optimize both parts of the code. My rewrite of your program:

def generate_primes(n):

    """ generate real primes """

    yield(2)

    primes = [(2, 4)]

    for m in range(3, n, 2):

        for prime, square in primes:

            if square > m:
                yield(m)
                primes.append((m, m * m))
                break

            if m % prime == 0:
                break

def primeFactors(n):
    string = ""
    i = 2

    for i in generate_primes(int(n ** 0.5) + 1):

        # counting how many times we can divide n on i
        c = 0

        while True:
            product, remainder = divmod(n, i)

            if remainder != 0:
                break

            n = product
            c += 1

        # if we can divide n on i only once
        if c == 1:
            string += f"({i})"
        elif c > 1:
            string += f"({i}**{c})"

    if n > 1:  # final prime factor greater than square root
        string += f"({n})"

    return string

I swapped in a prime generator to avoid redundant calculations. This optimized version can achieve 32ms per large number factored in the above test. Still not good enough. So let's try @JamesKPolk's suggestion and use pseudoprimes:

def generate_primes(n):

    """ generate 5-rough pseudoprimes """

    if n >= 2:
        yield(2)

    if n >= 3:
        yield(3)

    if n >= 5:
        m = 1
        x = 4

        while m < n:
            m += x
            yield(m)
            x = 6 - x

The tradeoff here is that we will test more divisors than we really need, but we can generate these divisors much faster. This change achieves our goal of <= 12ms per large number factored, on my machine anyway.

OUTPUT

> time python3 test.py
1000000000 = (2**9)(5**9)
1000000001 = (7)(11)(13)(19)(52579)
1000000002 = (2)(3)(43)(983)(3943)
1000000003 = (23)(307)(141623)
1000000004 = (2**2)(41**2)(148721)
1000000005 = (3)(5)(66666667)
1000000006 = (2)(500000003)
1000000007 = (1000000007)
1000000008 = (2**3)(3**2)(7)(109**2)(167)
1000000009 = (1000000009)
0.106u 0.010s 0:00.11 100.0%    0+0k 0+0io 0pf+0w
> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM