Эффективно генерировать все векторные разделы


12

Векторное разбиение разбивает вектор на ряд векторов так, что их сумма является исходной. Вот пара разделов:

[3, 1, 2] = [3, 1, 2]
[3, 1, 2] = [0, 0, 1] + [0, 0, 1] + [0, 1, 0] + [1, 0, 0] + [2, 0, 0]
[3, 1, 2] = [1, 1, 2] + [2, 0, 0]

Здесь сложение векторов выполняется поэлементно. Допустимый раздел не содержит векторов с отрицательными целыми числами или вектора с нулем.

Теперь задача состоит в том, чтобы написать программу или функцию, которая генерирует все возможные векторные разбиения по заданному вектору. Это может звучать относительно легко ...

... но есть поворот. Если входной вектор имеет размер L, а самый большой раздел, который он генерирует, имеет M элементов, вы не можете использовать больше, чем O (L * M) памяти.

Вы можете предположить, что целое число использует O (1) памяти. Это означает, что вы должны выводить разделы по мере их создания. Кроме того, вы должны выводить каждый раздел только один раз. Например, это один и тот же раздел:

[3, 1, 2] = [3, 0, 2] + [0, 1, 0]
[3, 1, 2] = [0, 1, 0] + [3, 0, 2]

Если вы должны были вывести оба, ваш ответ неверен.


Все разделы для [3, 2]:

[3, 2]
[0, 1] + [3, 1]
[0, 1] + [0, 1] + [3, 0]
[0, 1] + [0, 1] + [1, 0] + [2, 0]
[0, 1] + [0, 1] + [1, 0] + [1, 0] + [1, 0]
[0, 1] + [1, 0] + [2, 1]
[0, 1] + [1, 0] + [1, 0] + [1, 1]
[0, 1] + [1, 1] + [2, 0]
[0, 2] + [3, 0]
[0, 2] + [1, 0] + [2, 0]
[0, 2] + [1, 0] + [1, 0] + [1, 0]
[1, 0] + [2, 2]
[1, 0] + [1, 0] + [1, 2]
[1, 0] + [1, 1] + [1, 1]
[1, 1] + [2, 1]
[1, 2] + [2, 0]

Чтобы проверить свой ответ, запустите его [3, 2, 5, 2]. Он должен генерировать 17939 разделов, каждый из которых суммируется [3, 2, 5, 2]и является уникальным (вы можете проверить уникальность, сначала отсортировав каждый раздел лексикографически).


Самый короткий код в байтах побеждает.

Ответы:


3

Python 2, 289 байт

Простой алгоритм перебора. Обрабатывает весь список как число в base max(input)+1( b) и проверяет каждое «число» в диапазоне, [0, b**(L*M))чтобы увидеть, является ли оно:

  1. Суммы к правильному количеству
  2. В алфавитном порядке (обеспечивает уникальность)

Если список соответствует этим критериям, программа выводит его со всеми обнуленными векторами.

Использование памяти

Самая большая структура данных, которую я использую в этой программе, - это список Mс двойным вложением, длина списка, содержащая длину liss Lдля выделения O(L*M)памяти.

Для других моих структур данных у меня есть 3 глобальных целых числа O(3), 1 длина списка L( O(L)), 1 длина массива M( O(M)) и копия самого большого массива при выводе ( O(L*M)).

В целом, это дает мне возможность использовать память, O(2*L*M + L + M + 3)что упрощает O(L*M)критерии.

Сложность времени

Будучи алгоритмом перебора, этот алгоритм чрезвычайно медленный. Чтобы завершить цикл while, необходимо, чтобы последний массив в массиве был b-1. Цикл должен пройти b**(L*M)время до того, как это произойдет.

Кроме того, при каждом запуске списка необходимо проверить оба условия и распечатать список в худшем случае, используя L*M+L+Mитерации. Это упрощает, чтобы дать общее O(L*M * b**(L*M)). Я попытался проверить свою программу [3, 2, 5, 2], но через 45 минут сдался.

Гольф-программа

v=input()
L=len(v)
M=sum(v)
b=max(v)
R=range
t=[L*[0]for i in R(M)]
def A(l,i):
 if i<L*M-1and~-b<l[i/L][i%L]:A(l,i+1)
 l[i/L][i%L]=-~l[i/L][i%L]%-~b
while t[-1][-1]<b:
 if v==[sum(q[i]for q in t)for i in R(L)]and all(`t[i]`>=`t[i+1]`for i in R(M-1)):print[x for x in t if[0]*L!=x]
 A(t,0)

Я мог бы играть в гольф немного больше, особенно в части приращения. Идет неуправляемый код.


Определенно не та эффективность, на которую я надеялся, когда писал этот вопрос, но я думаю, что это технически решает проблему :)
orlp
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.