0

I have a string that holds numbers. I want to check if the string has all the numbers between 0-9. Currently, the way I am checking is pretty slow and it will definitely be useless for large strings. Here is my code below:

import sys

# check if all numbers (0-9) exist in a string
num = "31586055033755830765"
for i in num:
    if int(i) not in [0, 1, 2 ,3 ,4 ,5 ,6, 7, 8, 9]:
        print("The string doesn't have all the numbers")
        sys.exit(1)

The code works fine but is pretty slow. Is there a faster way to accomplish the task?

user2314737
  • 21,279
  • 16
  • 81
  • 95
Souvik Ray
  • 2,295
  • 2
  • 16
  • 42
  • This one from yesterday is a dupe: https://stackoverflow.com/questions/48464062/regex-to-find-if-a-string-contains-all-numbers-0-9-be-it-in-any-order – pault Jan 27 '18 at 16:30

6 Answers6

7

Several reasons for your code's bad performance:

  • It creates a new list ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) every iteration.
  • in on lists is quite expensive (O(n)). Instead of in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] prefer in {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}. in on a set is much cheaper (O(1)).
  • It converts every character in num to an integer (a function call + the time the conversion itself takes). Instead you can compare the digits as strings:

    if i not in {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
    

These changes will improve the performance of your code, but you can use an entirely different, shorter and much faster approach by using only sets:

import string

num = '31586055033755830765'

print(set(num) == set(string.digits))
# False

print(set('1234567890') == set(string.digits))
# True
DeepSpace
  • 65,330
  • 8
  • 79
  • 117
2

This is how I would do it:

def check_num(num):
    not_found="0123456789"
    for i in num:
        if i in not_found:
            not_found = not_found.replace(i, '')
            if not not_found:
                break
    return not_found

Run

num = "31586055033755830765"

print(bool(not check_num(num))) # False

This code examines one digit at a time and exits the loop if all digits are found.

Running time is in the order of magnitude of the other answer:

import string
import random
num=str(random.getrandbits(256))

In [35]: %timeit set(num) == set(string.digits)
100000 loops, best of 3: 9.89 µs per loop

In [36]: %timeit bool(not check_num(num))
100000 loops, best of 3: 14.9 µs per loop

For very large numbers in the average case (when the number contains uniformly distributed digits) it's slightly better

In [47]: num=str(random.getrandbits(2048))

In [48]: %timeit bool(not check_num(num))
100000 loops, best of 3: 15.8 µs per loop

In [49]: %timeit set(num) == set(string.digits)
10000 loops, best of 3: 37.2 µs per loop
user2314737
  • 21,279
  • 16
  • 81
  • 95
1

You can also use all() here:

>>> from string import digits
>>> numbers = set(digits)
>>> num = '31586055033755830765'
>>> all(x in numbers for x in num)
True
RoadRunner
  • 23,173
  • 5
  • 28
  • 59
0

You can use hashing. Assuming your string has only numbers

num = "31586055033755830765"
temp = dict()
for i in num:
    if not i in a:
       a[i] = True
if (len(a)!=10):
    sys.exit(1)
Umang Gupta
  • 9,646
  • 5
  • 33
  • 56
0

A version if your string also contains strings different from numbers:

import string

num = 'abc0123456789'

print(set(num) == set(string.digits))
# False

all_numbers = set(string.digits)
found = False
for n in num:
    all_numbers.discard(n)
    if not all_numbers:
        found = True
        break
print(found)
# True
Mike Müller
  • 71,943
  • 15
  • 139
  • 140
-1

You can try this:

test='1234567890'
print(any(list(filter(lambda x:x not in "31586055033755830765" ,test))))

if result is True it means all int are not in second and if result is false then it means all are in string.

output:

True

because

['2', '4', '9'] are not in "31586055033755830765"