Sparse Matrix multiplication like (maxmin) in C++ using Octave libraries - c++
I'm implementing a maxmin function, it works like matrix multiplication but instead of summing products it gets max of min between two numbers pointwise. An example of naive implementation is
double mx = 0;
double mn = 0;
for (i = 0; i < rowsC;i++)
{
for(j = 0; j < colsC;j++)
{
mx = 0;
for(k = 0; k < colsA; k++)
{
if (a(i, k) < b(k, j))
mn = a(i,k);
else
mn = b(k,j);
if (mn > mx)
mx = mn;
}
c(i, j) = mx;
}
}
I'm coding it as an Octave oct-file so i have to use oct.h data structure. The problem is that i want to implement a sparse version, but usually you need a reference to the next non zero element in a row or in a column like in this example (see 4.3 algorithm):
http://www.eecs.harvard.edu/~ellard/Q-97/HTML/root/node20.html
There doing row_p->next gave the next nonzero element of the row (the same for the column). Is there a way to do the same with the octave SparseMatrix class? Or is there another way of implementing the sparse matrix multiplication i can adopt for my maxmin function?
I don't know if anyoe would ever be interested, but i managed to find a solution.
The code of the solution is part of fl-core1.0 a fuzzy logic core package for Octave and it is released under LGPL license.
(The code relies on some octave functions)
// Calculate the S-Norm/T-Norm composition of sparse matrices (single thread)
void sparse_compose(octave_value_list args)
{
// Create constant versions of the input matrices to prevent them to be filled by zeros on reading.
// a is the const reference to the transpose of a because octave sparse matrices are column compressed
// (to cycle on the rows, we cycle on the columns of the transpose).
SparseMatrix atmp = args(0).sparse_matrix_value();
const SparseMatrix a = atmp.transpose();
const SparseMatrix b = args(1).sparse_matrix_value();
// Declare variables for the T-Norm and S-Norm values
float snorm_val;
float tnorm_val;
// Initialize the result sparse matrix
sparseC = SparseMatrix((int)colsB, (int)rowsA, (int)(colsB*rowsA));
// Initialize the number of nonzero elements in the sparse matrix c
int nel = 0;
sparseC.xcidx(0) = 0;
// Calculate the composition for each element
for (int i = 0; i < rowsC; i++)
{
for(int j = 0; j < colsC; j++)
{
// Get the index of the first element of the i-th column of a transpose (i-th row of a)
// and the index of the first element of the j-th column of b
int ka = a.cidx(i);
int kb = b.cidx(j);
snorm_val = 0;
// Check if the values of the matrix are really not 0 (it happens if the column of a or b hasn't any value)
// because otherwise the cidx(i) or cidx(j) returns the first nonzero element of the previous column
if(a(a.ridx(ka),i)!=0 && b(b.ridx(kb),j)!=0)
{
// Cicle on the i-th column of a transpose (i-th row of a) and j-th column of b
// From a.cidx(i) to a.cidx(i+1)-1 there are all the nonzero elements of the column i of a transpose (i-th row of a)
// From b.cidx(j) to b.cidx(j+1)-1 there are all the nonzero elements of the column j of b
while ((ka <= (a.cidx(i+1)-1)) && (kb <= (b.cidx(j+1)-1)))
{
// If a.ridx(ka) == b.ridx(kb) is true, then there's a nonzero value on the same row
// so there's a k for that a'(k, i) (equals to a(i, k)) and b(k, j) are both nonzero
if (a.ridx(ka) == b.ridx(kb))
{
tnorm_val = calc_tnorm(a.data(ka), b.data(kb));
snorm_val = calc_snorm(snorm_val, tnorm_val);
ka++;
kb++;
}
// If a.ridx(ka) == b.ridx(kb) ka should become the index of the next nonzero element on the i column of a
// transpose (i row of a)
else if (a.ridx(ka) < b.ridx(kb))
ka++;
// If a.ridx(ka) > b.ridx(kb) kb should become the index of the next nonzero element on the j column of b
else
kb++;
}
}
if (snorm_val != 0)
{
// Equivalent to sparseC(i, j) = snorm_val;
sparseC.xridx(nel) = j;
sparseC.xdata(nel++) = snorm_val;
}
}
sparseC.xcidx(i+1) = nel;
}
// Compress the result sparse matrix because it is initialized with a number of nonzero element probably greater than the real one
sparseC.maybe_compress();
// Transpose the result
sparseC = sparseC.transpose();
}
Related
couldn't find the determinant of n*n matrix
I have written the following code to calculate the determinant of a N*N matrix. it works perfectly for the matrix 4*4 and 5*5. but it could not find the determinant of the 40*40 matrix named Z_z. The elements of matrix Z_z are presented herein. #include <iostream> int const N_M=40; void getCofactor(double A[N_M][N_M], double A_rc[N_M][N_M], int r,int c, int n) { int i = 0, j = 0; // Looping for each element of the matrix for (int row = 0; row < n; row++) { for (int col = 0; col < n; col++) { // Copying into temporary matrix only those element // which are not in given row and column if (row != r && col != c) { A_rc[i][j] = A[row][col]; j=j+1; // Row is filled, so increase row index and // reset col index if (j == n - 1) { j = 0; i=i+1; } } } } } double determinant(double A[N_M][N_M], int n) { double D = 0.0; // Initialize result // Base case : if matrix contains single element if (n==1) return A[0][0]; else if (n == 2) return (A[0][0]*A[1][1])-(A[0][1]*A[1][0]); else { double sub_Matrix_A_0c[N_M][N_M]; // To store cofactors // Iterate for each element of first row for (int c = 0; c < n; c++) { // Getting Cofactor of A[0][f] getCofactor(A, sub_Matrix_A_0c, 0, c, n); D += pow(-1.0,c) * A[0][c] * determinant(sub_Matrix_A_0c, n - 1); } return D;} } int main () { double Z_z[N_M][N_M]= {{-64,16,-4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {-43.7213019529827,12.4106746539480,-3.52287874528034,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,-43.7213019529827,12.4106746539480,-3.52287874528034,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,-27,9,-3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,-27,9,-3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,-16.0579142269798,6.36491716338729,-2.52287874528034,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,-16.0579142269798,6.36491716338729,-2.52287874528034,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,-8,4,-2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-8,4,-2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3.53179897265895,2.31915967282662,-1.52287874528034,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3.53179897265895,2.31915967282662,-1.52287874528034,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,1,-1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,1,-1,1,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-0.142956190020121,0.273402182265940,-0.522878745280338,1,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-0.142956190020121,0.273402182265940,-0.522878745280338,1,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2.20222621658426,1.69267904961742,1.30102999566398,1}, {37.2320239618439,-7.04575749056068,1,0,-37.2320239618439,7.04575749056068,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,27,-6,1,0,-27,6,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,19.0947514901619,-5.04575749056068,1,0,-19.0947514901619,5.04575749056068,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,12,-4,1,0,-12,4,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6.95747901847985,-3.04575749056068,1,0,-6.95747901847985,3.04575749056068,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,-2,1,0,-3,2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.820206546797821,-1.04575749056068,1,0,-0.820206546797821,1.04575749056068,-1,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,-1,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,1,0,-3,-2,-1,0}, {-21.1372724716820,2,0,0,21.1372724716820,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,-18,2,0,0,18,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,-15.1372724716820,2,0,0,15.1372724716820,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,-12,2,0,0,12,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-9.13727247168203,2,0,0,9.13727247168203,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,2,0,0,6,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3.13727247168203,2,0,0,3.13727247168203,-2,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,-2,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,2,0,0,-6,-2,0,0}, {24,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-7.80617997398389,-2,0,0}}; double det=determinant(Z_z, 40); cout<<det; system ("pause"); return 0;}
You recursively call determinant() functuon n times at the first stage, then n - 1 times for each of n calls, etc. So total number of call would be closed to n! (factorial). When n = 4 or n = 5 the number of calls is still acceptable, but at n = 40 you try to make 40! = 815915283247897734345611269596115894272000000000 virtual calls to say nothing about so many operations of any kind. I don't think you can find a machine to handle that.
Algorithm on hexagonal grid
Hexagonal grid is represented by a two-dimensional array with R rows and C columns. First row always comes "before" second in hexagonal grid construction (see image below). Let k be the number of turns. Each turn, an element of the grid is 1 if and only if the number of neighbours of that element that were 1 the turn before is an odd number. Write C++ code that outputs the grid after k turns. Limitations: 1 <= R <= 10, 1 <= C <= 10, 1 <= k <= 2^(63) - 1 An example with input (in the first row are R, C and k, then comes the starting grid): 4 4 3 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 Simulation: image, yellow elements represent '1' and blank represent '0'. This problem is easy to solve if I simulate and produce a grid each turn, but with big enough k it becomes too slow. What is the faster solution? EDIT: code (n and m are used instead R and C) : #include <cstdio> #include <cstring> using namespace std; int old[11][11]; int _new[11][11]; int n, m; long long int k; int main() { scanf ("%d %d %lld", &n, &m, &k); for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) scanf ("%d", &old[i][j]); } printf ("\n"); while (k) { for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { int count = 0; if (i % 2 == 0) { if (i) { if (j) count += old[i-1][j-1]; count += old[i-1][j]; } if (j) count += (old[i][j-1]); if (j < m-1) count += (old[i][j+1]); if (i < n-1) { if (j) count += old[i+1][j-1]; count += old[i+1][j]; } } else { if (i) { if (j < m-1) count += old[i-1][j+1]; count += old[i-1][j]; } if (j) count += old[i][j-1]; if (j < m-1) count += old[i][j+1]; if (i < n-1) { if (j < m-1) count += old[i+1][j+1]; count += old[i+1][j]; } } if (count % 2) _new[i][j] = 1; else _new[i][j] = 0; } } for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) old[i][j] = _new[i][j]; } k--; } for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { printf ("%d", old[i][j]); } printf ("\n"); } return 0; }
For a given R and C, you have N=R*C cells. If you represent those cells as a vector of elements in GF(2), i.e, 0s and 1s where arithmetic is performed mod 2 (addition is XOR and multiplication is AND), then the transformation from one turn to the next can be represented by an N*N matrix M, so that: turn[i+1] = M*turn[i] You can exponentiate the matrix to determine how the cells transform over k turns: turn[i+k] = (M^k)*turn[i] Even if k is very large, like 2^63-1, you can calculate M^k quickly using exponentiation by squaring: https://en.wikipedia.org/wiki/Exponentiation_by_squaring This only takes O(log(k)) matrix multiplications. Then you can multiply your initial state by the matrix to get the output state. From the limits on R, C, k, and time given in your question, it's clear that this is the solution you're supposed to come up with.
There are several ways to speed up your algorithm. You do the neighbour-calculation with the out-of bounds checking in every turn. Do some preprocessing and calculate the neighbours of each cell once at the beginning. (Aziuth has already proposed that.) Then you don't need to count the neighbours of all cells. Each cell is on if an odd number of neighbouring cells were on in the last turn and it is off otherwise. You can think of this differently: Start with a clean board. For each active cell of the previous move, toggle the state of all surrounding cells. When an even number of neighbours cause a toggle, the cell is on, otherwise the toggles cancel each other out. Look at the first step of your example. It's like playing Lights Out, really. This method is faster than counting the neighbours if the board has only few active cells and its worst case is a board whose cells are all on, in which case it is as good as neighbour-counting, because you have to touch each neighbours for each cell. The next logical step is to represent the board as a sequence of bits, because bits already have a natural way of toggling, the exclusive or or xor oerator, ^. If you keep the list of neigbours for each cell as a bit mask m, you can then toggle the board b via b ^= m. These are the improvements that can be made to the algorithm. The big improvement is to notice that the patterns will eventually repeat. (The toggling bears resemblance with Conway's Game of Life, where there are also repeating patterns.) Also, the given maximum number of possible iterations, 2⁶³ is suspiciously large. The playing board is small. The example in your question will repeat at least after 2¹⁶ turns, because the 4×4 board can have at most 2¹⁶ layouts. In practice, turn 127 reaches the ring pattern of the first move after the original and it loops with a period of 126 from then. The bigger boards may have up to 2¹⁰⁰ layouts, so they may not repeat within 2⁶³ turns. A 10×10 board with a single active cell near the middle has ar period of 2,162,622. This may indeed be a topic for a maths study, as Aziuth suggests, but we'll tacke it with profane means: Keep a hash map of all previous states and the turns where they occurred, then check whether the pattern has occurred before in each turn. We now have: a simple algorithm for toggling the cells' state and a compact bitwise representation of the board, which allows us to create a hash map of the previous states. Here's my attempt: #include <iostream> #include <map> /* * Bit representation of a playing board, at most 10 x 10 */ struct Grid { unsigned char data[16]; Grid() : data() { } void add(size_t i, size_t j) { size_t k = 10 * i + j; data[k / 8] |= 1u << (k % 8); } void flip(const Grid &mask) { size_t n = 13; while (n--) data[n] ^= mask.data[n]; } bool ison(size_t i, size_t j) const { size_t k = 10 * i + j; return ((data[k / 8] & (1u << (k % 8))) != 0); } bool operator<(const Grid &other) const { size_t n = 13; while (n--) { if (data[n] > other.data[n]) return true; if (data[n] < other.data[n]) return false; } return false; } void dump(size_t n, size_t m) const { for (size_t i = 0; i < n; i++) { for (size_t j = 0; j < m; j++) { std::cout << (ison(i, j) ? 1 : 0); } std::cout << '\n'; } std::cout << '\n'; } }; int main() { size_t n, m, k; std::cin >> n >> m >> k; Grid grid; Grid mask[10][10]; for (size_t i = 0; i < n; i++) { for (size_t j = 0; j < m; j++) { int x; std::cin >> x; if (x) grid.add(i, j); } } for (size_t i = 0; i < n; i++) { for (size_t j = 0; j < m; j++) { Grid &mm = mask[i][j]; if (i % 2 == 0) { if (i) { if (j) mm.add(i - 1, j - 1); mm.add(i - 1, j); } if (j) mm.add(i, j - 1); if (j < m - 1) mm.add(i, j + 1); if (i < n - 1) { if (j) mm.add(i + 1, j - 1); mm.add(i + 1, j); } } else { if (i) { if (j < m - 1) mm.add(i - 1, j + 1); mm.add(i - 1, j); } if (j) mm.add(i, j - 1); if (j < m - 1) mm.add(i, j + 1); if (i < n - 1) { if (j < m - 1) mm.add(i + 1, j + 1); mm.add(i + 1, j); } } } } std::map<Grid, size_t> prev; std::map<size_t, Grid> pattern; for (size_t turn = 0; turn < k; turn++) { Grid next; std::map<Grid, size_t>::const_iterator it = prev.find(grid); if (1 && it != prev.end()) { size_t start = it->second; size_t period = turn - start; size_t index = (k - turn) % period; grid = pattern[start + index]; break; } prev[grid] = turn; pattern[turn] = grid; for (size_t i = 0; i < n; i++) { for (size_t j = 0; j < m; j++) { if (grid.ison(i, j)) next.flip(mask[i][j]); } } grid = next; } for (size_t i = 0; i < n; i++) { for (size_t j = 0; j < m; j++) { std::cout << (grid.ison(i, j) ? 1 : 0); } std::cout << '\n'; } return 0; } There is probably room for improvement. Especially, I'm not so sure how it fares for big boards. (The code above uses an ordered map. We don't need the order, so using an unordered map will yield faster code. The example above with a single active cell on a 10×10 board took significantly longer than a second with an ordered map.)
Not sure about how you did it - and you should really always post code here - but let's try to optimize things here. First of all, there is not really a difference between that and a quadratic grid. Different neighbor relationships, but I mean, that is just a small translation function. If you have a problem there, we should treat this separately, maybe on CodeReview. Now, the naive solution is: for all fields count neighbors if odd: add a marker to update to one, else to zero for all fields update all fields by marker of former step this is obviously in O(N). Iterating twice is somewhat twice the actual run time, but should not be that bad. Try not to allocate space every time that you do that but reuse existing structures. I'd propose this solution: at the start: create a std::vector or std::list "activated" of pointers to all fields that are activated each iteration: create a vector "new_activated" for all items in activated count neighbors, if odd add to new_activated for all items in activated set to inactive replace activated by new_activated* for all items in activated set to active *this can be done efficiently by putting them in a smart pointer and use move semantics This code only works on the activated fields. As long as they stay within some smaller area, this is far more efficient. However, I have no idea when this changes - if there are activated fields all over the place, this might be less efficient. In that case, the naive solution might be the best one. EDIT: after you now posted your code... your code is quite procedural. This is C++, use classes and use representation of things. Probably you do the search for neighbors right, but you can easily make mistakes there and therefore should isolate that part in a function, or better method. Raw arrays are bad and variables like n or k are bad. But before I start tearing your code apart, I instead repeat my recommendation, put the code on CodeReview, having people tear it apart until it is perfect.
This started off as a comment, but I think it could be helpful as an answer in addition to what has already been stated. You stated the following limitations: 1 <= R <= 10, 1 <= C <= 10 Given these restrictions, I'll take the liberty to can represent the grid/matrix M of R rows and C columns in constant space (i.e. O(1)), and also check its elements in O(1) instead of O(R*C) time, thus removing this part from our time-complexity analysis. That is, the grid can simply be declared as bool grid[10][10];. The key input is the large number of turns k, stated to be in the range: 1 <= k <= 2^(63) - 1 The problem is that, AFAIK, you're required to perform k turns. This makes the algorithm be in O(k). Thus, no proposed solution can do better than O(k)[1]. To improve the speed in a meaningful way, this upper-bound must be lowered in some way[1], but it looks like this cannot be done without altering the problem constraints. Thus, no proposed solution can do better than O(k)[1]. The fact that k can be so large is the main issue. The most anyone can do is improve the rest of the implementation, but this will only improve by a constant factor; you'll have to go through k turns regardless of how you look at it. Therefore, unless some clever fact and/or detail is found that allows this bound to be lowered, there's no other choice. [1] For example, it's not like trying to determine if some number n is prime, where you can check all numbers in the range(2, n) to see if they divide n, making it a O(n) process, or notice that some improvements include only looking at odd numbers after checking n is not even (constant factor; still O(n)), and then checking odd numbers only up to √n, i.e., in the range(3, √n, 2), which meaningfully lowers the upper-bound down to O(√n).
Calculating the determinant of a matrix
I am trying to calculate the determinant of a square matrix using row operations. I ran into this code but I do not really understand how it works. What do subi and subj do? Does it use row operations? What is the logic behind this code? int c, subi, i, j, subj; double submat[10][10],d=0; if (n == 2) { return((mat[0][0] * mat[1][1]) - (mat[1][0] * mat[0][1])); } else { for (c = 0; c < n; c++) { subi = 0; for (int i = 1; i < n; i++) { subj = 0; for (j = 0; j < n; j++) { if (j == c) continue; submat[subi][subj] = mat[i][j]; subj++; } subi++; } d = d + (pow(-1, c)*mat[0][c] * determinant(n - 1, submat)); } } return d;
The function, which looks like: double determinant(int n, double mat[10][10]); recursively goes through rows and calls itself on the submatrices for that row and the first column return a value for all by matrices. The recursion ends for 2 by 2 matrices.
This is a recursive function using Laplace expansion to calculate the determinant whose base case is a 2 by 2 matrix. However, it does not seem to be a good program to me for: what if the input is a 1 by 1 matrix submat is limited by size of 10 by 10 submat is a waste of memory When matrix is large, it is better to use LU decomposition.
In a matrix put 0 in the row and column of a cell which contains 0 without using extra space
Given a matrix, if a cell contains 0, then we have make entire row and column corresponding to the cell as 0. For example, if 1 2 3 M = 0 4 5 4 2 0 then the output should be 0 2 0 0 0 0 0 0 0 The method I thought is as follows Make auxiliary arrays row[] and col[]. If a cell(i,j) contains 0 then, mark row[i] and col[j] as 0.(Initially row[] and col[] contains all 1s). Again traverse the whole matrix, if for cell(i,j), either of row[i] or col[j] is 0, then put cell(i,j) as 0. This takes O(m*n) time and O(m+n) space. How to optimize it further specially in terms of space.Any suggestions for improving time complexity are also welcomed.
Aha, this is an old question. Use one boolean variate(isZeroInFirstRow) saving if first row has zero element(s) or not and one boolean variate(isZeroInFirstCol) saving if first column has zero element(s) or not. Then, traverse the whole matrix. If cell(i,j)==0, then set cell(0,j) and cell(i,0) to 0. Traverse the first row of the matrix. If cell(0,j)==0, then set all elements in column(j) to 0. Traverse the first column of the matrix. If cell(i,0)==0, then set all elements in row(i) to 0. If isZeroInFirstRow==true, set all elements in row(0) to 0. If isZeroInFirstCol==true, set all elements in column(0) to 0.
You can solve this in O(1) space. One solution is to iterate on the matrix, for each 0 you see, you fill the corresponding row/col with some character, 'X' for example. When you finish, you should have something like that: X 2 X M= 0 X X X X 0 Then you iterate again on the matrix and replace each 'X' with 0 to get: 0 2 0 M= 0 0 0 0 0 0
If you are concerned with storage you may think of using some sparse matrix storage formats to store the resulting matrix, and then free the original dense input. An example of what I am proposing may be the following (implementing COO format) which should take O(M*N) time: #include<vector> #include<iostream> #include<algorithm> #include<cstddef> using namespace std; int main() { constexpr size_t M = 3; constexpr size_t N = 3; int matrix[M][N] = { {1, 2, 3}, {0, 4, 5}, {4, 2, 0} }; vector<size_t> markedRows; vector<size_t> markedColumns; // Search for zeroes for (size_t ii = 0; ii < M; ++ii) { for(size_t jj = 0; jj < N; ++jj) { if (matrix[ii][jj] == 0) { markedRows.push_back (ii); markedColumns.push_back(jj); } } } // Sort columns (rows are ordered by construction) sort(markedColumns.begin(),markedColumns.end()); // Eliminate duplicates markedRows.erase (unique(markedRows.begin() ,markedRows.end()) ,markedRows.end() ); markedColumns.erase(unique(markedColumns.begin(),markedColumns.end()),markedColumns.end()); // Construct COO matrix format vector<size_t> irow; vector<size_t> icol; vector<int> val; for (size_t ii = 0; ii < M; ++ii) { for(size_t jj = 0; jj < N; ++jj) { if ( ( find(markedRows.begin() ,markedRows.end() ,ii) == markedRows.end() ) && ( find(markedColumns.begin(),markedColumns.end(),jj) == markedColumns.end() ) ) { irow.push_back(ii); icol.push_back(jj); val.push_back (matrix[ii][jj]); } } } // FROM HERE YOU NO LONGER NEED MATRIX, AND YOU CAN FREE THE STORAGE // Print non zero entries for( size_t ii = 0; ii < irow.size(); ++ii) { cout << "A["<<irow[ii]<<","<<icol[ii]<<"] = "<<val[ii]<<endl; } return 0; }
You can use your algorithm without allocating and auxiliary row or column by searching the matirx for a row that contains no zeros and a column that contains no zero elements. If either of these searches fails, then the resulting matrix will all zeros, so your work is done by simply setting all elements to zero. Otherwise, use the row and colum you found as the bookkeeping row and column you mentioned, setting the corresponding element to zero as you find zeros in the remainder of the matrix. Once that pass is done you walk the bookkeeping row, setting the matix columns to zeros for any zero found in the bookkeeping row, similarly for the aux column.
Here is an algorithm can do it in O(M*N) time and O(1) space : - Find the max element in the matrix . Mat[i][j] = max - Mat[i][j] for all (i,j) Notice that Mat[i][j] will only have positive values. Use negetive values as sentinels and Mat[i][j] = max as zeros. Retrieve original values as Mat[i][j] = max - Mat[i][j]
Simple and easy answer: <2 nested loop> to search through all columns and rows you find any cell = 0 through all column and set it to zeros through all row and set it to zeros. let me know if it not clear to record video for it. Int main() { //example matrix dimension rows(r=6) * columns (c=3) int r = 6; int c = 3; int matrix[r][c]; for(int i=0; i<r; ++i){ for(int j=0 ; j < c ; ++j){ if(matrix[i][j] == 0){ for(int ii=0; ii<r; ++ii){ Matrix[ii][j] = 0 ; } for(int jj=0; jj<c; ++jj){ Matrix[i][jj] = 0 ; } } } } }
Gaussian Elimination Inverse Matrix
What does you block of code shown below do? I'm confused if (temp != j) for (k = 0; k < 2 * dimension; k++) { temporary = augmentedmatrix[j][k]; augmentedmatrix[j][k] = augmentedmatrix[temp][k]; augmentedmatrix[temp][k] = temporary; }
Edit: the original question was how to compute inverse matrix with Gaussian Elimination. OP has stucked on the actual elimination part of the algorithm. now if element 1,1 is not zero then you are going to zero element 2,1 using matrix elementary operations: F_s,t - interchange rows s and t F_s,t_(a) - add row t*a to s F_s_(a) - multiply row s by a You can also start by zeroing 1,2 element. These operations correspond to elementary matrices ( and to linear transformation matrices). Because each invertible matrix can be expressed as product of some elementary matrices A = P1,...,Pk,Ql,...,Q1 which are invertible, we can retrieve inverse of A, A_inverse by applying corresponding operations to original matrix A, and this is the same as multiplication A by P,Q: A_inverse = Q1_inv,...,Ql_inv,Pk_inv,...,P1_inv For each row in a matrix, if the row does not consist of only zeros, then the left-most non-zero entry is called the leading coefficient (or pivot) of that row. So if two leading coefficients are in the same column, then a row operation of type 3 (see above) could be used to make one of those coefficients zero. Then by using the row swapping operation, one can always order the rows so that for every non-zero row, the leading coefficient is to the right of the leading coefficient of the row above. If this is the case, then matrix is said to be in row echelon form. So the lower left part of the matrix contains only zeros, and all of the zero rows are below the non-zero rows. The word "echelon" is used here because one can roughly think of the rows being ranked by their size, with the largest being at the top and the smallest being at the bottom. now basically you should store augmented matrix float augmentedmatrix[maximum][2*maximum] ; so you can perform operations on matrix A and on identity matrix simultaneously. Fill identity matrix: /* augmenting with identity matrix of similar dimensions */ for(i=0;i<dimension; i++) for(j=dimension; j<2*dimension; j++) if(i==j%dimension) augmentedmatrix[i][j]=1; else augmentedmatrix[i][j]=0; and perform Gauss-Jordan elimination on the extended matrix by: /* finding maximum jth column element in last (dimension-j) rows */ /* swapping row which has maximum jth column element */ /* performing row operations to form required identity matrix out of the input matrix */ What you are missing is: /* using gauss-jordan elimination */ for (j = 0; j < dimension; j++) { temp = j; /* finding maximum jth column element in last (dimension-j) rows */ for (i = j + 1; i < dimension; i++) if (augmentedmatrix[i][j] > augmentedmatrix[temp][j]) temp = i; if (fabs(augmentedmatrix[temp][j]) < minvalue) { printf("\n Elements are too small to deal with !!!"); return -1; } /* swapping row which has maximum jth column element */ if (temp != j) for (k = 0; k < 2 * dimension; k++) { temporary = augmentedmatrix[j][k]; augmentedmatrix[j][k] = augmentedmatrix[temp][k]; augmentedmatrix[temp][k] = temporary; } /* performing row operations to form required identity matrix out of the input matrix */ for (i = 0; i < dimension; i++) if (i != j) { r = augmentedmatrix[i][j]; for (k = 0; k < 2 * dimension; k++) augmentedmatrix[i][k] -= augmentedmatrix[j][k] * r / augmentedmatrix[j][j]; } else { r = augmentedmatrix[i][j]; for (k = 0; k < 2 * dimension; k++) augmentedmatrix[i][k] /= r; } }