Я хотел бы вернуть массив или индекс того, который содержит, какие комбинации членов в массиве NumPy суммируют к определенному числу.
Например, если я возьму этот массив и захочу найти все комбинации его членов, сумма которых равна 7:
import numpy as np
example = np.arange(4) + 1
example
>>> array([1, 2, 3, 4])
это вернуло бы:
somefunction
>>> [[1,2,4], [3,4]]
или индекс:
>>> [[0,1,2], [2,3]]
Я могу представить себе подход с использованием itertools.combinations
, хотя я бы хотел этого избежать, потому что в наборе данных, на котором я пытаюсь это использовать, уже есть ~ 30 000 участников. Он не работает достаточно быстро при рассмотрении каждой длины комбинаций.
Есть ли более быстрый способ сделать это?
Изменение: Для получения дополнительной информации мне не обязательно использовать каждую комбинацию из 30 000 членов. Например, я суммирую целые числа до ~ 1000, поэтому мне нужно <1000 составляющих - в моем случае конечное число составляющих списка, вероятно, будет состоять из 100-400 составляющих.
Расширенный комментарий, а не ответ. В зависимости от структуры ваших данных перечисление всех комбинаций элементов с заданной суммой может оказаться невозможным. Однако существует эффективный способ подсчета количества комбинаций. Затем вы можете решить, хотите ли вы попробовать перечислить все.
Например, с 10k случайных целых чисел от 0 до 10 я нашел 243905016604941663446994
подмножеств, которые составляют 10 - это 24-значное число. Если бы вы могли перечислить комбинацию каждую наносекунду, это заняло бы более 7 миллионов лет. Число для массива из 30 тысяч случайных чисел, суммирующих до 1000
должно быть значительно больше.
Фрагмент кода для подсчета комбинаций, суммирующих в число.
import numpy as np
import sys
example = np.arange(4) + 1
example_target = 7
# assuming all elemenst of arr are positive integers
def count_combs(arr, sum_):
arr = np.sort(arr)
sys.setrecursionlimit(100_000)
state_dict = {}
def state(i, j):
if (i, j) in state_dict:
return state_dict[(i, j)]
elif j < 0:
res = 0
elif j == 0:
res = 1
elif i == 0:
res = 1 if j == arr[i] else 0
else:
res = state(i - 1, j - arr[i]) + state(i - 1, j)
state_dict[(i, j)] = res
return res
return state(arr.shape[0] - 1, sum_)
# print(count_combs(example, example_target))
# prints 2
test_big = np.random.randint(0, 10, size=10000)
test_target = 10
def test():
print(count_combs(test_big, test_target))
if __name__ == "__main__":
test()
# e.g. 258364297793668558120414
Вы можете использовать, если вы не возражаете, itertools.combinations
:
print([x for i in range(1,4) for x in itertools.combinations(example,i) if sum(x)==7])
Выход:
[(3, 4), (1, 2, 4)]
Сортировать, если вы хотите заказ, который вы хотите:
print(sorted([x for i in range(1,4) for x in itertools.combinations(example,i) if sum(x)==7]))
Выход:
[(1, 2, 4), (3, 4)]
Как вы говорите, itertools.combinations
будет медленным, но на самом деле не существует другого, более эффективного способа, чем itertools.combinations
tho.