简体   繁体   English

降低简单循环的时间/空间复杂度

[英]Reduce time /space complexity of simple loop

So basically if i have an iteration like this in python Ive editted the question to include my full code所以基本上,如果我在 python 中有这样的迭代,我已经编辑了问题以包含我的完整代码

class Solution:
    def myPow(self, x: float, n: int) -> float:
        temp = [];
        span = range(1,abs(n))
        if n ==0:
            return 1
        if abs(n)==1:
            temp.append(x)
        else:
            for y in span:
                if y == 1:
                    temp = []
                    temp.append(x*x)
                else:
                    temp.append(temp[-1] * x)
        if(n < 0):
            return 1/temp[-1]
        else:
            return temp[-1]

The problem link is: Pow(x,n)-leetcode How can I modify this to conserve memory and time.问题链接是: Pow(x,n)-leetcode我如何修改它以节省 memory 和时间。 Is there another data structure i can use.我可以使用另一种数据结构吗? Im just learning python....我正在学习 python....

------------EDIT------------ ive modified the code to use a variable instead of a list for the temp data ----------编辑------------ 我修改了代码以使用变量而不是临时数据列表

class Solution:
    def myPow(self, x: float, n: int) -> float:
        span = range(1,abs(n))
        if n ==0:
            return 1
        if abs(n)==1:
            temp = x
        else:
            for y in span:
                if y == 1:
                    temp = x*x
                else:
                    temp = temp * x
        if(n < 0):
            return 1/temp
        else:
            return temp

I still have a problem with my time complexity.我的时间复杂度仍然有问题。 Its working for many testcases, however when it trys to run with x = 0.00001 and n = 2147483647. The time limit issue arises它适用于许多测试用例,但是当它尝试以 x = 0.00001 和 n = 2147483647 运行时。出现时间限制问题

To reduce the time complexity you can divide the work each time by taking x to the power of 2 and dividing the exponent by two.为了降低时间复杂度,您可以通过取x的 2 次方并将指数除以 2 来划分每次的工作。 This makes a logarithmic time algorithm since the exponent is halved at each step.这形成了一个对数时间算法,因为指数在每一步都减半。

Consider the following examples:考虑以下示例:

10^8 = 10^(2*4) = (10^2)^4 = (10*10)^4

Now, there is one edge case.现在,有一种极端情况。 When the exponent is an odd number you can't integer divide it by 2. So in that case you need to multiply the results by the base one additional time.当指数为奇数时,您不能用 integer 将其除以 2。因此,在这种情况下,您需要将结果再乘以底数一次。

The following is a direct recursive implementation of the above idea:下面是上述思路的直接递归实现:

class Solution:
    def myPow(self, x: float, n: int) -> float:
        sign = -1 if n < 0 else 1
        n = abs(n)
        
        def helper(x, n):
            if n == 1: return x
            if n == 0: return 1
            
            if n % 2 == 1:
                return helper(x*x, n // 2) * x
            else:
                return helper(x*x, n // 2)
        
        res = helper(x, n)
        if sign == -1:
            return 1/res
        else:
            return res

Note that we have taken abs of the exponent and stored the sign and deal with it at the end.请注意,我们已经获取了指数的abs并存储了符号并在最后处理它。

Instead of iterating from 1 to n , use divide-and-conquer: divide the exponent by 2 and use recursion to get that power, and then square that result.不要从 1 迭代到n ,而是使用分而治之:将指数除以 2 并使用递归来获得该幂,然后将该结果平方。 If n was odd, multiply one time more with x :如果n是奇数,则再乘以x一次:

class Solution:
    def myPow(self, x: float, n: int) -> float:
        if n == 0:
            return 1
        if n == 1:
            return x
        if n < 0:
            return self.myPow(1/x, -n)
        temp = self.myPow(x, n // 2)
        temp *= temp
        if n % 2:
            temp *= x
        return temp

A simple naive solution might be:一个简单天真的解决方案可能是:

def myPow(x: float, n: int) -> float:
    ## -----------------------
    ## if we have a negative n then invert x and take the absolute value of n
    ## -----------------------
    if n < 0:
        x = 1/x
        n = -n
    ## -----------------------

    retval = 1
    for _ in range(n):
        retval *= x
    return retval

While this technically works, you will wait until the cows come home to get a result for:虽然这在技术上可行,但您将等到奶牛回家才能获得以下结果:

x = 0.00001 and n = 2147483647

So we need to find a shortcut.所以我们需要找到一条捷径。 Lets' consider 2^5.让我们考虑 2^5。 Our naïve method would calculate that as:我们的天真方法会将其计算为:

(((2 * 2) * 2) * 2) * 2 == 32

However, what might we observe about the problem if we group some stuff together in a different way:但是,如果我们以不同的方式将一些东西组合在一起,我们可能会观察到这个问题:

(2 * 2) * (2 * 2) * 2 == 32

similarly:相似地:

((2 * 2) * (2 * 2) * 2) * ((2 * 2) * (2 * 2) * 2) == 32 * 32 = 1024

We might observe that we only technically need to calculate我们可能会观察到我们在技术上只需要计算

(2 * 2) * (2 * 2) * 2 == 32

once and use it twice to get 2^10.一次并使用它两次得到 2^10。

Similarly we only need to calcuate:同样我们只需要计算:

2 * 2 = 4

once and use it twice to get 2^5....一次并使用它两次得到 2^5....

This suggests a recursion to me.这向我暗示了递归。

Let's modify our first attempt to use this divide and concur method.让我们修改我们第一次尝试使用这种分而治之的方法。

def myPow2(x: float, n: int) -> float:
    ## -----------------------
    ## if we have a negative n then invert x and take the absolute value of n
    ## -----------------------
    if n < 0:
        x = 1/x
        n = -n
    ## -----------------------

    ## -----------------------
    ## We only need to calculate approximately half the work and use it twice
    ## at any step.
    ## -----------------------
    def _recurse(x, n):
        if n == 0:
            return 1
        res = _recurse(x, n//2) # calculate it once
        res = res * res # use it twice
        return res * x if n % 2 else res # if n is odd, multiple by x one more time (see 2^5 above)
    ## -----------------------

    return _recurse(x, n)

Now let's try:现在让我们试试:

print(myPow2(2.0, 0))
print(myPow2(2.0, 1))
print(myPow2(2.0, 5))
print(myPow2(2.1, 3))
print(myPow2(2.0, -2))
print(myPow2(0.00001, 2147483647))

That gives me:这给了我:

1
2.0
32.0
9.261000000000001
0.25
0.0

If you have to loop, you have to lope and there is nothing that can be done.如果你必须循环,你就必须大步跑,没有什么可以做的。 Loops in python are slow. python 中的循环很慢。 That said you may not have to loop and if you do have to loop, it may be possible to push this loop to a highly optimised internal function. Tell us what you are trying to do (not how you think you have to do it, appending elements to a lis may or may not be needed).那就是说您可能不必循环,如果您确实必须循环,则可以将此循环推送到高度优化的内部 function。告诉我们您正在尝试做什么(而不是您认为必须这样做,将元素附加到 lis 可能需要也可能不需要)。 Always recall the two rules of program optimisation General Rule: Don't do it.永远记住程序优化的两条规则 一般规则:不要这样做。 Rule for experts: Don't do it yet.专家规则:先不要做。 Make it work before you make it fast, who knows, it may be fast enough.在让它变快之前让它工作,谁知道呢,它可能已经足够快了。

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

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