Ефективно генеруйте всі векторні розділи


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що містить довжину лишаю, 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, щоб вийти, повинен бути кінцевим int в масиві 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.