Рубрики
Без рубрики

Brute-Perging NPR в воскресенье головоломки

Очень нужна ли воскресная головоломка NPR NPR, или просто способность кодировать? Они один и то же самое? Теги с Python, OpenLibrary, NPR.

Пару недель назад я смотрел на NPR воскресенье головоломки и мозговой штурм ответа с моей девушкой. Головоломка:

Назовите город У.с. и его государство – 12 букв в целом. Измените две буквы во имя состояния. Результатом будет название из двух слов классического романа. Что это?

Через некоторое время просто пытаясь перечислить все книги, которые мы знали, мы что-то гуглили вдоль линий «Список классических книг» и проходив через списки, проверяя, соответствует ли кто-нибудь из них критериями. Через несколько минут я что-то понял – мы просто были скользиты через головоломку. Так почему я не мог просто автоматизировать это?

Что это Делает

Я написал простой скрипт, который итерации по поводу списка книг и проверяет, соответствует ли он критериям, данным головоломкой. Я поставил данные о названиях книги из OpenLibrary , который обеспечивает Данные дампы На всю информацию в их каталоге в любой момент времени. Сам скрипт довольно прост. Я написал функцию, которая принимает слово и возвращает любое состояние, которое ровно два персонажа от него

# Define a function that takes a word and checks to see if it's two characters away from a state
# If it is, it returns the state
# If it's not, it returns False
def word_to_state(word):
    # Make a list of every state
    states = ['Alaska', 'Alabama', 'Arkansas', 'American Samoa', 'Arizona', 'California', 'Colorado', 'Connecticut', 'District of Columbia', 'Delaware', 'Florida', 'Georgia', 'Guam', 'Hawaii', 'Iowa', 'Idaho', 'Illinois', 'Indiana', 'Kansas', 'Kentucky', 'Louisiana', 'Massachusetts', 'Maryland', 'Maine', 'Michigan', 'Minnesota', 'Missouri', 'Northern Mariana Islands', 'Mississippi', 'Montana', 'National', 'North Carolina', 'North Dakota', 'Nebraska', 'New Hampshire', 'New Jersey', 'New Mexico', 'Nevada', 'New York', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Puerto Rico', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Virginia', 'Virgin Islands', 'Vermont', 'Washington', 'Wisconsin', 'West Virginia', 'Wyoming']
    # Iterate over every state to check it against the word
    for state in states:
        # If the length of the state is the same as the length of the word...
        if len(state) == len(word):
            # Convert the word and the state to all lowercase to make it easier to compare them
            word, state = word.lower(), state.lower()

            # Create a counter to check the amount of changes that you make to the word
            changes = 0

            # Iterate over the index and character in each state (Texas -> (0, T) (1, E) (2, X) (3, A), (4, S))
            for index, character in enumerate(state):

                # Check to see if the letter in the state is the same as the letter in that position of the word
                if character == word[index]:
                    # If it is, you don't need to do anything! Keep going
                    pass
                else:
                    # If it's not, increment the amount of changes we need to turn the state into the word
                    changes += 1

            # After we're done checking to see how many changes it takes to turn a word 
            # into a state, we check to see if it's greater than 2
            if changes > 2:
                # If it is, we continue on to the next state
                continue
            else:
                # If it's not, this is the state we're looking for - we return the state
                # The function will end here if a state can be found
                return state

    # If you make it here, no state was found - return False
    return False

После написания этой функции остальные были простыми. Мы просто повторяем наш список книг из OpenLibrary, проверьте, если каждая книга встречает начальные (негосударственные) критерии, затем пропустите состояние через нашу функцию и вернуть результаты.

# Iterate over every book in the file
for book in books:
    # Figure out how many words are in the books title by splitting the title 
    words_in_book_title = book.split(' ')
    if len(words_in_book_title) == 2:
        # Find the amount of characters in the book title
        characters_in_book_title = ''.join([letter for letter in book if letter.isalpha()]) # 'Infinite Jest' -> 'InfiniteJest'
        if len(characters_in_book_title) == 12:
            # Get the second word in the book title, pass it to word_to_state
            second_word = book.split(' ')[1]
            # word_to_state('TexZZ') -> 'Texas'
            # word_to_state('gibberish1029831') -> False
            if word_to_state(second_word):
                # Print the title of the book and the state if there's a match
                print(book, word_to_state(second_word))

После очистки OpenLibrary Dump немного дамка, я использовал его в качестве ввода для скрипта. Через несколько секунд результаты были в! Возможные названия были:

Vergine Madre Maine
Painful Tears Texas
Inuit Indians Indiana
Settling Down Iowa
Savage Dragon Oregon
Problem Texts Texas
...(1259 more lines)

Более 1200 матчей? Я никого не было, я собирался разбирать все это вручную.

Как мы можем обрезать это?

Это было отличное начало, но нам нужно было обрезать это до использования подходящего количества книг. Для этого я использовал отличную библиотеку Python Pyzipcode , который позволил мне посмотреть в городах и штатах по почтовому индексу. Если у City/State Combo не имеет почтового индекса, это не ответ. Я сохранил названия книги в текстовый файл и напишите новый скрипт:

for book in potential_books:
    # Book format: "Painful Tears Texas"
    city, state = book.split(' ')[0], book.split(' ')[2]
    results = zcdb.find_zip(city=city, state=state)
    if results is not None:
        print(title + ' - ' + city + ', ' + state)

Это привело к этому (гораздо более разумному) выводу:

Miami Indians - Miami, Indiana
Lake Michigan - Lake, Michigan
Raymond Hains - Raymond, Maine
Moon Virginia - Moon, Virginia
Joseph Crugon - Joseph, Oregon
Eugene Onegin - Eugene, Oregon
Richmond Whig - Richmond, Ohio
Columbus Ohio - Columbus, Ohio
Garrison Town - Garrison, Iowa
Joseph Gregor - Joseph, Oregon

Немного Google-Fu (просто чтобы подтвердить, какой заголовок был классической книгой) позже, и у нас был ответ – Евгений Онегин , кусок классического российского грамота, опубликованного в последовательной форме между 1825 и 1832 годами. Онегин – это только две буквы от штата Орегон. Мы сделали это!

После подачи нашего ответа мы терпеливо ждали до следующей недели для результатов. Мы были правы Действительно Хотя наше письмо не было выбрано в качестве выигрыша ответа, поздравляем о Роб Харди Дейтона, Огайо, чтобы получить его – вы, вероятно, заслужили его больше;)

Что дальше

Давайте будем настоящим, word_to_state не совсем эффективная функция. Я сделал это как можно более подробно, чтобы попытаться помочь моей девушке, которые учатся в курсе. Вот быстрое улучшение:

def word_to_state(word):
    states = ['Alaska', 'Alabama', 'Arkansas', 'American Samoa', 'Arizona', 'California', 'Colorado', 'Connecticut', 'District of Columbia', 'Delaware', 'Florida', 'Georgia', 'Guam', 'Hawaii', 'Iowa', 'Idaho', 'Illinois', 'Indiana', 'Kansas', 'Kentucky', 'Louisiana', 'Massachusetts', 'Maryland', 'Maine', 'Michigan', 'Minnesota', 'Missouri', 'Northern Mariana Islands', 'Mississippi', 'Montana', 'National', 'North Carolina', 'North Dakota', 'Nebraska', 'New Hampshire', 'New Jersey', 'New Mexico', 'Nevada', 'New York', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Puerto Rico', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Virginia', 'Virgin Islands', 'Vermont', 'Washington', 'Wisconsin', 'West Virginia', 'Wyoming']
    for state in states:
        if len(word) == len(state):
            word, state = word.lower(), state.lower()
            changed_characters = [letter for index, letter in enumerate(word) if state[index] != letter]
            if len(changed_characters) == 2:
                return state.title()
    return False

Я изначально думал, что набор сравнений будет способ пойти, но для этой проблемы Заказ букв имеет значение Отказ Задача ответа Onegin будет не удалось использовать набор сравнений, потому что мы меняем букву «R» в букву, которое уже в Word «Oregon». Хотя определенно улучшения должны быть сделаны, это еще на один день. При ретроспективе моя начальная функция даже не справилась с этим правильно – просто посмотрите на некоторые из вывода! К счастью, я работал над такими маленькими масштабами, это не имело большого значения.

Это было изначально опубликовано на мой блог – Давай тусоваться!

Оригинал: “https://dev.to/mobbsdev/brute-forcing-the-npr-sunday-puzzle”