Python 如何使这个完美平方和函数更有效?
我家庭作业的一部分是做一个函数,检查一个数字是否是两个平方的和。问题是,大量的数据需要很长时间才能通过它。有没有关于如何提高效率的建议?例如,给定数字Python 如何使这个完美平方和函数更有效?,python,python-3.x,function,while-loop,Python,Python 3.x,Function,While Loop,我家庭作业的一部分是做一个函数,检查一个数字是否是两个平方的和。问题是,大量的数据需要很长时间才能通过它。有没有关于如何提高效率的建议?例如,给定数字50,它将返回(7,1),因为7^2是49,1^2是1,所以总数将是50 代码如下: def sum_of_squares(n) : i = 1 while i * i <= n : j = 1 while(j * j <= n) : while (i * i + j *
50
,它将返回(7,1)
,因为7^2是49,1^2是1,所以总数将是50
代码如下:
def sum_of_squares(n) :
i = 1
while i * i <= n :
j = 1
while(j * j <= n) :
while (i * i + j * j == n) :
return (j,i)
j = j + 1
i = i + 1
def平方和(n):
i=1
而i*idef平方和(n):
范围=四舍五入(数学sqrt(n))
i=1
而i=圆形(范围/2)):
而(i*i+j*j==n):
返回(j,i)
j=j-1
i=i+1
您可以使用动态编程,以防止反复计算正方形。通过跟踪结果列表,您只需查找值即可。如果尚未定义该值,则只需计算一次,如果之前已计算过,则只需返回列表中的相应条目,而不是重新计算:
squares = []
def get_square(x):
global squares
if(squares[x] == -1):
squares[x] = x * x
return squares[x]
def sum_of_squares(n):
global squares
squares = [-1 for i in range(n)]
i = 1
while get_square(i) <= n - 1:
j = 1
while(get_square(j) <= n - 1) :
if(get_square(i) + get_square(j) == n) :
return (j,i)
j = j + 1
i = i + 1
print(sum_of_squares(25))
squares=[]
def get_平方(x):
全球广场
如果(平方[x]=-1):
正方形[x]=x*x
返回方块[x]
定义平方和(n):
全球广场
平方=[-1表示范围内的i(n)]
i=1
而得到_平方(i)算法的一种改进
方法1——将方块保存在字典中,以便以后查找
方法2——使用二进制搜索计算整数平方根
方法3——使用
math.sqrt函数
方法1
该技术基本上是通过从0到sqrt(n)的数字循环,将平方i*i存储为字典键,i作为值
如果数字(n-i*i)已经在字典中,那么您已经找到一对(i&d[n-i*i])
返回具有最大元组值的解决方案
算法复杂度O(sqrt(n))
方法2
def sum_square_binary(n):
def binary_search(start_, end_, val):
# If lower limit exceeds
# upper limit.
if start_ > end_:
return None
# Calculating mid.
mid = start_ + (end_ - start_) // 2
if mid * mid == val:
return mid
if mid * mid > val:
return binary_search(start_, mid - 1, val)
return binary_search(mid + 1, end_, val)
maxi = None
for i in range(n):
if i*i > n:
break
b = n - i * i
# Use binary search to see if b is a perfect square
# only need to check range [0, b] in binary search
k = binary_search(0, b, b) # k is the root of b (if perfect square)
if k:
maxi = max(maxi, (i, k), key = max) if maxi else (i, k)
return (max(maxi), min(maxi)) if maxi else maxi
方法3
import math
def sum_square_sqrt(n):
def integer_sqrt(x):
""" Returns sqrt of integer if it is a perfect square.
Uses technique from Cook https://www.johndcook.com/blog/2008/11/17/fast-way-to-test-whether-a-number-is-a-square/ to reduce the number of times sqrt is called """
# Find the floating point value of
# square root of x.
h = x & 0xF
if h > 9:
return None
if ( h != 2 and h != 3 and h != 5 and h != 6 and h != 7 and h != 8 ):
sr = math.sqrt(x)
# If square root is an integer
return int(sr) if ((sr - math.floor(sr)) == 0) else None
else:
return None
maxi = None
for i in range(n):
if i*i > n:
break
b = n - i * i
# Use binary search to see if b is a perfect square
# only need to check range [0, b] in binary search
k = integer_sqrt(b) # k is the root of b (if perfect square)
if k:
maxi = max(maxi, (i, k), key = max) if maxi else (i, k)
return (max(maxi), min(maxi)) if maxi else maxi
性能
方法1使用字典的速度要快得多,方法3与之相当。
方法3应该是可行的,直到sqrt作为较大n的测试变得不准确
tst = [randint(1e6, 1e9) for _ in range(10)]
%timeit for k in tst: sum_square(k)
>>方法1:每个循环180 ms±14.7 ms(7次运行的平均值±标准偏差,每个循环1次)
>>方法2:4.04 s±97.1 ms/循环(7次运行的平均值±标准偏差,每个循环1次)
方法3:每个循环192 ms±5.66 ms(7次循环的平均值±标准偏差,每个循环10次)如上所述,防止每次迭代中计算平方是一个良好的开端。下面是一个使用for循环的简单实现:
def sum_of_squares(n):
lim = int(n**0.5)+1
for i in range(1, lim):
for j in range(1,i):
if(i**2 + j**2==n):
return (i,j)
有很多优化技术可以提到,这导致这个问题变得有点不确定。对不起,对于python来说,这是一个相当新的问题,可以提到什么样的优化技术?我想到了记忆。也许还可以使用sqrt
和floor
?一种更好的方法来查看代码的哪些部分实际需要优化的是使用timeit
或其他类型的profiling,您可以消除所有乘法。平方序列只是奇数整数的累积和,因为(i+1)**2==i**2+2*i+1
。在Python 3.8中,[平方:=(square+i)表示范围(1,n,2)]
,或者在Python的任何最新版本中,列表(itertools.acculate(范围(1,n,2),operator.add))
大家好,欢迎来到stackoverflow!如果您详细介绍一下您的解决方案,您的帖子将对OP更有意义。仅仅给出纯代码有点超出stackoverflow的范围。乘法非常便宜;哈希表查找要昂贵得多。需要是dict
,这样您就可以返回n-i*i
@o11c--我作弊并返回了两个正方形。我想我会把它变成一个dict并返回两个实际的数字。@o11c--谢谢,更新为使用字典来返回对而不是正方形。@DarrylG这是可行的,但我的函数要求是它希望返回的方式能够最大化较大的数字。例如integer 50,我希望它返回(7,1)而不是(5,5)@piethon——更新后它返回最大元组——因此在本例中返回(7,1)50。你的lim
方法实际上比原来的方法更糟糕。你减少j
的迭代次数减少了常数因子,但它仍然是二次复杂度。
tst = [randint(1e6, 1e9) for _ in range(10)]
%timeit for k in tst: sum_square(k)
%timeit for k in tst: sum_square_binary(k)
%timeit for k in tst: sum_square_sqrt(k)
def sum_of_squares(n):
lim = int(n**0.5)+1
for i in range(1, lim):
for j in range(1,i):
if(i**2 + j**2==n):
return (i,j)