Одним из решений является перечисление набора мощностей строк, а затем проверка каждого возможного подмножества строк для условия суммирования. Для матриц с большим количеством строк это может быть довольно медленным.
Используйте стандартный рецепт itertools для блока питания:
from itertools import chain, combinations
def powerset(iterable):
xs = list(iterable)
return chain.from_iterable(combinations(xs, n) for n in range(len(xs) + 1))
тогда я показываю рабочий пример с некоторыми синтетическими данными:
In [79]: data
Out[79]:
array([[0, 1, 1],
[0, 0, 1],
[1, 0, 1],
[0, 1, 1],
[0, 0, 0],
[0, 1, 0],
[1, 1, 1],
[1, 1, 0],
[1, 1, 1],
[0, 1, 0]], dtype=int32)
In [80]: def is_constant(array):
...: return (array == array[0]).all()
...:
In [81]: solution = []
In [82]: for candidate in powerset(range(len(data))):
...: if candidate and is_constant(data[candidate, :].sum(axis=0)):
...: solution.append(candidate)
...:
Который показывает, например:
In [83]: solution
Out[83]:
[(4,),
(6,),
(8,),
(1, 7),
(2, 5),
(2, 9),
(4, 6),
(4, 8),
(6, 8),
(0, 2, 7),
(1, 4, 7),
(1, 6, 7),
(1, 7, 8),
(2, 3, 7),
(2, 4, 5),
(2, 4, 9),
(2, 5, 6),
(2, 5, 8),
(2, 6, 9),
(2, 8, 9),
(4, 6, 8),
(0, 2, 4, 7),
(0, 2, 6, 7),
(0, 2, 7, 8),
(1, 2, 5, 7),
(1, 2, 7, 9),
(1, 4, 6, 7),
(1, 4, 7, 8),
(1, 6, 7, 8),
(2, 3, 4, 7),
(2, 3, 6, 7),
(2, 3, 7, 8),
(2, 4, 5, 6),
(2, 4, 5, 8),
(2, 4, 6, 9),
(2, 4, 8, 9),
(2, 5, 6, 8),
(2, 6, 8, 9),
(0, 2, 4, 6, 7),
(0, 2, 4, 7, 8),
(0, 2, 6, 7, 8),
(1, 2, 4, 5, 7),
(1, 2, 4, 7, 9),
(1, 2, 5, 6, 7),
(1, 2, 5, 7, 8),
(1, 2, 6, 7, 9),
(1, 2, 7, 8, 9),
(1, 4, 6, 7, 8),
(2, 3, 4, 6, 7),
(2, 3, 4, 7, 8),
(2, 3, 6, 7, 8),
(2, 4, 5, 6, 8),
(2, 4, 6, 8, 9),
(0, 2, 4, 6, 7, 8),
(1, 2, 4, 5, 6, 7),
(1, 2, 4, 5, 7, 8),
(1, 2, 4, 6, 7, 9),
(1, 2, 4, 7, 8, 9),
(1, 2, 5, 6, 7, 8),
(1, 2, 6, 7, 8, 9),
(2, 3, 4, 6, 7, 8),
(1, 2, 4, 5, 6, 7, 8),
(1, 2, 4, 6, 7, 8, 9)]
и мы можем проверить решение для нескольких из этих случаев:
In [84]: data[(1, 2, 4, 6, 7, 8, 9), :].sum(axis=0)
Out[84]: array([4, 4, 4])
In [85]: data[(0, 2, 4, 6, 7), :].sum(axis=0)
Out[85]: array([3, 3, 3])
Чтобы расширить это для более конкретных случаев использования, вы можете использовать itertools.combinations
для создания подмножеств только определенного размера, например, подмножеств ровно 2 строки или ровно 3 строки и т. Д.
Или вы можете просто отфильтровать нежелательные результаты (например, тривиальные решения, состоящие из одной строки за раз) из набора результатов, приведенного в моем примере.
Обратите внимание, что вы можете упростить определение функции powerset
(та, которую я использую, буквально взята из документации Python о рецептах itertools). Вместо передачи итерируемой переменной, которая преобразуется в список, вы можете просто передать целое число и сразу пропустить, чтобы вернуть окончательный результат chain.from_iterable
, а затем изменить, просто передав len(data)
в качестве аргумента powerset
в моем примере, как это:
from itertools import chain, combinations
def powerset(N):
"""Power set of integers {0, ..., N-1}."""
xs = list(range(N))
return chain.from_iterable(combinations(xs, n) for n in range(N + 1))
...
for candidate in powerset(len(data)):
...