Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andersonbosa/09b7d6bd42c190a9b01f3913633a0415 to your computer and use it in GitHub Desktop.
Save andersonbosa/09b7d6bd42c190a9b01f3913633a0415 to your computer and use it in GitHub Desktop.

Explorando a Eficiência: Desvendando Matrizes Esparsas com JavaScript

Você já se deparou com situações em que uma matriz é composta majoritariamente por zeros? Pode parecer desperdício de espaço representar todos esses zeros. É aqui que entram as matrizes esparsas, uma abordagem inteligente para otimizar o armazenamento de dados. Vamos mergulhar em uma implementação prática dessa ideia utilizando JavaScript e listas encadeadas.

Entendendo o Conceito: Listas Encadeadas e Matrizes Esparsas

Antes de começarmos, é essencial entender o que são listas encadeadas. Em termos simples, são estruturas de dados compostas por nós, onde cada nó contém dados e uma referência ao próximo nó na sequência.

Agora, imagine aplicar essa ideia para representar matrizes esparsas. Em uma matriz esparsa, a maioria dos elementos é zero. Em vez de armazenar todos esses zeros, podemos economizar espaço conectando apenas os elementos não nulos em uma lista encadeada.

Mãos à Obra: Implementação em JavaScript

Vamos dar uma olhada em como isso pode ser feito em JavaScript:

class Node {
  constructor(row, col, data) {
    this.row = row;
    this.col = col;
    this.data = data;
    this.next = null;
  }
}

class SparseMatrix {
  constructor() {
    this.head = null;
    this.tail = null;
    this.size = 0;
  }

  createNewNode(row, col, data) {
    const newNode = new Node(row, col, data);

    if (this.size === 0) {
      this.head = newNode;
    } else {
      this.tail.next = newNode;
    }

    this.tail = newNode;
    this.size++;
  }

  sparseMatrixEncode(A) {
    const n = A.length;
    const m = A[0].length;

    for (let i = 0; i < n; i++) {
      for (let j = 0; j < m; j++) {
        if (A[i][j] !== 0) {
          this.createNewNode(i + 1, j + 1, A[i][j]);
        }
      }
    }
  }
}

// Exemplo de uso:
const sparseMatrix = new SparseMatrix();
const matrixA = [
  [0, 0, 0, 0],
  [5, 0, 8, 0],
  [0, 0, 0, 0],
  [0, 0, 3, 0],
];

sparseMatrix.sparseMatrixEncode(matrixA);

console.log(sparseMatrix);

Cenários Práticos de Uso

Quando pensar em eficiência de espaço é crucial, como em processamento de imagens, modelagem de redes neurais e problemas de álgebra linear, a representação de matrizes esparsas pode ser a chave para otimizar suas soluções.

Eficiência e Limitações

Esta abordagem é eficaz na economia de espaço, especialmente em matrizes grandes com muitos zeros. No entanto, é importante destacar que a eficiência na recuperação de dados pode depender da distribuição dos elementos não nulos.

Explorando Mais sobre Estruturas de Dados e Algoritmos

Se este mergulho na otimização de matrizes esparsas despertou seu interesse, sugerimos explorar mais sobre estruturas de dados. Considere aprender sobre árvores, grafos e outras implementações de matrizes esparsas. Existem excelentes cursos online e livros sobre algoritmos e estruturas de dados em JavaScript que podem enriquecer ainda mais seu conhecimento.

Em resumo, ao entender e aplicar eficientemente estruturas de dados como matrizes esparsas com listas encadeadas, você estará preparado para enfrentar desafios computacionais de maneira mais inteligente e econômica. O aprendizado contínuo é a chave para aprimorar suas habilidades e explorar novos horizontes na vasta paisagem da ciência da computação. Happy coding! 🚀

@andersonbosa
Copy link
Author

Vamos criar implementações em JavaScript para representar matrizes esparsas de duas maneiras: a forma tradicional com um array bidimensional e a forma eficiente utilizando listas encadeadas. Em seguida, compararemos a eficiência dessas representações.

Forma Tradicional: Array Bidimensional

class SparseMatrixTraditional {
  constructor(rows, cols) {
    this.rows = rows;
    this.cols = cols;
    this.matrix = Array.from({ length: rows }, () => Array(cols).fill(0));
  }

  setValue(row, col, value) {
    this.matrix[row][col] = value;
  }
}

// Exemplo de uso:
const traditionalMatrix = new SparseMatrixTraditional(4, 4);
traditionalMatrix.setValue(1, 2, 5);
traditionalMatrix.setValue(1, 3, 8);
traditionalMatrix.setValue(3, 2, 3);

console.log("Forma Tradicional:");
console.log(traditionalMatrix.matrix);

Forma Eficiente: Lista Encadeada

class Node {
  constructor(row, col, data) {
    this.row = row;
    this.col = col;
    this.data = data;
    this.next = null;
  }
}

class SparseMatrixEfficient {
  constructor() {
    this.head = null;
    this.tail = null;
    this.size = 0;
  }

  createNewNode(row, col, data) {
    const newNode = new Node(row, col, data);

    if (this.size === 0) {
      this.head = newNode;
    } else {
      this.tail.next = newNode;
    }

    this.tail = newNode;
    this.size++;
  }
}

// Exemplo de uso:
const efficientMatrix = new SparseMatrixEfficient();
efficientMatrix.createNewNode(1, 2, 5);
efficientMatrix.createNewNode(1, 3, 8);
efficientMatrix.createNewNode(3, 2, 3);

console.log("Forma Eficiente:");
console.log(efficientMatrix);

Comparação de Eficiência

Ao avaliar a eficiência, podemos considerar o consumo de memória em cada abordagem. Para isso, ferramentas de monitoramento de uso de memória podem ser utilizadas, como o process.memoryUsage() em ambientes Node.js.

A representação eficiente usando listas encadeadas é esperada para ter um consumo de memória significativamente menor em comparação com a forma tradicional, especialmente para matrizes esparsas.

Lembre-se de ajustar os tamanhos das matrizes e os valores inseridos conforme necessário para simular cenários relevantes ao seu caso de uso.

@andersonbosa
Copy link
Author

Certamente! Vamos criar implementações em JavaScript para representar matrizes esparsas de duas maneiras: a forma tradicional com um array bidimensional e a forma eficiente utilizando listas encadeadas. Em seguida, compararemos a eficiência dessas representações.

Forma Tradicional: Array Bidimensional

class SparseMatrixTraditional {
  constructor(rows, cols) {
    this.rows = rows;
    this.cols = cols;
    this.matrix = Array.from({ length: rows }, () => Array(cols).fill(0));
  }

  setValue(row, col, value) {
    this.matrix[row][col] = value;
  }
}

// Exemplo de uso:
const traditionalMatrix = new SparseMatrixTraditional(4, 4);
traditionalMatrix.setValue(1, 2, 5);
traditionalMatrix.setValue(1, 3, 8);
traditionalMatrix.setValue(3, 2, 3);

console.log("Forma Tradicional:");
console.log(traditionalMatrix.matrix);

Forma Eficiente: Lista Encadeada

class Node {
  constructor(row, col, data) {
    this.row = row;
    this.col = col;
    this.data = data;
    this.next = null;
  }
}

class SparseMatrixEfficient {
  constructor() {
    this.head = null;
    this.tail = null;
    this.size = 0;
  }

  createNewNode(row, col, data) {
    const newNode = new Node(row, col, data);

    if (this.size === 0) {
      this.head = newNode;
    } else {
      this.tail.next = newNode;
    }

    this.tail = newNode;
    this.size++;
  }
}

// Exemplo de uso:
const efficientMatrix = new SparseMatrixEfficient();
efficientMatrix.createNewNode(1, 2, 5);
efficientMatrix.createNewNode(1, 3, 8);
efficientMatrix.createNewNode(3, 2, 3);

console.log("Forma Eficiente:");
console.log(efficientMatrix);

Comparação de Eficiência

Ao avaliar a eficiência, podemos considerar o consumo de memória em cada abordagem. Para isso, ferramentas de monitoramento de uso de memória podem ser utilizadas, como o process.memoryUsage() em ambientes Node.js.

A representação eficiente usando listas encadeadas é esperada para ter um consumo de memória significativamente menor em comparação com a forma tradicional, especialmente para matrizes esparsas.

Lembre-se de ajustar os tamanhos das matrizes e os valores inseridos conforme necessário para simular cenários relevantes ao seu caso de uso.

@andersonbosa
Copy link
Author

andersonbosa commented Jan 3, 2024

// file: tradicional.js
const traditionalMatrixRows = 40 * 100; // Aumentado em 10000%
const traditionalMatrixCols = 40 * 100; // Aumentado em 10000%
const traditionalMatrix = Array.from({ length: traditionalMatrixRows }, () =>
  Array(traditionalMatrixCols).fill(0)
);

const populateTraditionalMatrix = () => {
  for (let i = 0; i < 100; i++) {
    for (let j = 0; j < 100; j++) {
      traditionalMatrix[i][j] = i + j;
    }
  }
};

const measureMemoryUsageTraditional = () => {
  const usedMemory = process.memoryUsage().heapUsed / 1024 / 1024;
  console.log(`Forma Tradicional Memory Usage: ${usedMemory.toFixed(2)} MB`);
};

const measureExecutionTimeTraditional = () => {
  console.time("Forma Tradicional Execution Time");
  populateTraditionalMatrix();
  console.timeEnd("Forma Tradicional Execution Time");
};

measureExecutionTimeTraditional();
measureMemoryUsageTraditional();
// file: eficiente.js
class Node {
  constructor(row, col, data) {
    this.row = row;
    this.col = col;
    this.data = data;
    this.next = null;
  }
}

class SparseMatrixEfficient {
  constructor() {
    this.head = null;
    this.tail = null;
    this.size = 0;
  }

  createNewNode(row, col, data) {
    const newNode = new Node(row, col, data);

    if (this.size === 0) {
      this.head = newNode;
    } else {
      this.tail.next = newNode;
    }

    this.tail = newNode;
    this.size++;
  }
}

const efficientMatrix = new SparseMatrixEfficient();

const populateEfficientMatrix = () => {
  for (let i = 0; i < 100; i++) {
    for (let j = 0; j < 100; j++) {
      efficientMatrix.createNewNode(i, j, i + j);
    }
  }
};

const measureMemoryUsageEfficient = () => {
  const usedMemory = process.memoryUsage().heapUsed / 1024 / 1024;
  console.log(`Forma Eficiente Memory Usage: ${usedMemory.toFixed(2)} MB`);
};

const measureExecutionTimeEfficient = () => {
  console.time("Forma Eficiente Execution Time");
  populateEfficientMatrix();
  console.timeEnd("Forma Eficiente Execution Time");
};

measureExecutionTimeEfficient();
measureMemoryUsageEfficient();
❯ node tradicional.js
Forma Tradicional Execution Time: 2.692ms
Forma Tradicional Memory Usage: 125.93 MB
 
❯ node eficiente.js
Forma Eficiente Execution Time: 10.809ms
Forma Eficiente Memory Usage: 4.01 MB

Comparação:

A forma tradicional é mais rápida, mas consome significativamente mais memória.
A forma eficiente economiza memória, mas é mais lenta na execução.

Escolha Recomendada:

Se a eficiência de memória é crucial e a velocidade de acesso não é a prioridade, considere a forma eficiente.
Se a velocidade é crítica e a memória não é um problema, opte pela forma tradicional.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment