在Python中查找数字的所有因子最有效的方法是什么?

在Python中查找数字的所有因子最有效的方法是什么?,python,algorithm,python-2.7,performance,factorization,Python,Algorithm,Python 2.7,Performance,Factorization,有人能给我解释一下在Python(2.7)中查找数字的所有因子的有效方法吗 我可以创建一个算法来实现这一点,但我认为它的编码很差,并且需要很长时间才能生成大量的结果。from functools import reduce def系数(n): 返回集(减少(列表), ([i,n//i]表示范围(1,int(n**0.5)+1)内的i,如果n%i==0))) 这将很快返回一个数字n的所有因子 为什么平方根是上限 sqrt(x)*sqrt(x)=x。如果这两个因子相同,它们都是平方根。如果你让一个

有人能给我解释一下在Python(2.7)中查找数字的所有因子的有效方法吗

我可以创建一个算法来实现这一点,但我认为它的编码很差,并且需要很长时间才能生成大量的结果。

from functools import reduce
def系数(n):
返回集(减少(列表),
([i,n//i]表示范围(1,int(n**0.5)+1)内的i,如果n%i==0)))
这将很快返回一个数字
n
的所有因子

为什么平方根是上限

sqrt(x)*sqrt(x)=x
。如果这两个因子相同,它们都是平方根。如果你让一个因素变大,你必须让另一个因素变小。这意味着两个匹配因子中的一个始终小于或等于
sqrt(x)
,因此您只需搜索到该点即可找到两个匹配因子中的一个。然后可以使用
x/fac1
获取
fac2

reduce(list.\uu add.\uuu,…)
[fac1,fac2]
的小列表合并成一个长列表

如果n%i==0,则范围(1,int(sqrt(n))+1)内i的
[i,n/i]返回一对因子,如果将
n
除以较小的值时余数为零(它不需要也检查较大的值;它只需将
n
除以较小的值即可得到。)


外部的
集合(…)
正在消除重复项,这仅适用于完美正方形。对于
n=4
,这将返回
2
两次,因此
set
将其中一个去掉。

agf回答的另一种方法:

def factors(n):    
    result = set()
    for i in range(1, int(n ** 0.5) + 1):
        div, mod = divmod(n, i)
        if mod == 0:
            result |= {i, div}
    return result

agf的回答真的很酷。我想看看是否可以重写它以避免使用
reduce()
。这就是我想到的:

导入itertools
展平\u iter=itertools.chain.from\u iterable
def系数(n):
返回集(展平(i,n//i)
对于范围内的i(1,int(n**0.5)+1,如果n%i==0))
我还尝试了一个使用复杂生成器函数的版本:

def系数(n):
返回集(x表示tup in([i,n//i]
对于范围内的i(1,int(n**0.5)+1,如果n%i==0),对于tup中的x)
我通过计算来计时:

start = 10000000
end = start + 40000
for n in range(start, end):
    factors(n)
我运行了一次,让Python编译它,然后在time(1)命令下运行了三次,并保持了最佳时间

  • 减少版本:11.58秒
  • itertools版本:11.49秒
  • 棘手版本:11.12秒
注意,itertools版本正在构建一个元组并将其传递给flatten_iter()。如果我将代码改为构建列表,则速度会稍微减慢:

  • iTrools(列表)版本:11.62秒

我相信复杂的生成器函数版本是Python中最快的版本。但它实际上并没有比reduce版本快多少,根据我的测量,大约快4%。

对afg&eryksun解决方案的进一步改进。 以下代码返回所有因素的排序列表,而不改变运行时渐进复杂性:

    def factors(n):    
        l1, l2 = [], []
        for i in range(1, int(n ** 0.5) + 1):
            q,r = n//i, n%i     # Alter: divmod() fn can be used.
            if r == 0:
                l1.append(i) 
                l2.append(q)    # q's obtained are decreasing.
        if l1[-1] == l2[-1]:    # To avoid duplication of the possible factor sqrt(n)
            l1.pop()
        l2.reverse()
        return l1 + l2
想法:不要使用list.sort()函数来获得一个排序后的列表,这会增加nlog(n)的复杂性;在l2上使用list.reverse()要快得多,这需要O(n)复杂度。(python就是这样制作的。) 在l2.reverse()之后,可以将l2附加到l1,以获得因子的排序列表


请注意,l1包含的i-s正在增加。l2包含正在减小的q-s。这就是使用上述想法的原因。

对于像99这样有3*3*11和
floor sqrt(99)+1==10的不寻常数字,一定要抓住大于
sqrt(数字系数)

import math

def factor(x):
  if x == 0 or x == 1:
    return None
  res = []
  for i in range(2,int(math.floor(math.sqrt(x)+1))):
    while x % i == 0:
      x /= i
      res.append(i)
  if x != 1: # Unusual numbers
    res.append(x)
  return res

使用以下简单的理解列表,注意我们不需要测试1和我们试图找到的数字:

def factors(n):
    return [x for x in range(2, n//2+1) if n%x == 0]
关于平方根的使用,假设我们要找到10的因子。
sqrt(10)=4的整数部分,因此
范围(1,int(sqrt(10))=[1,2,3,4]
和测试多达4个明显未命中5


除非我遗漏了一些建议,否则如果必须这样做,请使用
int(ceil(sqrt(x))
。当然,这会产生大量不必要的函数调用。

由@agf提出的解决方案非常好,但是通过检查奇偶校验,可以使任意奇数的运行时间缩短约50%。由于奇数的因子本身总是奇数,因此在处理奇数时不必检查这些因子

我刚开始自己解决难题。在某些问题中,在两个嵌套的
for
循环中调用除数检查,因此此函数的性能至关重要

将这一事实与agf的优秀解决方案结合起来,我最终实现了以下功能:

from functools import reduce
from math import sqrt
def factors(n):
        step = 2 if n%2 else 1
        return set(reduce(list.__add__,
                    ([i, n//i] for i in range(1, int(sqrt(n))+1, step) if n % i == 0)))
import numpy as np
def b(n):
    r = np.arange(1, int(n ** 0.5) + 1)
    x = r[np.mod(n, r) == 0]
    return set(np.concatenate((x, n / x), axis=None))   
但是,对于较小的数字(~<100),此更改产生的额外开销可能会导致函数花费更长的时间

为了检查速度,我做了一些测试。下面是使用的代码。为了生成不同的图,我相应地修改了
X=range(1100,1)

import timeit
from math import sqrt
from matplotlib.pyplot import plot, legend, show

def factors_1(n):
    step = 2 if n%2 else 1
    return set(reduce(list.__add__,
                ([i, n//i] for i in range(1, int(sqrt(n))+1, step) if n % i == 0)))

def factors_2(n):
    return set(reduce(list.__add__,
                ([i, n//i] for i in range(1, int(sqrt(n)) + 1) if n % i == 0)))

X = range(1,100000,1000)
Y = []
for i in X:
    f_1 = timeit.timeit('factors_1({})'.format(i), setup='from __main__ import factors_1', number=10000)
    f_2 = timeit.timeit('factors_2({})'.format(i), setup='from __main__ import factors_2', number=10000)
    Y.append(f_1/f_2)
plot(X,Y, label='Running time with/without parity check')
legend()
show()
X=范围(1100,1)

在这方面没有显著差异,但数字越大,优势就越明显:

X=范围(11000001000)(仅奇数)

X=范围(210000100)(仅偶数)

X=范围(11000001001)(交替奇偶校验)

这里是另一个没有reduce的替代方案,它在处理大量数据时表现良好。它使用
sum
将列表展平

def factors(n):
    return set(sum([[i, n//i] for i in xrange(1, int(n**0.5)+1) if not n%i], []))

我已经用timeit尝试了大多数这些美妙的答案来比较它们的效率和我的简单功能,但我经常看到我的比这里列出的更好。我想我会和大家分享,看看大家怎么想

def factors(n):
    results = set()
    for i in xrange(1, int(math.sqrt(n)) + 1):
        if n % i == 0:
            results.add(i)
            results.add(int(n/i))
    return results
正如它写的,你必须导入数学来测试,但是
def factors(num):
    if (num == 1 or num == 0):
        return []
    f = [1]
    sq = int(math.sqrt(num))
    for i in range(2, sq):
        if num % i == 0:
            f.append(i)
            f.append(num/i)
    if sq > 1 and num % sq == 0:
        f.append(sq)
        if sq*sq != num:
            f.append(num/sq)
    return f
def factors(n):
    return set(
        factor for i in range(1, int(n**0.5) + 1) if n % i == 0
        for factor in (i, n//i)
    )
def num_factors(n):
    results = set()
    for i in range(1, int(n**0.5) + 1):
        if n % i == 0: results.update([i,int(n/i)])
    return results
def factors(n):
    '''
    return prime factors and multiplicity of n
    n = p0^e0 * p1^e1 * ... * pk^ek encoded as
    res = [(p0, e0), (p1, e1), ..., (pk, ek)]
    '''

    res = []

    # get rid of all the factors of 2 using bit shifts
    mult = 0
    while not n & 1:
        mult += 1
        n >>= 1
    if mult != 0:
        res.append((2, mult))

    limit = round(sqrt(n))
    test_prime = 3
    while test_prime <= limit:
        mult = 0
        while n % test_prime == 0:
            mult += 1
            n //= test_prime
        if mult != 0:
            res.append((test_prime, mult))
            if n == 1:              # only useful if ek >= 3 (ek: multiplicity
                break               # of the last prime) 
            limit = round(sqrt(n))  # adjust the limit
        test_prime += 2             # will often not be prime...
    if n != 1:
        res.append((n, 1))
    return res
>>> from sympy import factorint
>>> factorint(2**70 + 3**80) 
{5: 2,
 41: 1,
 101: 1,
 181: 1,
 821: 1,
 1597: 1,
 5393: 1,
 27188665321L: 1,
 41030818561L: 1}
>>> int(n**0.5)
10000000000000078L
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))
    x = 23

    i = 1
    while i <= x:
      if x % i == 0:
        print("factor: %s"% i)
      i += 1
import numpy as np
def b(n):
    r = np.arange(1, int(n ** 0.5) + 1)
    x = r[np.mod(n, r) == 0]
    return set(np.concatenate((x, n / x), axis=None))   
def factors(n):
    factors = []
    for i in range(1, n//2+1):
        if n % i == 0:
            factors.append (i)
    factors.append(n)

    return factors
import 'dart:math';
generateFactorsOfN(N){
  //determine lowest bound divisor range
  final lowerBoundCheck = sqrt(N).toInt();
  var factors = Set<int>(); //stores factors
  /**
   * Lets take 16:
   * 4 = sqrt(16)
   * start from 1 ...  4 inclusive
   * check mod 16 % 1 == 0?  set[1, (16 / 1)]
   * check mod 16 % 2 == 0?  set[1, (16 / 1) , 2 , (16 / 2)]
   * check mod 16 % 3 == 0?  set[1, (16 / 1) , 2 , (16 / 2)] -> unchanged
   * check mod 16 % 4 == 0?  set[1, (16 / 1) , 2 , (16 / 2), 4, (16 / 4)]
   *
   *  ******************* set is used to remove duplicate
   *  ******************* case 4 and (16 / 4) both equal to 4
   *  return factor set<int>.. this isn't ordered
   */

  for(var divisor = 1; divisor <= lowerBoundCheck; divisor++){
    if(N % divisor == 0){
      factors.add(divisor);
      factors.add(N ~/ divisor); // ~/ integer division 
    }
  }
  return factors;
}
 import math

    '''
    I applied finding prime factorization to solve this. (Trial Division)
    It's not complicated
    '''


    def generate_factors(n):
        lower_bound_check = int(math.sqrt(n))  # determine lowest bound divisor range [16 = 4]
        factors = set()  # store factors
        for divisors in range(1, lower_bound_check + 1):  # loop [1 .. 4]
            if n % divisors == 0:
                factors.add(divisors)  # lower bound divisor is found 16 [ 1, 2, 4]
                factors.add(n // divisors)  # get upper divisor from lower [ 16 / 1 = 16, 16 / 2 = 8, 16 / 4 = 4]
        return factors  # [1, 2, 4, 8 16]


    print(generate_factors(12)) # {1, 2, 3, 4, 6, 12} -> pycharm output

 Pierre Vriens hopefully this makes more sense. this is an O(nlogn) solution. 
def factors(x):
    return [i for i in range(1,x+1) if x%i==0]
number=30
tuple_list=[]
for i in np.arange(1,number):
    if number%i==0:
         other=int(number/i)
         if any([(x,v) for (x,v) in tuple_list if (i==x) or (i==v)])==True:
             break
         tuple_list.append((i,other))
    
 flattened = [item for sublist in tuple_list for item in sublist]              
 print(sorted(flattened))
 [1, 2, 3, 5, 6, 10, 15, 30]