1. Ce este o submultime?
{}
{1}
{1, 2}
{1, 2, 3}
{1, 3}
{2}
{2, 3}
{3}
Total: 23 = 8 submultimi
n elemente are exact 2n submultimi (inclusiv {} si multimea insasi).De ce 2n? Fiecare element are exact 2 optiuni: il includem sau nu. Deci avem 2 × 2 × … × 2 (de n ori) = 2n combinatii posibile.
n= 3: 2^n = 8 submultimi n= 4: 2^n = 16 submultimi n= 5: 2^n = 32 submultimi n= 8: 2^n = 256 submultimi n=10: 2^n = 1,024 submultimi n=20: 2^n = 1,048,576 submultimi n=30: 2^n = 1,073,741,824 submultimi
Atentie: 2n creste exponential. La n=30, peste un miliard de submultimi.
2. Ideea algoritmului backtracking pentru submultimi
=> Solutie: {}
Adaug 1: sol=[1]
=> Solutie: {1}
Adaug 2: sol=[1, 2]
=> Solutie: {1, 2}
Scot 2
Scot 1
Adaug 2: sol=[2]
=> Solutie: {2}
Scot 2
4 submultimi = 22, generate in ordine: {}, {1}, {1,2}, {2}.
- sol — lista cu elementele alese pana acum (submultimea in constructie)
- back(start) — genereaza toate extensiile posibile cu elemente ≥ start
- Inregistrare la INCEPUT: inregistram sol inaintea buclei (includem si multimea vida)
- Revenire: dupa explorarea ramurii, scoatem elementul adaugat (sol.pop())
- Parametrul start creste la fiecare nivel, garantand ordine si unicitate
3. Implementarea completa in Python
# Generarea submultimilor prin backtracking # Limbaj: Python | Nivel: mat-info cls XI def submultimi_bt(n): sol = [] # submultimea in constructie rezultate = [] def back(start): # inregistram solutia curenta (chiar daca e vida) rezultate.append(sol[:]) for i in range(start, n + 1): sol.append(i) # adaugam i la submultime back(i + 1) # continuam cu elemente > i sol.pop() # revenire: scoatem i back(1) return rezultate # Generam submultimile lui {1, 2, 3} n = 3 subs = submultimi_bt(n) print(f"Submultimile lui {chr(123)}1,...,{n}{chr(125)} ({len(subs)} bucati):") for s in subs: if s: print('{' + ', '.join(map(str, s)) + '}') # nevida else: print('{}') # multimea vida
Submultimile lui {1,...,3} (8 bucati):
{}
{1}
{1, 2}
{1, 2, 3}
{1, 3}
{2}
{2, 3}
{3}
4. Produs cartezian si conexiunea cu submultimile
# Produs cartezian al k multimi prin backtracking def produs_cartezian_bt(multimi): k = len(multimi) sol = [None] * k # tuplul in constructie rezultate = [] def back(nivel): if nivel == k: # tuplul e complet rezultate.append(tuple(sol)) return for elem in multimi[nivel]: sol[nivel] = elem back(nivel + 1) back(0) return rezultate multimi = [[1, 2], [3, 4], [5, 6]] rez = produs_cartezian_bt(multimi) print(f"Produs cartezian {{1,2}} x {{3,4}} x {{5,6}} (2x2x2={len(rez)} tupli):") for r in rez: print(r)
Produs cartezian {1,2} x {3,4} x {5,6} (2x2x2=8 tupli):
(1, 3, 5)
(1, 3, 6)
(1, 4, 5)
(1, 4, 6)
(2, 3, 5)
(2, 3, 6)
(2, 4, 5)
(2, 4, 6)
Diferenta intre produs cartezian si submultimi: La produsul cartezian avem k niveluri fixe si fiecare nivel parcurge propria multime. La submultimi, dimensiunea solutiei variaza (de la 0 la n) si parametrul start evita dublurile.
5. Submultimi cu restrictii: taieri de ramuri (pruning)
# Submultimi cu suma exacta S (cu pruning) def submultimi_suma(n, S): sol = [] rezultate = [] def back(start, suma_curenta): if suma_curenta == S: rezultate.append(sol[:]) return if suma_curenta > S: return # pruning: suma a depasit S, taiem for i in range(start, n + 1): sol.append(i) back(i + 1, suma_curenta + i) sol.pop() back(1, 0) return rezultate n = 6 S = 7 rez = submultimi_suma(n, S) print(f"Submultimi din {{1,...,{n}}} cu suma={S} ({len(rez)} solutii):") for s in rez: print('{' + ', '.join(map(str, s)) + '}')
Submultimi din {1,...,6} cu suma=7 (4 solutii):
{1, 2, 4}
{1, 6}
{2, 5}
{3, 4}
6. Implementare C++ EXCLUSIV INTENSIV
La profilul mat-info intensiv, implementam acelasi algoritm in C++. Diferente fata de Python: tablou static int sol[20] cu contor lung in loc de lista dinamica; variabile globale pentru acces direct din recursie; afisare directa.
#include <iostream> using namespace std; int n; int sol[20]; // submultimea in constructie int lung; // numarul de elemente curente in sol void back(int start) { // afisam solutia curenta (inclusiv {}) cout << "{"; for (int i = 0; i < lung; i++) { if (i > 0) cout << ", "; cout << sol[i]; } cout << "}\n"; for (int i = start; i <= n; i++) { sol[lung++] = i; // adaugam i, lung creste back(i + 1); // continuam cu elemente > i lung--; // revenire: lung scade } } int main() { n = 3; lung = 0; cout << "Submultimile lui {1,...," << n << "} (2^n=" << (1 << n) << " bucati):\n"; back(1); return 0; }
Submultimile lui {1,...,3} (2^n=8 bucati):
{}
{1}
{1, 2}
{1, 2, 3}
{1, 3}
{2}
{2, 3}
{3}