简体   繁体   English

Python 寻找质因数

[英]Python Finding Prime Factors

Two part question:两部分问题:

  1. Trying to determine the largest prime factor of 600851475143, I found this program online that seems to work.试图确定 600851475143 的最大质因数,我在网上发现这个程序似乎有效。 The problem is, I'm having a hard time figuring out how it works exactly, though I understand the basics of what the program is doing.问题是,我很难弄清楚它到底是如何工作的,尽管我了解程序正在做什么的基础知识。 Also, I'd like if you could shed some light on any method you may know of finding prime factors, perhaps without testing every number, and how your method works.另外,我希望您能阐明您可能知道的寻找主要因素的任何方法,也许无需测试每个数字,以及您的方法是如何工作的。

Here's the code that I found online for prime factorization [NOTE: This code is incorrect.这是我在网上找到的素数分解代码 [注意:此代码不正确。 See Stefan's answer below for better code.] :请参阅下面 Stefan 的回答以获得更好的代码。]

n = 600851475143
i = 2
while i * i < n:
     while n % i == 0:
         n = n / i
     i = i + 1

print(n)

#takes about ~0.01secs
  1. Why is that code so much faster than this code, which is just to test the speed and has no real purpose other than that?为什么这段代码比这段代码快这么多,这只是为了测试速度,除此之外没有其他真正目的?
i = 1
while i < 100:
    i += 1
#takes about ~3secs

This question was the first link that popped up when I googled "python prime factorization" .这个问题是我在 google 上搜索"python prime factorization"时出现的第一个链接。 As pointed out by @quangpn88, this algorithm is wrong (!) for perfect squares such as n = 4, 9, 16, ... However, @quangpn88's fix does not work either, since it will yield incorrect results if the largest prime factor occurs 3 or more times, eg, n = 2*2*2 = 8 or n = 2*3*3*3 = 54 .正如@quangpn88 所指出的,这个算法对于完美的平方是错误的(!) ,例如n = 4, 9, 16, ...但是,@quangpn88 的修复也不起作用,因为如果最大的素数,它会产生不正确的结果factor 出现 3 次或更多次,例如, n = 2*2*2 = 8n = 2*3*3*3 = 54

I believe a correct, brute-force algorithm in Python is:我相信 Python 中正确的蛮力算法是:

def largest_prime_factor(n):
    i = 2
    while i * i <= n:
        if n % i:
            i += 1
        else:
            n //= i
    return n

Don't use this in performance code, but it's OK for quick tests with moderately large numbers:不要在性能代码中使用它,但它可以用于中等数量的快速测试:

In [1]: %timeit largest_prime_factor(600851475143)
1000 loops, best of 3: 388 µs per loop

If the complete prime factorization is sought, this is the brute-force algorithm:如果寻求完整的素数分解,这是蛮力算法:

def prime_factors(n):
    i = 2
    factors = []
    while i * i <= n:
        if n % i:
            i += 1
        else:
            n //= i
            factors.append(i)
    if n > 1:
        factors.append(n)
    return factors

Ok.行。 So you said you understand the basics, but you're not sure EXACTLY how it works.所以你说你了解基础知识,但你不确定它是如何工作的。 First of all, this is a great answer to the Project Euler question it stems from.首先,这是对它源自的 Project Euler 问题的一个很好的回答。 I've done a lot of research into this problem and this is by far the simplest response.我对这个问题做了很多研究,这是迄今为止最简单的回应。

For the purpose of explanation, I'll let n = 20 .出于解释的目的,我让n = 20 To run the real Project Euler problem, let n = 600851475143 .要运行真正的 Project Euler 问题,让n = 600851475143

n = 20 
i = 2

while i * i < n:
    while n%i == 0:
        n = n / i
    i = i + 1

print (n)

This explanation uses two while loops.这个解释使用了两个while循环。 The biggest thing to remember about while loops is that they run until they are no longer true .关于while循环的最重要的事情是它们会一直运行,直到它们不再为true为止。

The outer loop states that while i * i isn't greater than n (because the largest prime factor will never be larger than the square root of n ), add 1 to i after the inner loop runs.外循环指出,虽然i * i不大于n (因为最大的质因子永远不会大于n平方根),但在内循环运行后将i1

The inner loop states that while i divides evenly into n , replace n with n divided by i .内部循环指出,当i平均分为n ,将n替换为n除以i This loop runs continuously until it is no longer true.这个循环持续运行,直到它不再为真。 For n=20 and i=2 , n is replaced by 10 , then again by 5 .对于n=20i=2n替换为10 ,然后再次替换为5 Because 2 doesn't evenly divide into 5 , the loop stops with n=5 and the outer loop finishes, producing i+1=3 .因为2不能均匀地分成5 ,循环在n=5停止,外循环结束,产生i+1=3

Finally, because 3 squared is greater than 5 , the outer loop is no longer true and prints the result of n .最后,因为3 squared 大于5 ,所以外循环不再为true并打印出n的结果。

Thanks for posting this.感谢您发布此信息。 I looked at the code forever before realizing how exactly it worked.在意识到它究竟是如何工作之前,我一直看着代码。 Hopefully, this is what you're looking for in a response.希望这就是您在回复中寻找的内容。 If not, let me know and I can explain further.如果没有,请告诉我,我可以进一步解释。

It looks like people are doing the Project Euler thing where you code the solution yourself.看起来人们正在做 Project Euler 的事情,你自己编写解决方案。 For everyone else who wants to get work done, there's the primefac module which does very large numbers very quickly:对于其他想要完成工作的人,有一个primefac 模块可以非常快速地处理非常大的数字:

#!python

import primefac
import sys

n = int( sys.argv[1] )
factors = list( primefac.primefac(n) )
print '\n'.join(map(str, factors))

For prime number generation I always use Sieve of Eratosthenes :对于素数生成,我总是使用Sieve of Eratosthenes

def primes(n):
    if n<=2:
        return []
    sieve=[True]*(n+1)
    for x in range(3,int(n**0.5)+1,2):
        for y in range(3,(n//x)+1,2):
            sieve[(x*y)]=False

    return [2]+[i for i in range(3,n,2) if sieve[i]]

In [42]: %timeit primes(10**5)
10 loops, best of 3: 60.4 ms per loop

In [43]: %timeit primes(10**6)
1 loops, best of 3: 1.01 s per loop

You can use Miller-Rabin primality test to check whether a number is prime or not.您可以使用Miller-Rabin 素数检验来检查数字是否为素数。 You can find its Python implementations here .您可以在此处找到它的 Python 实现。

Always use timeit module to time your code, the 2nd one takes just 15us :始终使用timeit模块为您的代码计时,第二个只需要15us

def func():
    n = 600851475143
    i = 2
    while i * i < n:
         while n % i == 0:
            n = n / i
         i = i + 1

In [19]: %timeit func()
1000 loops, best of 3: 1.35 ms per loop

def func():
    i=1
    while i<100:i+=1
   ....:     

In [21]: %timeit func()
10000 loops, best of 3: 15.3 us per loop

Isn't largest prime factor of 27 is 3 ?? 27 的最大质因数不是 3 吗?? The above code might be fastest,but it fails on 27 right ?上面的代码可能是最快的,但它在 27 上失败了,对吗? 27 = 3*3*3 The above code returns 1 As far as I know.....1 is neither prime nor composite 27 = 3*3*3 以上代码返回 1 据我所知.....1 既不是质数也不是合数

I think, this is the better code我认为,这是更好的代码

def prime_factors(n):
    factors=[]
    d=2
    while(d*d<=n):
        while(n>1):            
            while n%d==0:
                factors.append(d)
                n=n/d
            d+=1
    return factors[-1]
"""
The prime factors of 13195 are 5, 7, 13 and 29.

What is the largest prime factor of the number 600851475143 ?

"""

from sympy import primefactors
print primefactors(600851475143)[-1]
def find_prime_facs(n):
  list_of_factors=[]
  i=2
  while n>1:
    if n%i==0:
      list_of_factors.append(i)
      n=n/i
      i=i-1
    i+=1  
  return list_of_factors

Another way of doing this:这样做的另一种方法:

import sys
n = int(sys.argv[1])
result = []
for i in xrange(2,n):
    while n % i == 0:
        #print i,"|",n
        n = n/i
        result.append(i)

    if n == 1: 
        break

if n > 1: result.append(n)
print result

sample output :样本输出:
python test.py 68蟒蛇测试.py 68
[2, 2, 17] [2, 2, 17]

If you are looking for pre-written code that is well maintained, use the function sympy.ntheory.primefactors from SymPy .如果您正在寻找维护良好的预先编写的代码,请使用来自SymPy的函数sympy.ntheory.primefactors

It returns a sorted list of prime factors of n .它返回n的素因数的排序列表。

>>> from sympy.ntheory import primefactors
>>> primefactors(6008)
[2, 751]

Pass the list to max() to get the biggest prime factor: max(primefactors(6008))将列表传递给max()以获得最大的质因数: max(primefactors(6008))

In case you want the prime factors of n and also the multiplicities of each of them, use sympy.ntheory.factorint .如果您想要n的质因数以及它们中的每一个的多重性,请使用sympy.ntheory.factorint

Given a positive integer n , factorint(n) returns a dict containing the prime factors of n as keys and their respective multiplicities as values.给定一个正整数nfactorint(n)返回一个字典,其中包含n的质因子作为键,它们各自的重数作为值。

>>> from sympy.ntheory import factorint
>>> factorint(6008)   # 6008 = (2**3) * (751**1)
{2: 3, 751: 1}

The code is tested against Python 3.6.9 and SymPy 1.1.1.该代码针对 Python 3.6.9 和 SymPy 1.1.1 进行了测试。

The code is wrong with 100. It should check case i * i = n:代码错误为 100。它应该检查 case i * i = n:

I think it should be:我觉得应该是:

while i * i <= n:
    if i * i = n:
        n = i
        break

    while n%i == 0:
        n = n / i
    i = i + 1

print (n)

My code:我的代码:

# METHOD: PRIME FACTORS
def prime_factors(n):
    '''PRIME FACTORS: generates a list of prime factors for the number given
    RETURNS: number(being factored), list(prime factors), count(how many loops to find factors, for optimization)
    '''
    num = n                         #number at the end
    count = 0                       #optimization (to count iterations)
    index = 0                       #index (to test)
    t = [2, 3, 5, 7]                #list (to test)
    f = []                          #prime factors list
    while t[index] ** 2 <= n:
        count += 1                  #increment (how many loops to find factors)
        if len(t) == (index + 1):
            t.append(t[-2] + 6)     #extend test list (as much as needed) [2, 3, 5, 7, 11, 13...]
        if n % t[index]:            #if 0 does else (otherwise increments, or try next t[index])
            index += 1              #increment index
        else:
            n = n // t[index]       #drop max number we are testing... (this should drastically shorten the loops)
            f.append(t[index])      #append factor to list
    if n > 1:
        f.append(n)                 #add last factor...
    return num, f, f'count optimization: {count}'

Which I compared to the code with the most votes, which was very fast我将其与得票最多的代码进行了比较,速度非常快

    def prime_factors2(n):
        i = 2
        factors = []
        count = 0                           #added to test optimization
        while i * i <= n:
            count += 1                      #added to test optimization
            if n % i:
                i += 1
            else:
                n //= i
                factors.append(i)
        if n > 1:
            factors.append(n)
        return factors, f'count: {count}'   #print with (count added)

TESTING, (note, I added a COUNT in each loop to test the optimization)测试,(注意,我在每个循环中添加了一个 COUNT 来测试优化)

# >>> prime_factors2(600851475143)
# ([71, 839, 1471, 6857], 'count: 1472')
# >>> prime_factors(600851475143)
# (600851475143, [71, 839, 1471, 6857], 'count optimization: 494')

I figure this code could be modified easily to get the (largest factor) or whatever else is needed.我认为可以轻松修改此代码以获得(最大因素)或其他任何需要的代码。 I'm open to any questions, my goal is to improve this much more as well for larger primes and factors.我对任何问题持开放态度,我的目标是对更大的质数和因数进行更多改进。

如果您想使用 numpy,这里有一种方法可以创建一个包含不大于 n 的所有素数的数组:

[ i for i in np.arange(2,n+1) if 0 not in np.array([i] * (i-2) ) % np.arange(2,i)]

Check this out, it might help you a bit in your understanding.看看这个,它可能对你的理解有所帮助。

#program to find the prime factors of a given number
import sympy as smp

try:
    number = int(input('Enter a number : '))
except(ValueError) :
    print('Please enter an integer !')
num = number
prime_factors = []
if smp.isprime(number) :
    prime_factors.append(number)
else :
    for i in range(2, int(number/2) + 1) :   
        """while figuring out prime factors of a given number, n
        keep in mind that a number can itself be prime or if not, 
        then all its prime factors will be less than or equal to its int(n/2 + 1)"""
        if smp.isprime(i) and number % i == 0 :
            while(number % i == 0) :
                prime_factors.append(i)
                number = number  / i
print('prime factors of ' + str(num) + ' - ')
for i in prime_factors :
    print(i, end = ' ')

在此处输入图片说明

This is my python code: it has a fast check for primes and checks from highest to lowest the prime factors.这是我的 python 代码:它可以快速检查素数,并从最高到最低检查素数。 You have to stop if no new numbers came out.如果没有新的数字出来,你就必须停下来。 (Any ideas on this?) (对此有什么想法吗?)

import math


def is_prime_v3(n):
    """ Return 'true' if n is a prime number, 'False' otherwise """
    if n == 1:
        return False

    if n > 2 and n % 2 == 0:
        return False

    max_divisor = math.floor(math.sqrt(n))
    for d in range(3, 1 + max_divisor, 2):
        if n % d == 0:
            return False
    return True


number = <Number>

for i in range(1,math.floor(number/2)):
    if is_prime_v3(i):
        if number % i == 0:
            print("Found: {} with factor {}".format(number / i, i))

The answer for the initial question arrives in a fraction of a second.最初问题的答案会在几分之一秒内到达。

Below are two ways to generate prime factors of given number efficiently:以下是两种有效生成给定数的质因数的方法:

from math import sqrt


def prime_factors(num):
    '''
    This function collectes all prime factors of given number and prints them.
    '''
    prime_factors_list = []
    while num % 2 == 0:
        prime_factors_list.append(2)
        num /= 2
    for i in range(3, int(sqrt(num))+1, 2):
        if num % i == 0:
            prime_factors_list.append(i)
            num /= i
    if num > 2:
        prime_factors_list.append(int(num))
    print(sorted(prime_factors_list))


val = int(input('Enter number:'))
prime_factors(val)


def prime_factors_generator(num):
    '''
    This function creates a generator for prime factors of given number and generates the factors until user asks for them.
    It handles StopIteration if generator exhausted.
    '''
    while num % 2 == 0:
        yield 2
        num /= 2
    for i in range(3, int(sqrt(num))+1, 2):
        if num % i == 0:
            yield i
            num /= i
    if num > 2:
        yield int(num)


val = int(input('Enter number:'))
prime_gen = prime_factors_generator(val)
while True:
    try:
        print(next(prime_gen))
    except StopIteration:
        print('Generator exhausted...')
        break
    else:
        flag = input('Do you want next prime factor ? "y" or "n":')
        if flag == 'y':
            continue
        elif flag == 'n':
            break
        else:
            print('Please try again and enter a correct choice i.e. either y or n')

Since nobody has been trying to hack this with old nice reduce method, I'm going to take this occupation.由于没有人试图用旧的 nice reduce方法来破解它,所以我要从事这个职业。 This method isn't flexible for problems like this because it performs loop of repeated actions over array of arguments and there's no way how to interrupt this loop by default.此方法对于此类问题并不灵活,因为它对参数数组执行重复操作的循环,并且默认情况下无法中断此循环。 The door open after we have implemented our own interupted reduce for interrupted loops like this:在我们为这样的中断循环实现我们自己的interupted reduce之后,门打开了:

from functools import reduce

def inner_func(func, cond, x, y):
    res = func(x, y)
    if not cond(res):
        raise StopIteration(x, y)
    return res

def ireducewhile(func, cond, iterable):
    # generates intermediary results of args while reducing
    iterable = iter(iterable)
    x = next(iterable)
    yield x
    for y in iterable:
        try:
            x = inner_func(func, cond, x, y)
        except StopIteration:
            break
        yield x

After that we are able to use some func that is the same as an input of standard Python reduce method .之后我们就可以使用一些与标准 Python reduce 方法的输入相同的func Let this func be defined in a following way:让这个func以下列方式定义:

def division(c):
    num, start = c
    for i in range(start, int(num**0.5)+1):
        if num % i == 0:
            return (num//i, i)
    return None

Assuming we want to factor a number 600851475143, an expected output of this function after repeated use of this function should be this:假设我们要对一个数 600851475143 进行因式分解,重复使用该函数后该函数的预期输出应该是这样的:

(600851475143, 2) -> (8462696833 -> 71), (10086647 -> 839), (6857, 1471) -> None

The first item of tuple is a number that division method takes and tries to divide by the smallest divisor starting from second item and finishing with square root of this number.元组的第一项是division方法所取的数字,并尝试除以最小的除数,从第二项开始,以该数字的平方根结束。 If no divisor exists, None is returned.如果不存在除数,则返回 None。 Now we need to start with iterator defined like this:现在我们需要从这样定义的迭代器开始:

def gener(prime):
    # returns and infinite generator (600851475143, 2), 0, 0, 0...
    yield (prime, 2)
    while True:
        yield 0

Finally, the result of looping is:最后,循环的结果是:

result = list(ireducewhile(lambda x,y: div(x), lambda x: x is not None, iterable=gen(600851475143)))
#result: [(600851475143, 2), (8462696833, 71), (10086647, 839), (6857, 1471)]

And outputting prime divisors can be captured by:输出质数除数可以通过以下方式捕获:

if len(result) == 1: output = result[0][0]
else: output = list(map(lambda x: x[1], result[1:]))+[result[-1][0]]
#output: [2, 71, 839, 1471]

Note:笔记:

In order to make it more efficient, you might like to use pregenerated primes that lies in specific range instead of all the values of this range.为了提高效率,您可能希望使用位于特定范围内的预生成素数,而不是该范围内的所有值。

You shouldn't loop till the square root of the number!你不应该循环到数字的平方根! It may be right some times, but not always!有时可能是对的,但并非总是如此!

Largest prime factor of 10 is 5, which is bigger than the sqrt(10) (3.16, aprox). 10 的最大质因数是 5,比 sqrt(10) (3.16, aprox) 大。

Largest prime factor of 33 is 11, which is bigger than the sqrt(33) (5.5,74, aprox). 33 的最大质因数是 11,比 sqrt(33) (5.5,74, aprox) 大。

You're confusing this with the propriety which states that, if a number has a prime factor bigger than its sqrt, it has to have at least another one other prime factor smaller than its sqrt.您将此与适当性混淆了,即如果一个数字的质因数大于其 sqrt,则它必须至少有另一个小于其 sqrt 的质因数。 So, with you want to test if a number is prime, you only need to test till its sqrt.所以,如果你想测试一个数字是否是素数,你只需要测试直到它的 sqrt。

def prime(n):
    for i in range(2,n):
        if n%i==0:
            return False
    return True

def primefactors():
    m=int(input('enter the number:'))
    for i in range(2,m):
        if (prime(i)):
            if m%i==0:
                print(i)
    return print('end of it')

primefactors()

Another way that skips even numbers after 2 is handled:处理 2 后跳过偶数的另一种方法:

def prime_factors(n):
   factors = []
   d    = 2
   step = 1
   while d*d <= n:
      while n>1:
         while n%d == 0:
            factors.append(d)
            n = n/d
        d += step
        step = 2

  return factors
n=int(input("Enter the number"))
if n==1 :  #because the below logic doesn't work on 1
    print(n)
for i in range(2 , n+1):
    if n%i==0 :
        n1=i  #get factor
        for b in range(2,n+1): #check if it is prime
            if ((n1%b)==0) & (n1==b):
                print(n1)
            elif (n1%b)==0 or n1<b:  #if not then pass
                break

i am sure this is the worst logic but it's all the knowledge i have in .py this program will get a number from user and prints all of it's factors numbers that are prime like for 12 it will give 2,3我确信这是最糟糕的逻辑,但这是我在 .py 中拥有的所有知识,该程序将从用户那里获得一个数字并打印所有的因数,这些数字是 12 的素数,它将给出 2,3

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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