1

So basically if i have an iteration like this in python Ive editted the question to include my full code

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. Is there another data structure i can use. Im just learning 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

Noble Eugene
  • 523
  • 1
  • 5
  • 15
  • for now, that consumes pretty nothing in memory – azro Feb 07 '22 at 19:47
  • Im attempting to solve a leetcode problem, where a loop becomes very large, And it runs out of memory, I tried it locally and my PC completely dried out. – Noble Eugene Feb 07 '22 at 19:48
  • Are you using Python 2? `range` returns a materialized list in Python 2 but an iterator in Python 3. If you're using Python 3, then you should not run out of memory. See https://stackoverflow.com/questions/44571718/python-3-range-vs-python-2-range and https://docs.python.org/3.0/whatsnew/3.0.html#views-and-iterators-instead-of-lists – Ankur Feb 07 '22 at 19:50
  • I'm guessing you are doing "something" other than `print()` inside the loop right... – JonSG Feb 07 '22 at 19:51
  • 1
    Yes i am using a function other than print. I Should have mentioned that earlier. I am appending values to a list – Noble Eugene Feb 07 '22 at 19:52
  • This question is too open ended. We need to know what you're doing in the loop to make judgements as to whether or not optimizations can be made. If printing is all you're doing, there really isn't much you can do. Post your code. – Visual Studio Feb 07 '22 at 19:53
  • Are you adding the results to a list after calling your function? perhaps if you explained a little more we would be able to help. Given that `range()` is lazy, and `print()` does mostly nothing, other than the latency of screen refresh this is most free no matter how large a range you pick – JonSG Feb 07 '22 at 19:54
  • 1
    Ive saved my edits to include my full code now – Noble Eugene Feb 07 '22 at 19:57
  • WHY are you even using a list at all? You never access anything other than the last value, that could simply be kept in a variable. – jasonharper Feb 07 '22 at 19:58
  • I thought of that but i didnt know how to store it in a variable without overwriting before its use in the next iteration – Noble Eugene Feb 07 '22 at 20:01
  • There is no need to maintain a list for this, you can use the current value in your iteration – JonSG Feb 07 '22 at 20:03
  • why in the world are you keeping all of your intermediate results? – kindall Feb 07 '22 at 20:09
  • 1
    Ive discared intermediate results now @kindall. See new edit – Noble Eugene Feb 07 '22 at 20:10
  • How long are you given to find x = 0.00001 and n = 2147483647? – JonSG Feb 07 '22 at 20:18
  • @JonSG I dont think leetcode specifies the time i have. The only warn when my algorithm is inefficient. That is why im requesting for any more efficient data structures i could use – Noble Eugene Feb 07 '22 at 20:20
  • You don't need any data structure here. – Kelly Bundy Feb 07 '22 at 20:25
  • Ohhh.. well how can i make it faster @KellyBundy – Noble Eugene Feb 07 '22 at 20:28
  • Exponentiation by squaring, as probably shown by *thousands* of people in the discussion at the LeetCode problem (because people there keep spamming their worthless junk regardless of how terrible it is and regardless of how often it has been spammed by others already). – Kelly Bundy Feb 07 '22 at 20:31

4 Answers4

1

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. 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.

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.

user1984
  • 5,990
  • 2
  • 13
  • 32
  • Could you please explain the modulus conditional a little more? – Noble Eugene Feb 07 '22 at 21:26
  • 1
    Sure, that's how we know whether the exponent `n` is odd or even. If it's even, it's business as usual since we can integer divide it by 2. But when it's odd, we need to multiply the result one more time by `x` to take care of that. Consider `2^5 = 2^3 * 2^2 = 2^2 * 2 * 2^2`. That extra `2` in the middle is what we do when the exponent is odd. Feel free to ask if the idea doesn't click. – user1984 Feb 07 '22 at 21:42
  • Ok thanks a bunch. I would do well to study up recursive algorithms – Noble Eugene Feb 07 '22 at 21:47
0

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. If n was odd, multiply one time more with 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
trincot
  • 317,000
  • 35
  • 244
  • 286
0

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. 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.

Similarly we only need to calcuate:

2 * 2 = 4

once and use it twice to get 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
JonSG
  • 10,542
  • 2
  • 25
  • 36
-1

If you have to loop, you have to lope and there is nothing that can be done. Loops in python are slow. 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). 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.

William
  • 324
  • 1
  • 8