简体   繁体   中英

How can I optimize this function to return the number of integers with unique digits (ie. no repeating digits) in a given range?

Given a list of two integers (ie. [1,20] or [10,10000]), I am trying to create a function that returns the number of integers in the range (inclusive) that do NOT have repeating digits (ie. 8, 9, 10 would count, but 11 would not). The code I've written here works fine when the input list is relatively small, but is less efficient when the range is quite large. How can I make this more efficient?

good_numbers = 0

current_count = list[0]

while current_count <= list[1]:
    list_of_digits = [int(i) for i in str(current_count)]
    if len(list_of_digits) == len(set(list_of_digits)):
        good_numbers += 1
    current_count += 1

print(good_numbers)

This works fine when the range is relatively small, but I receive a timeout error when the range is quite large.

Without changing the algorithm, here's a simple ~3x speed up

In [38]: def a(start, end): 
    ...:     g = 0 
    ...:     for i in range(start, end+1): 
    ...:         s = list(f"{i}") 
    ...:         if len(s) == len(set(s)): 
    ...:             g += 1 
    ...:     return g      
    ...:      

Results:

New 
In [35]: %timeit a(10, 10000)                                                                   
12.1 ms ± 147 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Yours :
In [37]: %timeit b([10, 10000])                                                                 
33.5 ms ± 402 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Edit:

For an überfaster solution, you'll need to play a little bit smarter.

For example: Basically you need to be able to find permutations of digits in between the start and the end boundaries.

Assume that you have ne boundaries. The operation would be simply calculating the sums of numbers of permutations of n digits, n -1 digits, n - 2 digits ...

For example, if i have 3 digits, then i need to have total sum of 9 + (9 * 9) + (9 * 9 * 8) (try this with my or your code with boundaries 1, 1000 :)

Where,

In [80]: %timeit 9 + 9 * 9 + 9 * 9 * 8                                                          
11.2 ns ± 0.13 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)

You'll need to come up with an algorithm such as

In [89]: from functools import reduce
In [90]: def c(start, end): 
    ...:     sl = len(list(f"{start}")) 
    ...:     el = len(list(f"{end}")) 
    ...:     t = 0 
    ...:     for i in range(sl, el): 
    ...:         t += reduce(lambda x, y: x * y, [9 - j for j in range(i - 1)], 9) 
    ...:     return t 

In [91]: %timeit c(10, 10000)                                                                   
7.93 µs ± 46.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

But this lacks the controls for the boundary values, where you'll need to check for possible values to be higher than the start and lower than the end.

As you can see, the last one is 1400x faster :D But lacks the necessary controls which you'll need to apply

You can drastically speed up your algorithm if you know that the

eg:
with 2 digits -> 10! / 8! * 9/10 = 81
with 3 digits -> 10! / 7! * 9/10 = 648
n = 1 is a special case (we can do 10 possible numbers with 1 digit if you include the zero)

Now you can calculate the amount of numbers with non repeated digits with n digits at most like this:
n = 1 -> 10
n = 2 -> 10 + 81 = 91
n = 3 -> 10 + 81 + 739 + 4536
...

Here is a simple function which calculates it:

from functools import reduce
from operator import mul

@lru_cache(maxsize=32)
def good_numbers(n):
    if n > 10:
        return good_numbers(10)
    if n == 1:
        return 10
    return good_numbers(n - 1) + 9 * reduce(mul, range(10 - n + 1, 10))


print([good_numbers(k) for k in range(1, 5)])

Output is:

[10, 91, 739, 5275]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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