Возможно, существует более быстрый способ использования нескольких различных трюков в numpy. numpy.indicies
- это то, где вы хотите начать. Это по существу эквивалент itertools.product
, как только вы объедините его с rollaxis
. Ответ Свена Марнаха in this question - отличный пример этого (в последнем примере есть небольшая ошибка, но это то, что вы хотите использовать. Это должно быть numpy.indices((len(some_list) + 1), * some_length...
)
Однако, для чего-то вроде этого, вероятно, будет более читаемый с использованием itertools.
numpy.fromiter
немного быстрее, чем прямое преобразование вещей в список, особенно если вы укажете ему количество элементов в итераторе. Главное преимущество заключается в том, что используется значительно меньше памяти, что может быть очень полезно в случае больших итераторов.
Вот некоторые примеры с использованием как numpy.indices
трюка и различными способами преобразования итератора в Numpy массива:
import itertools
import numpy as np
import scipy.special
def fixed_total_product(bins, num_items):
return itertools.ifilter(lambda combo: sum(combo) == num_items,
itertools.product(xrange(num_items + 1), repeat=bins))
def fixed_total_product_fromiter(bins, num_items):
size = scipy.special.binom(bins - 1 + num_items, num_items)
combinations = fixed_total_product(bins, num_items)
indicies = (idx for row in combinations for idx in row)
arr = np.fromiter(indicies, count=bins * int(size), dtype=np.int)
return arr.reshape(-1, bins)
def fixed_total_product_fromlist(bins, num_items):
return np.array(list(fixed_total_product(bins, num_items)), dtype=np.int)
def fixed_total_product_numpy(bins, num_items):
arr = np.rollaxis(np.indices((num_items + 1,) * bins), 0, bins + 1)
arr = arr.reshape(-1, bins)
arr = np.arange(num_items + 1)[arr]
sums = arr.sum(axis=1)
return arr[sums == num_items]
m, n = 5, 20
if __name__ == '__main__':
import timeit
list_time = timeit.timeit('fixed_total_product_fromlist(m, n)',
setup='from __main__ import fixed_total_product_fromlist, m, n',
number=1)
iter_time = timeit.timeit('fixed_total_product_fromiter(m, n)',
setup='from __main__ import fixed_total_product_fromiter, m, n',
number=1)
numpy_time = timeit.timeit('fixed_total_product_numpy(m, n)',
setup='from __main__ import fixed_total_product_numpy, m, n',
number=1)
print 'All combinations of {0} items drawn from a set of {1} items...'.format(m,n)
print ' Converting to a list and then an array: {0} sec'.format(list_time)
print ' Using fromiter: {0} sec'.format(iter_time)
print ' Using numpy.indices: {0} sec'.format(numpy_time)
Что касается времени:
All combinations of 5 items drawn from a set of 20 items...
Converting to a list and then an array: 2.75901389122 sec
Using fromiter: 2.10619592667 sec
Using numpy.indices: 1.44955015182 sec
Вы не заметите, что ни один из они особенно быстры.
Вы используете алгоритм грубой силы (генерируете все возможные комбинации, а затем фильтруете их), и я просто копирую его в примере на основе numpy.
Возможно, существует гораздо более эффективный способ сделать это! Тем не менее, я никоим образом не являюсь специалистом в области CS или математики, поэтому я не знаю, есть ли известный алгоритм для этого, не создавая сначала все возможные комбинации ...
Удачи, во всяком случае !
я упоминал [целочисленной задачи раздела] (http://en.wikipedia.org/wiki/Partition_ (number_theory)), который дает длину подобной последовательности. Я не думаю, что это далеко, потому что, как только у вас есть эта целая последовательность разделов, легко удалить порядок (добавить все перестановки) и обеспечить соблюдение ряда ячеек ('m' в' n' bins означает, что наибольшее число в любой бит может быть не более 'mn'). –
О, извините. Я думал, что видел раньше. Однако я должен не согласиться с решением. Я добавил docstring и более описательные vars, чтобы помочь понять алгоритм ... –