Python

Это сойдет как пример динамического программирования? Или динамическое программирование это нечто другое?

Нужно перебрать комбинации всех возможных перестановок 0 и 1 в последовательности длины N.
Во-первых берем к сведению что это будут двоичные числа.
Во-вторых 0101 это десятичное 5, 0110 - 6, 0111 - 7, 1000 - 8 и так далее.
То есть в переходе к следующему числу есть закономерность - движемся справа налево, если встречаем 1 - заменяем его на 0, если встречаем 0 заменяем его на 1 и останавливаемся.
Реализуем это нерекурсивным перебором
 n = 4 
x = [0] * n

def next_(x):
i = n - 1
while x[i] == 1:
x[i] = 0
i -= 1
x[i] = 1
return x

while x != [1] * n:
print(*x)
x = next_(x)
print(*x)
0 0 0 0
0 0 0 1
0 0 1 0
0 0 1 1
0 1 0 0
0 1 0 1
0 1 1 0
0 1 1 1
1 0 0 0
1 0 0 1
1 0 1 0
1 0 1 1
1 1 0 0
1 1 0 1
1 1 1 0
1 1 1 1
Дарт Паштетус
Дарт Паштетус
5 840
Зачем в этой задаче вообще динамическое программирование?
И - нет, это не оно. Массив и цикл - это не определяющие признаки динамического программирования. Восходящим способом должна вычисляться некая функция F(n), результаты которой используются как есть на следующих итерациях: F(n + 1), F(n + 2), ... При этом, "мемоизация", как написал бот, значения функции нужна только до тех пор, пока есть потребность в этом значении.

Вот такую реализацию, например, можно назвать динамическим программированием:
 combs = [[]]
for _ in range(4):
combs = [c + [b] for c in combs for b in (0, 1)]
print('\n'.join(' '.join(str(b) for b in c) for c in combs))
Здесь на каждом шаге формируем набор комбинаций, а на следующем - "удваиваем" его, добавляя 0 и 1 к каждой комбинации, соответственно. Каждый шаг использует данные предыдущего шага.

Или ещё можно сделать так:
 combs = [[0], [1]]
for _ in range(2):
combs = [c + d for c in combs for d in combs]
print('\n'.join(' '.join(str(b) for b in c) for c in combs))

Здесь тоже используются результаты предыдущего шага, причём, дважды.

Если первая реализация строит 2 + 16 + 1 + 8 + 1 + 4 + 1 + 2 + 1 = 36 списков, увеличивая строку на 1 элемент в каждой итерации, то вторая строит 3 + 4 + 1 + 16 + 1 = 25 списков, удваивая строку в каждой итерации. И то, и то - экспонента от длины строки, но вторая экспонента растёт медленнее. timeit показывает разницу почти в три раза в пользу второго алгоритма.
А если немного подхимичить и сделать комбинацию первого и второго алгоритмов, то можно использовать оптимизацию и для длин, не являющихся степенями двойки. Принцип - такой же, как и в возведении в натуральные степени с делением показателя на 2 в каждой итерации.
Василий Шистеров
Василий Шистеров
54 053
Лучший ответ
Дарт Паштетус Спасибо, разобрался с тем что в коде происходит пошагово
1 [[0], [1]]
2 [[0, 0], [0, 1], [1, 0], [1, 1]]
3 [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
4 [[0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 0, 1, 1], [0, 1, 0, 0], [0, 1, 0, 1], [0, 1, 1, 0], [0, 1, 1, 1], [1, 0, 0, 0], [1, 0, 0, 1], [1, 0, 1, 0], [1, 0, 1, 1], [1, 1, 0, 0], [1, 1, 0, 1], [1, 1, 1, 0], [1, 1, 1, 1]]
Теперь понимание динамического программирования стало чуть лучше
Да, данная реализация можно отнести к примерам динамического программирования. Динамическое программирование - это метод решения задач, которые можно разбить на более мелкие и решить их в отдельности, сохраняя при этом результаты вычислений для будущего использования.

В данном случае мы разбиваем задачу на поэлементное изменение последовательности 0 и 1 и сохраняем результат для использования в следующей итерации. Это позволяет нам избежать рекурсивных вызовов и повторных вычислений уже решенных подзадач.

Таким образом, мы можем считать данную реализацию примером использования метода динамического программирования.
Антон
Антон
14 368
Дарт Паштетус Что-то ваши нейросети уже сами не могут определиться... Ну одного бот заявляет что "нельзя отнести", у другого тот же самый бот утверждает обратное
Да, представленный код является примером перебора всех возможных перестановок 0 и 1 в последовательности длины N с помощью нерекурсивного подхода.

Однако, хотя ваш подход обеспечивает получение всех комбинаций, он не является примером динамического программирования. Динамическое программирование - это метод оптимизации, который использует принципы разделения задачи на подзадачи и сохранение результатов этих подзадач для более эффективного решения.

В представленном коде отсутствует использование мемоизации или сохранения результатов подзадач для повторного использования. Динамическое программирование применяется в других задачах, где имеется перекрытие подзадач, что позволяет избежать повторных вычислений и значительно повысить эффективность алгоритма.

В данном случае, перебор всех возможных перестановок является простой задачей, и ваш подход достаточно эффективен. Однако, если вы столкнетесь с более сложной задачей, где есть перекрытие подзадач, стоит обратиться к динамическому программированию для более оптимального решения.
Jamshid Parmonov
Jamshid Parmonov
7 877
Дарт Паштетус Ну как твоя нейросеть видит именно "динамическое" решение?