diff --git a/DIRECTORY.md b/DIRECTORY.md index 3930e67fd8a..63964913e39 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -115,6 +115,9 @@ * [Linear Probing Hash Table](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/hashing/linear_probing_hash_table.cpp) * [Quadratic Probing Hash Table](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/hashing/quadratic_probing_hash_table.cpp) +## Linear Algebra + * [Gram Schmidt](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/linear_algebra/gram_schmidt.cpp) + ## Machine Learning * [Adaline Learning](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/machine_learning/adaline_learning.cpp) * [Kohonen Som Topology](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/machine_learning/kohonen_som_topology.cpp) diff --git a/linear_algebra/gram_schmidt.cpp b/linear_algebra/gram_schmidt.cpp new file mode 100644 index 00000000000..09ebc8d7c89 --- /dev/null +++ b/linear_algebra/gram_schmidt.cpp @@ -0,0 +1,251 @@ +/** + * @file + * @brief [Gram Schmidt Orthogonalisation Process](https://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process) + * + * @details + * Takes the input of Linearly Independent Vectors, + * returns vectors orthogonal to each other. + * + * ### Algorithm + * Take the first vector of given LI vectors as first vector of Orthogonal vectors. + * Take projection of second input vector on the first vector of Orthogonal vector + * and subtract it from the 2nd LI vector. + * Take projection of third vector on the second vector of Othogonal vectors and subtract it from the 3rd LI vector. + * Keep repeating the above process until all the vectors in the given input array are exhausted. + * + * For Example: + * In R2, + * Input LI Vectors={(3,1),(2,2)} + * then Orthogonal Vectors= {(3, 1),(-0.4, 1.2)} + * + * Have defined maximum dimension of vectors to be 10 and number of vectors + * taken is 20. + * Please do not give linearly dependent vectors + * + * + * @author [Akanksha Gupta](https://github.com/Akanksha-Gupta920) + */ + +#include /// for io operations +#include /// for assert +#include /// for fabs +#include /// for std::array + +/** + * @namespace linear_algebra + * @brief Linear Algebra algorithms + */ +namespace linear_algebra { +/** + * @namespace gram_schmidt + * @brief Functions for [Gram Schmidt Orthogonalisation Process](https://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process) + */ +namespace gram_schmidt { +/** + * Dot product function. + * Takes 2 vectors along with their dimension as input and returns the dot product. + * @param x vector 1 + * @param y vector 2 + * @param c dimension of the vectors + * + * @returns sum + */ +double dot_product(const std::array& x, const std::array& y, const int& c) { + double sum = 0; + for (int i = 0; i < c; ++i) { + sum += x[i] * y[i]; + } + return sum; +} + +/** + * Projection Function + * Takes input of 2 vectors along with their dimension and evaluates their projection in temp + * + * @param x Vector 1 + * @param y Vector 2 + * @param c dimension of each vector + * + * @returns factor + */ +double projection(const std::array& x,const std::array& y, + const int& c) { + double dot = dot_product(x, y, c); ///The dot product of two vectors is taken + double anorm = dot_product(y, y, c); ///The norm of the second vector is taken. + double factor = dot / anorm; ///multiply that factor with every element in a 3rd vector, whose initial values are same as the 2nd vector. + return factor; +} + +/** + * Function to print the orthogonalised vector + * + * @param r number of vectors + * @param c dimenaion of vectors + * @param B stores orthogonalised vectors + * + * @returns void + */ +void display(const int& r,const int& c,const std::array, 20>& B) { + for (int i = 0; i < r; ++i) { + std::cout << "Vector " << i + 1 << ": "; + for (int j = 0; j < c; ++j) { + std::cout << B[i][j] << " "; + } + std::cout << '\n'; + } +} + +/** + * Function for the process of Gram Schimdt Process + * @param r number of vectors + * @param c dimension of vectors + * @param A stores input of given LI vectors + * @param B stores orthogonalised vectors + * + * @returns void + */ +void gram_schmidt(int r,const int& c,const std::array, 20>& A, + std::array, 20> B) { + if (c < r) { /// we check whether appropriate dimensions are given or not. + std::cout + << "Dimension of vector is less than number of vector, hence \n first " + << c << " vectors are orthogonalised\n"; + r = c; + } + + int k = 1; + + while (k <= r) { + if (k == 1) { + for (int j = 0; j < c; j++) B[0][j] = A[0][j]; ///First vector is copied as it is. + } + + else { + std::array all_projection{}; ///array to store projections + for (int i = 0; i < c; ++i) { + all_projection[i] = 0; ///First initialised to zero + } + + int l = 1; + while (l < k) { + std::array temp{}; ///to store previous projected array + double factor; ///to store the factor by which the previous array will change + factor = projection(A[k - 1], B[l - 1], c); + for(int i = 0; i < c; ++i) + temp[i] = B[l - 1][i] * factor; ///projected array created + for (int j = 0; j < c; ++j) { + all_projection[j] = all_projection[j] + temp[j]; ///we take the projection with all the previous vector and add them. + } + l++; + } + for (int i = 0; i < c; ++i) { + B[k - 1][i] = A[k - 1][i] - all_projection[i]; ///subtract total projection vector from the input vector + } + } + k++; + } + display(r, c, B); //for displaying orthogoanlised vectors +} +} // namespace gram_schmidt +} // namespace linear_algebra +/** + * Test Function. Process has been tested for 3 Sample Inputs + * @returns void + */ +static void test() { + std::array, 20> a1 = { + {{1, 0, 1, 0}, {1, 1, 1, 1}, {0, 1, 2, 1}}}; + std::array, 20> b1 = {{0}}; + double dot1 = 0; + linear_algebra::gram_schmidt::gram_schmidt(3, 4, a1, b1); + int flag = 1; + for (int i = 0; i < 2; ++i) + for (int j = i + 1; j < 3; ++j) { + dot1 = fabs(linear_algebra::gram_schmidt::dot_product(b1[i], b1[j], 4)); + if (dot1 > 0.1) { + flag = 0; + break; + } + } + if (flag == 0) std::cout << "Vectors are linearly dependent\n"; + assert(flag == 1); + std::cout << "Passed Test Case 1\n "; + + std::array, 20> a2 = {{{3, 1}, {2, 2}}}; + std::array, 20> b2 = {{0}}; + double dot2 = 0; + linear_algebra::gram_schmidt::gram_schmidt(2, 2, a2, b2); + flag = 1; + for (int i = 0; i < 1; ++i) + for (int j = i + 1; j < 2; ++j) { + dot2 = fabs(linear_algebra::gram_schmidt::dot_product(b2[i], b2[j], 2)); + if (dot2 > 0.1) { + flag = 0; + break; + } + } + if (flag == 0) std::cout << "Vectors are linearly dependent\n"; + assert(flag == 1); + std::cout << "Passed Test Case 2\n"; + + std::array, 20> a3 = {{{1, 2, 2}, {-4, 3, 2}}}; + std::array, 20> b3 = {{0}}; + double dot3 = 0; + linear_algebra::gram_schmidt::gram_schmidt(2, 3, a3, b3); + flag = 1; + for (int i = 0; i < 1; ++i) + for (int j = i + 1; j < 2; ++j) { + dot3 = fabs(linear_algebra::gram_schmidt::dot_product(b3[i], b3[j], 3)); + if (dot3 > 0.1) { + flag = 0; + break; + } + } + if (flag == 0) std::cout << "Vectors are linearly dependent\n" ; + assert(flag == 1); + std::cout << "Passed Test Case 3\n"; +} + +/** + * @brief Main Function + * @return 0 on exit + */ +int main() { + int r=0, c=0; + test(); // perform self tests + std::cout << "Enter the dimension of your vectors\n"; + std::cin >> c; + std::cout << "Enter the number of vectors you will enter\n"; + std::cin >> r; + + std::array, 20> + A{}; ///a 2-D array for storing all vectors + std::array, 20> B = { + {0}}; /// a 2-D array for storing orthogonalised vectors + /// storing vectors in array A + for (int i = 0; i < r; ++i) { + std::cout << "Enter vector " << i + 1 <<'\n'; ///Input of vectors is taken + for (int j = 0; j < c; ++j) { + std::cout << "Value " << j + 1 << "th of vector: "; + std::cin >> A[i][j]; + } + std::cout <<'\n'; + } + + linear_algebra::gram_schmidt::gram_schmidt(r, c, A, B); + + double dot = 0; + int flag = 1; ///To check whether vectors are orthogonal or not + for (int i = 0; i < r - 1; ++i) { + for (int j = i + 1; j < r; ++j) { + dot = fabs(linear_algebra::gram_schmidt::dot_product(B[i], B[j], c)); + if (dot > 0.1) /// take make the process numerically stable, upper bound for the dot product take 0.1 + { + flag = 0; + break; + } + } + } + if (flag == 0) std::cout << "Vectors are linearly dependent\n"; + return 0; +}