En aquesta secció, treballarem en diversos exercicis pràctics per aplicar les estratègies de disseny d'algorismes que hem après en els mòduls anteriors. Cada exercici inclou una descripció del problema, una solució suggerida i una explicació detallada del codi.
Exercici 1: Divideix i Venceràs
Descripció del Problema
Implementa un algorisme que trobi l'element màxim en una llista de nombres utilitzant l'estratègia de "Divideix i Venceràs".
Solució Suggerida
def find_max(arr, low, high): # Cas base: Si la llista té un sol element if low == high: return arr[low] # Divideix la llista en dues meitats mid = (low + high) // 2 # Troba el màxim en cada meitat max1 = find_max(arr, low, mid) max2 = find_max(arr, mid + 1, high) # Retorna el màxim dels dos return max(max1, max2) # Exemple d'ús arr = [1, 5, 3, 9, 2, 8, 4] resultat = find_max(arr, 0, len(arr) - 1) print(f"L'element màxim és: {resultat}")
Explicació
- Cas Base: Si la llista té un sol element, aquest és el màxim.
- Divisió: Divideix la llista en dues meitats.
- Conquesta: Troba el màxim en cada meitat recursivament.
- Combinació: Retorna el màxim dels dos valors obtinguts.
Exercici 2: Algorismes Greedy
Descripció del Problema
Implementa un algorisme greedy per resoldre el problema de la motxilla fraccionària.
Solució Suggerida
class Objecte: def __init__(self, pes, valor): self.pes = pes self.valor = valor self.ratio = valor / pes def motxilla_fraccionaria(objectes, capacitat): # Ordena els objectes per valor per unitat de pes en ordre descendent objectes.sort(key=lambda x: x.ratio, reverse=True) valor_total = 0 for obj in objectes: if capacitat >= obj.pes: capacitat -= obj.pes valor_total += obj.valor else: valor_total += obj.valor * (capacitat / obj.pes) break return valor_total # Exemple d'ús objectes = [Objecte(10, 60), Objecte(20, 100), Objecte(30, 120)] capacitat = 50 resultat = motxilla_fraccionaria(objectes, capacitat) print(f"El valor màxim que es pot obtenir és: {resultat}")
Explicació
- Ordenació: Ordena els objectes per valor per unitat de pes en ordre descendent.
- Selecció: Afegeix els objectes a la motxilla fins que la capacitat es completi.
- Fraccionament: Si no hi ha prou capacitat per a un objecte sencer, afegeix una fracció d'aquest.
Exercici 3: Programació Dinàmica
Descripció del Problema
Implementa un algorisme per resoldre el problema del camí mínim en una matriu utilitzant programació dinàmica.
Solució Suggerida
def cami_minim(mat): if not mat or not mat[0]: return 0 n, m = len(mat), len(mat[0]) dp = [[0] * m for _ in range(n)] dp[0][0] = mat[0][0] # Inicialitza la primera fila for j in range(1, m): dp[0][j] = dp[0][j-1] + mat[0][j] # Inicialitza la primera columna for i in range(1, n): dp[i][0] = dp[i-1][0] + mat[i][0] # Omple la resta de la matriu dp for i in range(1, n): for j in range(1, m): dp[i][j] = mat[i][j] + min(dp[i-1][j], dp[i][j-1]) return dp[n-1][m-1] # Exemple d'ús matriu = [ [1, 3, 1], [1, 5, 1], [4, 2, 1] ] resultat = cami_minim(matriu) print(f"El camí mínim té un cost de: {resultat}")
Explicació
- Inicialització: Crea una matriu
dp
per emmagatzemar els costos mínims. - Primera Fila i Columna: Inicialitza la primera fila i columna amb els costos acumulats.
- Ompliment: Omple la resta de la matriu
dp
amb els costos mínims acumulats fins a cada cel·la. - Resultat: El valor a
dp[n-1][m-1]
és el cost mínim per arribar a la cantonada inferior dreta.
Exercici 4: Backtracking
Descripció del Problema
Implementa un algorisme per resoldre el problema de les N reines utilitzant backtracking.
Solució Suggerida
def es_segura(board, row, col, n): # Comprova la fila a l'esquerra for i in range(col): if board[row][i] == 1: return False # Comprova la diagonal superior esquerra for i, j in zip(range(row, -1, -1), range(col, -1, -1)): if board[i][j] == 1: return False # Comprova la diagonal inferior esquerra for i, j in zip(range(row, n, 1), range(col, -1, -1)): if board[i][j] == 1: return False return True def soluciona_n_reines(board, col, n): if col >= n: return True for i in range(n): if es_segura(board, i, col, n): board[i][col] = 1 if soluciona_n_reines(board, col + 1, n): return True board[i][col] = 0 return False def n_reines(n): board = [[0] * n for _ in range(n)] if not soluciona_n_reines(board, 0, n): return "No hi ha solució" return board # Exemple d'ús n = 4 resultat = n_reines(n) for fila in resultat: print(fila)
Explicació
- Funció de Seguretat: Comprova si és segur col·locar una reina en una posició específica.
- Backtracking: Col·loca les reines una per una i utilitza backtracking per trobar una solució.
- Solució: Si es troba una solució, es retorna el tauler amb les reines col·locades.
Conclusió
En aquesta secció, hem treballat amb diversos exercicis pràctics que ens han permès aplicar les estratègies de disseny d'algorismes que hem après. Hem vist com utilitzar "Divideix i Venceràs", algorismes greedy, programació dinàmica i backtracking per resoldre problemes comuns. Aquests exercicis ens ajuden a entendre millor com dissenyar algorismes eficients i eficaços.
Curs d'Anàlisi i Disseny d'Algorismes
Mòdul 1: Introducció als Algorismes
Mòdul 2: Anàlisi d'Algorismes
- Anàlisi de Complexitat Temporal
- Anàlisi de Complexitat Espacial
- Cases de Complexitat: Millor, Pitjor i Promig
Mòdul 3: Estratègies de Disseny d'Algorismes
Mòdul 4: Algorismes Clàssics
- Cerca Binària
- Ordenació per Inserció
- Ordenació per Mescla (Merge Sort)
- Ordenació Ràpida (Quick Sort)
- Algorisme de Dijkstra
- Algorisme de Floyd-Warshall