0
\$\begingroup\$

This takes a user inputted string, then tries to randomly 'guess' it. The problem is that whenever the input's length is 6 digits or more, it takes an incredibly long time to process it. Is there a more efficient way of doing this?

import random as rand
import string

def main(word):
    len_word = len(word)
    ntries = 0
    while True:
        letters = []
        for i in range(len_word):
            letters.append(rand.choice(string.ascii_lowercase))

        ntries += 1
        b = ''.join(letters)

        if b==word:
            print('Mission accomplished. It took ' + str(ntries) + ' tries.')
            break

if __name__ == '__main__':
    main(input('input a word: '))

Note: I have tried making the program guess the letters one by one, and then check if said letter was right, but it seemed to make the problem even worse.

\$\endgroup\$
  • 6
    \$\begingroup\$ How "efficient" could you expect random guessing to possibly be? What does it even mean to "optimize" a hopelessly lengthy task that is also kind of pointless? \$\endgroup\$ – 200_success Aug 24 at 6:04
3
\$\begingroup\$

Docstrings

Python documentation strings (or docstrings) provide a convenient way of associating documentation with Python modules, functions, classes, and methods. An object's docstring is defined by including a string constant as the first statement in the object's definition.

def guess_word():
    """Do calculations and return correct guessed word."""

Style

check pep0008 https://www.python.org/dev/peps/pep-0008/ the official Python style guide and here are some comments:

import string

def main(word):

Surround top-level function and class definitions with two blank lines.

ntries = 0
if b==word:
  • Use descriptive variable names, improves readability and general understanding of the code.
  • Always surround these binary operators with a single space on either side: assignment (=), augmented assignment (+=, -= etc.), comparisons (==, <, >, !=, <>, <=, >=, in, not in, is, is not), Booleans (and, or, not). Except when = is used to set a function parameter.

The code

len_word

len_word = len(word)
ntries = 0
while True:
    letters = []
    for i in range(len_word):

the variable len_word is unnecessary, can be expressed:

while True:
    letters = []
    for i in range(len(word)):

Random brute forcing:

a sample size of 6 out of 26 alphabets has 230230 possible combinations (where order is unimportant and each combination has n! re-orderings where n = combination length (letters in the word) what is very obvious is that with such algorithms, it is very likely that zombie dinosaurs start showing up before the code guesses the correct word (10 years to be able to guess a 10 letter word for 500,000 secret words/passwords examined per second). So if what you are trying to build is a password cracker, the algorithms that are better than brute forcing are beyond the scope of this review.

What your mini-program does is trying n digit random choices and comparing them to the secret word which number of trials is variable

input a word: red Mission accomplished. It took 8428 tries.

input a word: red Mission accomplished. It took 16894 tries.

and since it's a random choice(not entirely random because it's based on a so-called 'pseudo-random' algorithm, you might generate the same number of trials for a random.seed(n)) this is not the case however, it tries different possibilities, so with some bad luck, that with some word lengths might keep running until aliens start making documentaries about us.

Assuming we can check each character separately:

(** This is not the case of password crackers in the real world which must guess the exact word).

# Note: I have tried making the program guess the letters one by one, and then 
# check if said letter was right, but it seemed to make the problem even worse.

and since you asked about separate letter comparison:

import string


def guess_word():
    """Match characters with user input and return number of iterations, correct guess."""
    possible = string.ascii_lowercase + string.ascii_uppercase + string.digits + string.punctuation + ' '
    secret_word = input('Enter word: ')
    yield len(possible) * len(secret_word)
    for index in range(len(secret_word)):
        for char in possible:
            if secret_word[index] == char:
                yield char


if __name__ == '__main__':
    word = list(guess_word())
    print(f"Total: {word[0]} iterations.\nWord: {''.join(word[1:])}")
\$\endgroup\$
1
\$\begingroup\$

Random guessing will always take a long time. You can improve your code a bit if you dont append to a list and instead use random.choices.

import random
import string
def main(word):
    ntries = 0
    while True:
        ntries += 1
        b = "".join(random.choices(string.ascii_lowercase, k=len(word)))
        if b == word:
            print("Mission accomplished. It took " + str(ntries) + " tries.")
            break
\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.