Home > Mobile >  My code passes validation, but runs very slowly
My code passes validation, but runs very slowly

Time:05-18

def is_password_good(password):
    if len(txt) > 7 and [i for i in txt if i.isupper()] and [i for i in txt if i.islower() and [i for i in txt if i.isnumeric()]]:
        return True
    else:
        return False

txt = input()

print(is_password_good(txt))

CodePudding user response:

If you're looking for a capital letter, using the method you're using, every letter is going to be evaluated. So if you have 50 letters in your string, and you're using the list comprehension method, you're going to do 50 things per list. And in your case, there are three list comprehensions, so there will be len(txt) * 3 evaluations made everytime.

Using your method, modified with the password inline to make the timeit call simpler:

import timeit

def is_password_good():
    txt = "Sup3rlongpassword"
    if (
        len(txt) > 7
        and [i for i in txt if i.isupper()]
        and [i for i in txt if i.islower() and [i for i in txt if i.isnumeric()]]
    ):
        return True
    else:
        return False

>>> timeit.timeit(is_password_good)
17.099690434522927

Here's a method using regular expressions. In each case, as soon as the passing condition is met, it will return the value.

import re 

has_upper = re.compile('[A-Z]').search
has_lower = re.compile('[a-z]').search
has_digit = re.compile('[0-9]').search    

def is_password_good_re():
    txt = "Sup3rlongpassword"
    if len(txt) > 7 and has_upper(txt) and has_lower(txt) and has_digit(txt):
        return True
    else:
        return False

>>> timeit.timeit(is_password_good_re)
0.8171015260741115

Edit:

Something else that stands out, one of your lists is nested within the other.

[i for i in txt if i.islower() and [i for i in txt if i.isnumeric()]]

So this means the numeric check is happening every character. So if there are 10 characters in the password, there will be 10 checks for numbers, which involve 10 checks each, for a total of 100 evaluations for numbers alone.

CodePudding user response:

You're running three for loops, one after the other, when you could run a single for loop:

def is_password_good(password, good_length):
    if len(password) < good_length:
        return False

    has_upper, has_lower, has_digit = False, False, False
    for char in password:
        has_upper = char.isupper() or has_upper
        has_lower = char.islower() or has_lower
        has_digit = char.isdigit() or has_digit
        if has_upper and has_lower and has_digit:
            return True
    return False

Imagine a password of all lowercase letters -- you have to check every one to see if you also get a digit or uppercase letter, therefore you will iterate over all n characters before you can say definitively that the password does not satisfy the requirements. This means that in the worst-case scenario, the best you can possibly do is O(n).

The idea then should be to break out of the loop as soon as possible -- in this instance, once all three flags have been switched to True.

Each of the three char.isxxx() or has_xxx statements will be True forever once the first time the check is satisfied, since after the first time it is True, the statement reduces to char.isxxx() or True which will always be true.

Once all three flags are set, you can break and then return whether good_length is satisfied as well.

CodePudding user response:

You can use an check your conditions in a for loop:

def is_password_good(password):
    if len(password) > 7:
        up, lo, nu = False, False, False
        for i in password:
            if i.isupper():
                up = True
            elif i.islower():
                lo = True
            elif i.isnumeric():
                nu = True
    else:
        return False

    return all((up, lo, nu))
>>> is_password_good('1234567')
False
>>> is_password_good('12345678')
False
>>> is_password_good('123456aa')
False
>>> is_password_good('123456aB')
True
  • Related