优化用于计算平方除数列表的python代码

时间:2017-06-03 07:28:53

标签: python arrays algorithm

我参与了codewars网站的python挑战。我遇到了以下挑战:

42的除数是:1,2,3,6,7,14,21,42。这些除数的平方是:1,4,9,36,49,196,441,1764。平方的总和除数是2500,即50 * 50,一个正方形!

给定两个整数m,n(1 <= m <= n)我们想要找到m和n之间的所有整数,其平方除数之和本身就是一个平方。 42就是这样的数字。

结果将是一个数组数组,每个子数组有两个元素,首先是平方除数为平方的数字,然后是平方除数的总和。

输出应为:

list_squared(1, 250) --> [[1, 1], [42, 2500], [246, 84100]]
list_squared(42, 250) --> [[42, 2500], [246, 84100]]
list_squared(250, 500) --> [[287, 84100]]

我已经编写了以下代码,其中包含两个附加功能:一个用于确定数字的所有因子,另一个用于检查数字是否为完美正方形。

确定所有因素的功能:

def fact(m):
    return [i for i in range(1,m+1) if m%i == 0]

检查数字是否为完美正方形的函数,如果否则返回平方根

则返回0
def square_root(x):
    ans = 0
    while ans < x // 2 + 1:
        ans = ans + 1

        if ans*ans == x:
            return ans
            break;
    return 0

计算所需结果的函数

def list_squared(m, n):
    # your code
    fac=[]
    for i in range(m,n+1):
        sq_sum = sum(list(map(lambda x: x**2,fact(i))))
        if square_root(sq_sum) != 0:
            fac.append([i,sq_sum])
    return fac

这段代码给了我正确的结果,但是它太长了。我能够通过所有的测试结果,但它花了我大约6000毫秒。当我尝试提交代码时,Web提交返回该算法效率低下并且花费超过1200毫秒这是最大值。

如果有人能指出更好的算法,我将非常感激。

2 个答案:

答案 0 :(得分:2)

您的代码有几种优化,但最大的优化是在ans*ans大于x时停止:

def square_root(x):
    ans = 0
    while True:
        ans += 1
        sqans = ans*ans
        if sqans == x:
            return ans
        elif sqans > x:
            return 0

while中的条件可以删除,因为现在测试是在平方值上完成的。

通过该优化,我使用250, 500情况从8秒降至0.07秒。

但这并不令人满意。通常,包含中断或返回条件的算法至少为O(n),即使您可以节省时间,复杂性也会过高。

只需检查圆角平方根的平方即可做得更好:

def square_root(x):
    ans = int(x**0.5 + 0.5)  # rounded just in case it goes below the actual value (float inaccuracy)
    sqans = ans*ans
    return 0 if sqans !=x else x

我将执行时间再加2(由Optimized way to find if a number is a perfect square确认)

除此之外(这不会加速那么多,但值得一提):

无需在map中将list转换为sum

sq_sum = sum(map(lambda x: x**2,fact(i)))

同样fact可以避免循环到最大数量。循环到最大数字除以2并将最大数字添加到列表中是等效的。最大数量/ 2

以上不存在除数
def fact(m):
    return [i for i in range(1,m//2+1) if m%i == 0] + [m]

最终编辑:由于fact中使用的列表理解,这仍然很慢。我可以通过使用生成器来大幅缩短时间,并在其外部添加m*m

def sqfact(m):
    return (i*i for i in range(1,m//2+1) if m%i == 0)

最终代码,现在运行得如此之快,我得到0秒。

def sqfact(m):
    return (i*i for i in range(1,m//2+1) if m%i == 0)

def square_root(x):
    ans = int(x**0.5 + 0.5)
    return 0 if ans*ans !=x else x

def list_squared(m, n):
    # your code
    fac=[]
    for i in range(m,n+1):
        sq_sum = sum(sqfact(i)) + i*i  # add i square outside
        if square_root(sq_sum):
            fac.append([i,sq_sum])
    return fac

答案 1 :(得分:1)

我已经更新了效率非常低的事实函数。现在,不是迭代到m的完整值来找到它的因素,我只是上升到sqrt(m)。这极大地缩短了运行时间。这背后的逻辑是微不足道的,所以我没有详细说明。以下是适用于我的新代码。

def fact(m):

     #determining the lower factors i.e., smaller than sqrt(m)
    fac = [i for i in range(1, int(m**0.5) + 1) if m%i == 0]

     #determining the higher factors i.e., larger than sqrt(m)
    fac = fac + [m//i for i in range(1, int(m**0.5) + 1) if m%i == 0] 

    return sorted(list(set(fac))) #in order to get rid of duplicate factors