При попытке реализовать метод, основанный на рекурсии, для поиска набора мощности списка, в реализации C и Python наблюдалось различное поведение. Впервые это было опробовано на Python с использованием псевдокода следующим образом.
def(f, n):
if n<N:
f(s+[n+1], n+1)
f(s, n+1)
if n==N:
# all elements end up here
f([], 1)
Это вызывает наборы до N, начиная с 2, добавляя еще один элемент и не добавляя еще один.
Мой код c выглядит следующим образом
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#define u8 uint8_t
#define N 4
void f(bool *s, u8 n){
printf("%*s", 4*n, "");
printf("[");
for(int i=2;i<=N;i++)
if (s[i]) printf("%d ", i);
printf("]\n");
if (n<N){
f(s, n+1);
s[n+1] = true;
f(s, n+1);
}
if (n==N){}
}
int main(){
bool s[N+1] = {false};
f(s, 1);
}
Чтобы избежать использования динамического списка, я использовал логический массив. Проблемный код, вероятно, — это блок if с проверкой n<N
. В этом коде используются табуляции для разделения различных уровней рекурсии, чтобы упростить отладку.
То же самое для Python
N = 4
def f(s, n):
print('\t'*n, s)
if n<N:
f(s+[n+1], n+1)
f(s, n+1)
if (n==N):
pass
f([], 1)
Выход C
[]
[]
[]
[]
[4 ]
[3 4 ]
[3 4 ]
[3 4 ]
[2 3 4 ]
[2 3 4 ]
[2 3 4 ]
[2 3 4 ]
[2 3 4 ]
[2 3 4 ]
[2 3 4 ]
Вывод Python
[]
[2]
[2, 3]
[2, 3, 4]
[2, 3]
[2]
[2, 4]
[2]
[]
[3]
[3, 4]
[3]
[]
[4]
[]
да, извините, я написал это до того, как изменился на s+[..]
, до того, как .append
изменил объект и не вернул его (или не вернул None)
@Someprogrammerdude, я не понимаю, не было возможности изменить скопированную версию массива, изменив что-то одно, поэтому я просто меняю значение и передаю его (поскольку c сам позаботится о копии)
C не «заботится о копии», потому что он не создает копию.
Я чувствую, что это все еще проблема с заказом. f(s+[n+1], n+1)
по сути temp = s+[n+1]; f(temp, n+1)
. Вы добавляете перед рекурсивным вызовом, а код C добавляет после. Но есть и другая проблема: s+[n+1]
создаст новый список, который будет передан рекурсивному вызову. В C вы не можете создать новый «список» (или массив), все вызовы функций будут использовать один массив.
Разница заключается в этих фрагментах кода:
Питон:
f(s + [n+1], n+1)
f(s, n+1)
С:
f(s, n+1);
s[n+1] = true;
f(s, n+1);
Проблемы:
Код C делает выбор в другом порядке, включая n+1
для второго рекурсивного вызова, тогда как версия Python делает это для первого рекурсивного вызова. На самом деле это не проблема, но частично объясняет разницу в результатах.
Еще большая проблема заключается в том, что код C не устанавливает s[n+1]
обратно в false
после второго рекурсивного вызова, что (плохо) повлияет на поведение выше по дереву рекурсии. Это ошибка, которой нет в коде Python, где выбор i+1
существует только во время рекурсивного вызова, но не продолжает выбираться после завершения этого рекурсивного вызова.
Чтобы исправить ошибку, вы можете добавить следующий оператор после второго рекурсивного вызова:
s[n+1] = false;
Но чтобы вывод был таким же, как и для кода Python, также измените порядок:
s[n+1] = true;
f(s, n+1);
s[n+1] = false;
f(s, n+1);
Можно подробнее, как именно это влияет? Если я правильно понимаю, после первого вызова ему передается копия массива и модифицируется s на второй. В смысле предоставления они получают одну и ту же «информацию», так что же меняет добавление s[n+1] = false
?
Сдачу дадут не тут же на месте, а позже. В цикле печати вы также посетите элементы с индексами s
, которые были установлены на более глубокой глубине рекурсии через родственную ветвь в дереве рекурсии. Если вы не сбросили эти записи обратно на false
перед возвратом из более глубокой рекурсии, тогда этот цикл печати все равно будет видеть их установленными на true
, а это не то, что вам нужно.
В функции Python у вас есть
f(s.append(n+1), n+1)
, что эквивалентноs.append(n+1); f(s, n+1)
. В коде C у вас естьf(s, n+1); s[n+1] = true;
, что не эквивалентно.