在Python中查找数字的所有因子最有效的方法是什么?
有人能给我解释一下在Python(2.7)中查找数字的所有因子的有效方法吗 我可以创建一个算法来实现这一点,但我认为它的编码很差,并且需要很长时间才能生成大量的结果。在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。如果这两个因子相同,它们都是平方根。如果你让一个
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]