En aquest tema, explorarem com podem optimitzar l'ús de la memòria en els nostres algorismes per millorar el rendiment i l'eficiència del codi. L'ús eficient de la memòria és crucial en aplicacions on els recursos són limitats o quan es treballa amb grans volums de dades.
Conceptes Clau
- Gestió de Memòria
- Memòria Dinàmica vs. Memòria Estàtica: La memòria estàtica es reserva en temps de compilació, mentre que la memòria dinàmica es reserva en temps d'execució.
- Heap i Stack: El stack s'utilitza per a l'assignació de memòria automàtica, mentre que el heap s'utilitza per a l'assignació dinàmica de memòria.
- Estructura de Dades Eficients
- Arrays vs. Llistes Enllaçades: Els arrays tenen un accés ràpid a elements, però les llistes enllaçades són més eficients per a insercions i eliminacions.
- Hash Tables: Proporcionen un accés ràpid a elements, però poden requerir més memòria per evitar col·lisions.
- Algorismes d'Assignació de Memòria
- First Fit, Best Fit, Worst Fit: Estratègies per assignar blocs de memòria en el heap.
- Garbage Collection: Mecanismes per recuperar memòria no utilitzada.
Estratègies per Optimitzar l'Ús de Memòria
- Evitar la Duplicació de Dades
- Referències en lloc de còpies: Utilitzar referències a objectes en lloc de copiar-los pot estalviar molta memòria.
- Intercanvi de Memòria: Compartir memòria entre estructures de dades quan sigui possible.
- Utilitzar Estructures de Dades Adequades
- Estructures de Dades Compactes: Utilitzar estructures de dades que ocupin menys espai, com ara bitsets en lloc de arrays de booleans.
- Estructures de Dades Dinàmiques: Utilitzar estructures que creixin o es redueixin segons sigui necessari, com ara vectors dinàmics.
- Optimització de Codi
- Inlining de Funcions: Evitar la sobrecàrrega de crides a funcions petites.
- Reutilització de Memòria: Reutilitzar memòria ja assignada en lloc de fer noves assignacions.
Exemples Pràctics
Exemple 1: Ús de Bitsets
# Ús de bitsets per representar un conjunt de valors booleans from bitarray import bitarray # Crear un bitset de 10 elements bitset = bitarray(10) bitset.setall(False) # Establir alguns valors a True bitset[2] = True bitset[5] = True print(bitset)
Exemple 2: Reutilització de Memòria en Python
# Reutilització de memòria amb arrays
import numpy as np
# Crear un array de 1000 elements
array = np.zeros(1000)
# Reutilitzar l'array en lloc de crear-ne un de nou
for i in range(1000):
array[i] = i
print(array)Exercicis Pràctics
Exercici 1: Optimització de Memòria amb Llistes Enllaçades
Implementa una llista enllaçada en Python i compara el seu ús de memòria amb un array de la mateixa mida.
Exercici 2: Reducció de Memòria en Algorismes de Cerca
Implementa una taula de hash per a un algorisme de cerca i compara l'ús de memòria amb una cerca lineal en un array.
Solucions
Solució a l'Exercici 1
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
def append(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
return
last = self.head
while last.next:
last = last.next
last.next = new_node
# Crear una llista enllaçada i un array
linked_list = LinkedList()
array = []
# Afegir 1000 elements a cada estructura
for i in range(1000):
linked_list.append(i)
array.append(i)
# Comparar l'ús de memòria
import sys
print(f"Memòria utilitzada per la llista enllaçada: {sys.getsizeof(linked_list)} bytes")
print(f"Memòria utilitzada per l'array: {sys.getsizeof(array)} bytes")Solució a l'Exercici 2
# Implementació de la taula de hash
class HashTable:
def __init__(self, size):
self.size = size
self.table = [None] * size
def hash_function(self, key):
return key % self.size
def insert(self, key, value):
index = self.hash_function(key)
self.table[index] = value
def search(self, key):
index = self.hash_function(key)
return self.table[index]
# Crear una taula de hash i un array
hash_table = HashTable(1000)
array = [None] * 1000
# Inserir 1000 elements a cada estructura
for i in range(1000):
hash_table.insert(i, i)
array[i] = i
# Comparar l'ús de memòria
print(f"Memòria utilitzada per la taula de hash: {sys.getsizeof(hash_table.table)} bytes")
print(f"Memòria utilitzada per l'array: {sys.getsizeof(array)} bytes")Conclusió
En aquesta secció, hem explorat diverses estratègies per optimitzar l'ús de memòria en els nostres algorismes. Hem après sobre la gestió de memòria, l'ús d'estructures de dades eficients i algorismes d'assignació de memòria. També hem vist exemples pràctics i exercicis per aplicar aquests conceptes. Amb aquestes tècniques, podem millorar significativament el rendiment i l'eficiència dels nostres programes.
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
