2
\$\begingroup\$

I'm trying to optimize my solution to this LeetCode problem:

Write a program to find the nth super ugly number.

Super ugly numbers are positive numbers whose all prime factors are in the given prime list primes of size k.

My working, accepted solution:

import heapq
class Solution:
 def nthSuperUglyNumber(self, n: int, primes: List[int]) -> int:
 heap = [1]
 seen = set()
 while n>=0:
 num = heapq.heappop(heap)
 n -=1
 if n == 0:
 return num 
 for p in primes:
 if num * p not in seen:
 seen.add(num*p)
 heapq.heappush(heap, num*p)
 return heapq.heappop(heap)

What are some ways to optimize it in terms of time and space complexity?

Toby Speight
87.3k14 gold badges104 silver badges322 bronze badges
asked Sep 28, 2019 at 13:45
\$\endgroup\$
0

2 Answers 2

2
\$\begingroup\$

As already explained in this answer, there is no need to use a class with an instance method here. In addition, the convention for function names in Python is to use snake_case, not camelCase.

On the other hand, this template is given on the LeetCode submission template, so you are not free to change it.

What you can do is to make the required solution method a wrapper for a free function (which then is universally usable):

def nth_super_ugly_number(n: int, primes: List[int]) -> int:
 # ...
class Solution:
 def nthSuperUglyNumber(self, n: int, primes: List[int]) -> int:
 return nth_super_ugly_number(n, primes)

Generating the ugly numbers can be separated from counting them (until the nth ugly number is reached) if the former is done with a generator. The generator is now a reusable function as well, and you might want to add a docstring comment:

def super_ugly_numbers(primes: List[int]) -> int:
 """Generate super ugly numbers.
 Super ugly numbers are positive integers whose all prime factors are in
 the given prime list.
 """
 heap = [1]
 seen = set()
 while True:
 num = heapq.heappop(heap)
 yield num
 for p in primes:
 if num * p not in seen:
 seen.add(num * p)
 heapq.heappush(heap, num * p)
def nth_super_ugly_number(n: int, primes: List[int]) -> int:
 ugly_numbers = super_ugly_numbers(primes)
 for _ in range(n - 1):
 next(ugly_numbers)
 return next(ugly_numbers)

The last function can be simplified using itertools.islice:

import itertools
def nth_super_ugly_number(n: int, primes: List[int]) -> int:
 ugly_numbers = super_ugly_numbers(primes)
 return next(itertools.islice(ugly_numbers, n - 1, n))
answered Sep 29, 2019 at 6:32
\$\endgroup\$
1
\$\begingroup\$

Loop like a native

 while n>=0:
 n -=1

You don't actually use n, nor should you be manually decrementing it, so this can become

for _ in range(n+1):

Returns

I'm not clear on why you have two returns. return num will take effect on the last iteration of the loop, so how would the other return happen at all? Probably you should remove the if n == 0 condition and rework the place where heappop is called so that the for _ in range can completely control the loop.

Set operations

Try this out to see what performance impact it has:

Rather than

 for p in primes:
 if num * p not in seen:
 seen.add(num*p)
 heapq.heappush(heap, num*p)

try

to_add = {num*p for p in primes} - seen
seen |= to_add
for a in to_add:
 heapq.heappush(heap, a)
answered Sep 28, 2019 at 17:51
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.