LabASD - Elenca i sottoinsiemi di \(\{0, \ldots, n-1\}\)

Moreno Marzolla

Ultimo aggiornamento: 2024-03-28

Scrivere un programma che, dato un intero \(n \geq 1\) passato sulla riga di comando, stampi il powerset dell’insieme \(X_n = \{0, 1, \ldots, n-1\}\). Il powerset è l’insieme dei sottoinsiemi di \(X_n\).

Ad esempio, se \(n = 3\), il programma deve produrre un output simile al seguente (l’ordine con cui i sottoinsiemi vengono elencati non è importante):

{ }
{ 2 }
{ 1 }
{ 1 2 }
{ 0 }
{ 0 2 }
{ 0 1 }
{ 0 1 2 }

Suggerimento

Il primo problema da risolvere è: come rappresentare un qualunque sottoinsieme di \(X_n = \{0, 1, \ldots, n-1\}\)? Il modo più semplice è di usare una bitmask (detto anche bit set), cioè un array binario di lunghezza \(n\). Un sottoinsieme \(A \subseteq \{0, 1, \ldots, n-1\}\) è rappresentato da \(n\) bit \(b_0, \ldots, b_{n-1}\) tali che:

\[ b_k = \begin{cases} 1 & \mbox{se}\ k \in A\\ 0 & \mbox{altrimenti} \end{cases} \]

Ad esempio, il sottoinsieme \(\{0, 3, 4\}\) di \(X_6\) è rappresentato dall’array \([1, 0, 0, 1, 1, 0]\).

Generare tutti i sottoinsiemi di \(X_n\) equivale quindi a enumerare tutte le possibili combinazioni di \(n\) bit: \([0, 0, \ldots, 0]\) equivale al sottoinsieme vuoto, mentre \([1, 1, \ldots, 1]\) equivale all’intero insieme \(X_n\).

Per generare tutte le possibili combinazioni di \(n\) bit \(b_0, \ldots, b_{n-1}\) è possibile utilizzare un algoritmo ricorsivo:

La funzione powerset_ric(char *b, int i, int n) dovrebbe quindi avere il comportamento seguente:

La funzione deve essere invocata inizialmente con powerset_ric(b, 0, n). Il caso base si ha quando \(i = n\), dove la funzione deve stampare l’elenco dei valori \(k\) per i quali \(b_k = 1\). Questo equivale a stampare il contenuto del sottovettore di \(X_n\) rappresentato dalla bitmask \(b_0, \ldots, b_{n-1}\).

File