Open-Source Internship opportunity by OpenGenus for programmers. Apply now.
Reading time: 30 minutes | Coding time: 12 minutes
Freivalds' algorithm is a probabilistic randomized algorithm used to verify matrix multiplication. Given three n x n matrices, Freivalds' algorithm determines in O(kn^2) whether the matrices are equal for a chosen k value with a probability of failure less than 2^-k.
Freivalds' algorithm
-
Input: n×n matrices A, B and C.
-
We would like to verify if A×B=C.
Choose a n×1 column vector r→∈{0,1}n, uniformly and at random. In other words, each component of the vector is either 0 or 1 and chosen randomly and uniformly. -
Compute A⋅(B⋅r) and C⋅r. This takes Θ(n^2) time.
-
Now comes the output
- If A⋅(B⋅r)≠C⋅r, output FALSE. In other words, we say that the given matrix multiplication is incorrect. This has a probability of correctness = 1.
- If A⋅(B⋅r)=C⋅r, output TRUE. In other words, we say that the given matrix multiplication is correct. This has a probability of correctness ≥1/2.
Example
Let A,B,C are the matrices such that A × B = C.A random two-element vector with entries equal to 0 or 1 is selected Vector r=[1,1] .
This yields the zero vector, suggesting the possibility that AB = C. However, if in a second trial the vector r=[1,0] is selected, the result becomes:
The result is nonzero, proving that in fact AB ≠ C.
There are four two-element 0/1 vectors, and half of them give the zero vector in this case Vector r=[0,0] and r=[1,1].so the chance of randomly selecting these in two trials (and falsely concluding that AB=C) is 1/22 or 1/4. In the general case, the proportion of r yielding the zero vector may be less than 1/2, and a larger number of trials would be used, rendering the probability of error very small.
Error analysis
Let p equal the probability of error. We claim that if A × B = C, then p = 0, and if A × B ≠ C, then p ≤ 1/2.
Case A × B = C:
Regardless of the value of vector r, since it uses only that A × B - C =0. Hence the probability for error in this case would be zero.
Case A × B ≠ C:
Since A × B ≠ C, we have that some element of D is nonzero. Suppose that the element d≠0.For some constant y. Using Bayes' Theorem, we can partition over y.
Pseudocode
- Generate an n × 1 random 0/1 vector r.
- Compute P = A × (Br) – Cr.
- Return true if P = ( 0, 0, …, 0 )T, return false otherwise.
Br = matrix B multiplied by Vector r.
Cr = matrix C multiplied by Vector r.
Complexity
-
Worst case time complexity:
Θ(kn^2)
-
Space complexity:
Θ(n^2)
k = number of times the algorithm iterates.
Implementations
- C++
C++
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int main(int argc, char **argv)
{
cout << "Enter the dimension of the matrices: " << endl;
int n;
cin >> n;
cout << "Enter the 1st matrix: " << endl;
double a[n][n];
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
cin >> a[i][j];
}
}
cout << "Enter the 2nd matrix: " << endl;
double b[n][n];
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
cin >> b[i][j];
}
}
cout << "Enter the result matrix: " << endl;
double c[n][n];
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
cin >> c[i][j];
}
}
//random generation of the r vector
// containing only 0/1 as its elements
double r[n][1];
for (int i = 0; i < n; i++)
{
r[i][0] = rand() % 2;
}
//test A * (b*r) - (C*) = 0
double br[n][1];
for (int i = 0; i < n; i++)
{
for (int j = 0; j < 1; j++)
{
for (int k = 0; k < n; k++)
{
br[i][j] = br[i][j] + b[i][k] * r[k][j];
}
}
}
double cr[n][1];
for (int i = 0; i < n; i++)
{
for (int j = 0; j < 1; j++)
{
for (int k = 0; k < n; k++)
{
cr[i][j] = cr[i][j] + c[i][k] * r[k][j];
}
}
}
double abr[n][1];
for (int i = 0; i < n; i++)
{
for (int j = 0; j < 1; j++)
{
for (int k = 0; k < n; k++)
{
abr[i][j] = abr[i][j] + a[i][k] * br[k][j];
}
}
}
// br = multiplyVector(b, r, n);
// cr = multiplyVector(c, r, n);
// abr = multiplyVector(a, br, n);
//abr-cr
for (int i = 0; i < n; i++)
{
abr[i][0] -= cr[i][0];
}
bool flag = true;
for (int i = 0; i < n; i++)
{
if (abr[i][0] == 0)
continue;
else
flag = false;
}
if (flag == true)
cout << "True";
else
cout << "False";
}
Applications
-
Fingerprinting is a well known technique, which is often used in designing Monte Carlo algorithms for verifying identities involving matrices, integers and polynomials.
-
Freivalds' algorithm frequently arises in introductions to probabilistic algorithms due to its simplicity and how it illustrates the superiority of probabilistic algorithms in practice for some problems.