Поскольку у вас есть алфавит длиной 4 (или любой "степень целого числа 2" ), идея использования целочисленного идентификатора и побитовых операций приходит на ум вместо проверки последовательныхсимволы в строках.Мы можем присвоить целое значение каждому из символов в alphabet
, для простоты давайте используем индекс, соответствующий каждой букве.
Пример:
65463543
10 = 3321232103313
4 = 'aaaddcbcdcbaddbd'
Следующая функция преобразует целое число из 10 в слово, используя alphabet
.
def id_to_word(word_id, word_len):
word = ''
while word_id:
rem = word_id & 0x3 # 2 bits pet letter
word = ALPHABET[rem] + word
word_id >>= 2 # Bit shift to the next letter
return '{2:{0}>{1}}'.format(ALPHABET[0], word_len, word)
Теперь дляфункция для проверки того, является ли слово «хорошим» на основе целочисленного идентификатора.Следующий метод имеет формат, аналогичный id_to_word
, за исключением того, что счетчик используется для отслеживания последовательных символов.Функция вернет False
, если будет превышено максимальное количество идентичных последовательных символов, в противном случае она вернет True
.
def check_word(word_id, max_consecutive):
consecutive = 0
previous = None
while word_id:
rem = word_id & 0x3
if rem != previous:
consecutive = 0
consecutive += 1
if consecutive == max_consecutive + 1:
return False
word_id >>= 2
previous = rem
return True
Мы фактически рассматриваем каждое слово как целое число с основанием 4. Еслидлина алфавита не была значением "степень 2" , тогда по модулю % alpha_len
и целочисленное деление // alpha_len
можно было бы использовать вместо & log2(alpha_len)
и >> log2(alpha_len)
соответственно, хотя для этого потребовалось бы многобольше.
Наконец , найти все хорошие слова для данного word_len
.Преимущество использования диапазона целочисленных значений заключается в том, что вы можете уменьшить количество for-loop
с в вашем коде с word_len
до 2
, хотя внешний цикл очень велик.Это может позволить более дружественную многопроцессорную работу по поиску хорошего слова.Я также добавил в быстрый расчет, чтобы определить наименьшие и наибольшие идентификаторы, соответствующие хорошим словам, что помогает значительно сузить поиск хороших слов
ALPHABET = ('a', 'b', 'c', 'd')
def find_good_words(word_len):
max_consecutive = 3
alpha_len = len(ALPHABET)
# Determine the words corresponding to the smallest and largest ids
smallest_word = '' # aaabaaabaaabaaab
largest_word = '' # dddcdddcdddcdddc
for i in range(word_len):
if (i + 1) % (max_consecutive + 1):
smallest_word = ALPHABET[0] + smallest_word
largest_word = ALPHABET[-1] + largest_word
else:
smallest_word = ALPHABET[1] + smallest_word
largest_word = ALPHABET[-2] + largest_word
# Determine the integer ids of said words
trans_table = str.maketrans({c: str(i) for i, c in enumerate(ALPHABET)})
smallest_id = int(smallest_word.translate(trans_table), alpha_len) # 1077952576
largest_id = int(largest_word.translate(trans_table), alpha_len) # 3217014720
# Find and store the id's of "good" words
counter = 0
goodies = []
for i in range(smallest_id, largest_id + 1):
if check_word(i, max_consecutive):
goodies.append(i)
counter += 1
В этом цикле я специально сохранил идентификатор слова какВ отличие от самого слова, если вы собираетесь использовать слова для дальнейшей обработки.Однако, если вы используете только слова, измените вторую на последнюю строку, чтобы прочитать goodies.append(id_to_word(i, word_len))
.
ПРИМЕЧАНИЕ: Я получаю MemoryError
при попытке сохранить все хорошие идентификаторы для word_len >= 14
.Я предлагаю записать эти идентификаторы / слова в какой-нибудь файл!