Пару недель назад я смотрел на 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”