2

I have faced this C++ problem:

In numerical analysis and scientific computing, a sparse matrix or sparse array is a matrix in which most of the elements are zero. There is no strict definition how many elements need to be zero for a matrix to be considered sparse but a common criterion is that the number of non-zero elements is roughly the number of rows or columns. By contrast, if most of the elements are nonzero, then the matrix is considered dense. The number of zero-valued elements divided by the total number of elements (e.g., m × n for an m × n matrix) is sometimes referred to as the sparsity of the matrix.

Write a class for storing a sparse matrix. Your class should support matrix sizes up to 1million x 1million. Your class should be able to do any of the following:

A. Generate a random sparse matrix

B. Have the ability for an input matrix to be specified by a programmer

C. Compute the sparsity of a matrix

D. Rotate the matrix

I did research on the matter. I found this approach: A proper way to create a matrix in c++ but I a doubt this would work because the shear size requirement (1M x 1M) in the problem.

Another approach I found using maps: What is the best way to create a sparse array in C++? but it is not quite what I need and I am unsure whether the (1M x 1M) requirement is met. Not sure how to approach part B as well. Also the the map is not encapsulated in the class.

I am interested in learning how to do this. This is not a school project or anything. This was an interview question that I failed to find the answer for.

Edit: Here's my implementation. Although I need to improve on it as it crashes right now if I specify a matrix size of 1Mx1M. I need to make it store only non-zero values.

Your feedback on the implementation is appreciated.

HEADER:

#ifndef SPARSEMATRIX_H
#define SPARSEMATRIX_H

#include <map>
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

class SparseMatrix
{
public:
    // Default constructor
    SparseMatrix(unsigned int r, unsigned int c);
    // Constructor that takes a 2D vector
    SparseMatrix(std::vector<std::vector<int>> matrix);
    // Deconstructor
    ~SparseMatrix();
    // Print all matrix elements on console
    void printMatrix();
    // Return the sparsity
    float computeSparsity();
    // Rotate matrix by 90 degrees clockwise
    void rotate90Clockwise();
    // Return the element at the specified indices
    int at(unsigned int rIndex, unsigned int cIndex);

private:
    // Number of rows for the matrix
    unsigned int rows;
    // Number of columns for the matrix
    unsigned int cols;
    // Holds the number of zero-value elements in matrix
    unsigned int zeroCounter;
    // Map to hold the matrix in key-value fashion
    std::map<std::string,int> data;

};

#endif // SPARSEMATRIX_H

SOURCE:

#include "sparsematrix.h"


#define MAX_SIZE (unsigned int) 1000000
#define MIN_SIZE (unsigned int) 1

SparseMatrix::SparseMatrix( unsigned int r, unsigned int c )
{
    // Acquire the size required for the matrix
    rows = r;
    cols = c;
    zeroCounter = 0;

    // Check if the desired matrix is within design limit (1Mx1M elements)
    if( rows > MAX_SIZE || rows < MIN_SIZE || cols > MAX_SIZE || cols < MIN_SIZE )
        throw std::out_of_range("Matrix is out of range");

    // Loop to generate random values and store in map
    for( unsigned int i = 0; i < rows; ++i )
    {
        for( unsigned int j = 0; j < cols; ++j )
        {
            std::string key = std::to_string(i) + "," + std::to_string(j);

            // Generate random value
            unsigned int value = rand() % 10;
            // Keep count of zero value elements
            if( value == 0 ) zeroCounter++;
            // Store value in map
            data[key] = value;
        }
    }
}

SparseMatrix::SparseMatrix(std::vector<std::vector<int> > matrix)
{
    unsigned int colNum = 0;

    // Compute max column length
    for (unsigned int i = 0; i < matrix.size(); ++i)
    {
        colNum = std::max( { colNum, matrix[i].size() } );
    }

    // Loop to transfer the elements from vector to map
    // Fill all non existent elements with zero value
    for (unsigned int i = 0; i < matrix.size(); ++i)
    {
        for (unsigned int j = 0; j < colNum; ++j)
        {
            std::string key = std::to_string(i) + "," + std::to_string(j);

            unsigned int value;

            if( j < matrix[i].size() )
            {
                // Element exists
                value = matrix[i][j];
            }
            else
            {
                // Element does not exist; fill with zero
                value = 0;
            }

            // Keep count of zero value elements
            if( value == 0 ) zeroCounter++;
            // Store value in map
            data[key] = value;
        }
    }
}

SparseMatrix::~SparseMatrix()
{
    // Matrix deconstructor
}

void SparseMatrix::printMatrix()
{
    // Print all matrix elements on console
    for(auto elem : data)
    {
       std::cout << elem.first << " " << elem.second << std::endl;
    }

    std::cout << " " << std::endl;
}

float SparseMatrix::computeSparsity()
{
    // Return the calculated sparsity
    return ((float)zeroCounter/(rows*cols));

}

void SparseMatrix::rotate90Clockwise()
{
    std::map<std::string,int> temp;
    std::string oldKey;
    std::string newKey;

    //Transpose matrix first
    for(unsigned int r = 0; r < rows; r++)
    {
      for(unsigned int c = r; c < cols; c++)
      {
        oldKey = std::to_string(r) + "," + std::to_string(c);
        newKey = std::to_string(c) + "," + std::to_string(r);

        temp[newKey] = data[oldKey];

        if( oldKey != newKey && data.count(newKey))
            temp[oldKey] = data[newKey];
      }
    }

    // Assign the temp map to our data map
    data = temp;

    // Matrix is transposed now so rows and cols should be swaped
    std::swap(rows, cols);

    // Reverse elements of matrix on row order
    for(unsigned int r = 0; r < rows; r++)
    {
        for(unsigned int c =0; c < cols/2; c++)
        {
            oldKey = std::to_string(r) + "," + std::to_string(c);
            newKey = std::to_string(r) + "," + std::to_string(cols-c-1);

            data[newKey] = temp[oldKey];

            if( oldKey != newKey && temp.count(newKey))
                data[oldKey] = temp[newKey];
        }
    }
}

int SparseMatrix::at(unsigned int rIndex, unsigned int cIndex)
{
    // Check if the requested element is within matrix range
    if( rIndex >= rows || cIndex >= cols )
        throw std::out_of_range("Indices out of range");

    // Construct key for the requested element
    std::string key = std::to_string(rIndex) + "," + std::to_string(cIndex);

    // Return element at key
    return data[key];
}
  • Concerning the sparse matrix, you stated, it's expected that number of non-zero elements may be roughly the number of rows or columns. Hence, you have to expect round-about 1M elements (may be, less or more). The author of the article in [What is the best way to create a sparse array in C++?](https://stackoverflow.com/a/4311/7478597) stated: _10 million items took about 4.4 seconds and about 57 meg on my computer._ Sounds not that terrible. (Not a solution for an embedded but not a suitable problem for such thing as well.) For a PC, it shouldn't be a show-stopper. Why you don't give it a try? – Scheff's Cat Feb 07 '21 at 18:26
  • C: divide the number of entries by the size of the matrix, to get the density. (Is this what you call "sparsity"?) D: go through the list of entries and swap row and columns. That would be a transpose. Hence, you have to correct one or the other by subtracting it from the corresponding dimension (of rows or columns). – Scheff's Cat Feb 07 '21 at 18:50
  • 1
    The Wiki article on Sparse Matrix discusses the common storage formats such as COO, DOK and CSR. While the implementation details will vary with language, there's enough commonality. For example all of these are available in the Python `scipy.sparse` module. – hpaulj Feb 07 '21 at 18:57
  • hello from the HR team where you got this question from :) – deweydb Feb 26 '21 at 00:45
  • Hi @deweydb, I hope you didn't mind me posting the question on here. The implementation above is mine. I asked community for help only to learn. If you'd like it removed, I am sure there is a way that I can remove it. Though I intend on fixing my code once I get some time and showcasing it on my GitHub as it is my work – onemogunner Feb 27 '21 at 01:16
  • @onemogunner No, I do not mind at all. Some other candidates started copying and pasting your solution as their answers to the quiz, which is how I stumbled upon it. I'm glad you found the question interesting. – deweydb Feb 28 '21 at 22:46
  • @deweydb By giving them a bad code I am indirectly helping you weed out the bad candidates lol. Yes it was an interesting question (even though I am not there yet with the solution). I wouldn't mind taking a stab at few more like it. My C++ skills are not there yet. Did you get them from a recommended book? – onemogunner Mar 02 '21 at 01:24

0 Answers0