Наивное решение, которое решает проблему и является достаточно общим для любого приложения, которое у вас может быть, это:
def combinations(words, length):
if length == 0:
return []
result = [[word] for word in words]
while length > 1:
new_result = []
for combo in result:
new_result.extend(combo + [word] for word in words)
result = new_result[:]
length -= 1
return result
По сути, это постепенно создает дерево в памяти всех комбинаций, а затем возвращает их. Однако он требует большой памяти и поэтому не подходит для крупномасштабных комбинаций.
Другое решение этой проблемы, действительно, заключается в использовании подсчета, но затем для преобразования сгенерированных чисел в список слов из списка слов. Для этого нам сначала понадобится функция (называемая number_to_list()
):
def number_to_list(number, words):
list_out = []
while number:
list_out = [number % len(words)] + list_out
number = number // len(words)
return [words[n] for n in list_out]
Фактически это система преобразования десятичных чисел в другие базы. Затем мы пишем функцию подсчета; это относительно просто и составит ядро приложения:
def combinations(words, length):
numbers = xrange(len(words)**length)
for number in numbers:
combo = number_to_list(number, words)
if len(combo) < length:
combo = [words[0]] * (length - len(combo)) + combo
yield combo
Это генератор Python; создание генератора позволяет использовать меньше оперативной памяти. После превращения числа в список слов нужно проделать небольшую работу; это потому, что эти списки будут нуждаться в заполнении, чтобы они были на запрошенной длине Это будет использовано так:
>>> list(combinations('01', 3))
[['0', '0', '0'], ['0', '0', '1'],
['0', '1', '0'], ['0', '1', '1'],
['1', '0', '0'], ['1', '0', '1'],
['1', '1', '0'], ['1', '1', '1']]
Как видите, вы получаете список списков. Каждый из этих подсписков содержит последовательность исходных слов; Затем вы можете сделать что-то вроде map(''.join, list(combinations('01', 3)))
, чтобы получить следующий результат:
['000', '001', '010', '011', '100', '101', '110', '111']
Вы можете записать это на диск; однако, лучшей идеей было бы использовать встроенные оптимизации, которые есть у генераторов, и сделать что-то вроде этого:
fileout = open('filename.txt', 'w')
fileout.writelines(
''.join(combo) for combo in combinations('01', 3))
fileout.close()
При этом будет использоваться только столько оперативной памяти, сколько необходимо (достаточно для хранения одной комбинации). Надеюсь, это поможет.