You are on page 1of 11

Program 4: Program to implement Starssen’s Matrix Multiplication Algorithm and

analyze its Time Complexity.


Description:
Strassen's algorithm is an efficient method for matrix multiplication, especially for large
matrices. It's based on the principle of recursively breaking down matrices into smaller
submatrices to reduce the number of scalar multiplications required.
ALGORITHM:
• Divide a matrix of order of 2*2 recursively till we get the matrix of 2*2.
• Use the previous set of formulas to carry out 2*2 matrix multiplication.
• In this eight multiplication and four additions, subtraction are performed.
• Combine the result of two matrixes to find the final product or final matrix.
D1 = (a11 + a22) (b11 + b22)
D2 = (a21 + a22).b11
D3 = (b12 – b22).a11
D4 = (b21 – b11).a22
D5 = (a11 + a12).b22
D6 = (a21 – a11) . (b11 + b12)
D7 = (a12 – a22) . (b21 + b22)
C11 = d1 + d4 – d5 + d7
C12 = d3 + d5
C21 = d2 + d4
C22 = d1 + d3 – d2 – d6

Time complexity:

Using this recurrence relation, we get T(n)=O(nlog7)


• Worst case time complexity: Θ(n^2.8074)
• Best case time complexity: Θ(1)
• Space complexity: Θ(logn)
Strassen's algorithm has a time complexity of O(n^log2(7)), which approximately equals
O(n^2.81) due to the seven recursive calls it makes.

21
SOURCE CODE:
#include<bits/stdc++.h>
using namespace std;

//Function to add two matrices


vector<vector<int>> addMatrix(const vector<vector<int>>& A, const vector<vector<int>>&
B) {
int n = A.size();
vector<vector<int>> result(n, vector<int>(n, 0));

for (int i = 0; i < n; ++i) {


for (int j = 0; j < n; ++j) {
result[i][j] = A[i][j] + B[i][j];
}
}

return result;
}

// Function to subtract two matrices


vector<vector<int>> subtractMatrix(const vector<vector<int>>& A, const
vector<vector<int>>& B) {
int n = A.size();
vector<vector<int>> result(n, vector<int>(n, 0));

for (int i = 0; i < n; ++i) {


for (int j = 0; j < n; ++j) {
result[i][j] = A[i][j] - B[i][j];
}
}

return result;
}

// Function to multiply two matrices using the Strassen algorithm


vector<vector<int>> strassenMultiply(const vector<vector<int>>& A, const
vector<vector<int>>& B) {
int n = A.size();

// Base case: If matrices are 1x1, perform standard multiplication


if (n == 1) {
return {{A[0][0] * B[0][0]}};
}

// Divide matrices into four submatrices


int newSize = n / 2;

vector<vector<int>> A11(newSize, vector<int>(newSize, 0));

22
vector<vector<int>> A12(newSize, vector<int>(newSize, 0));
vector<vector<int>> A21(newSize, vector<int>(newSize, 0));
vector<vector<int>> A22(newSize, vector<int>(newSize, 0));

vector<vector<int>> B11(newSize, vector<int>(newSize, 0));


vector<vector<int>> B12(newSize, vector<int>(newSize, 0));
vector<vector<int>> B21(newSize, vector<int>(newSize, 0));
vector<vector<int>> B22(newSize, vector<int>(newSize, 0));

// Populate submatrices
for (int i = 0; i < newSize; ++i) {
for (int j = 0; j < newSize; ++j) {
A11[i][j] = A[i][j];
A12[i][j] = A[i][j + newSize];
A21[i][j] = A[i + newSize][j];
A22[i][j] = A[i + newSize][j + newSize];

B11[i][j] = B[i][j];
B12[i][j] = B[i][j + newSize];
B21[i][j] = B[i + newSize][j];
B22[i][j] = B[i + newSize][j + newSize];
}
}

// Calculate seven products using recursive calls


vector<vector<int>> M1 = strassenMultiply(addMatrix(A11, A22), addMatrix(B11, B22));
vector<vector<int>> M2 = strassenMultiply(addMatrix(A21, A22), B11);
vector<vector<int>> M3 = strassenMultiply(A11, subtractMatrix(B12, B22));
vector<vector<int>> M4 = strassenMultiply(A22, subtractMatrix(B21, B11));
vector<vector<int>> M5 = strassenMultiply(addMatrix(A11, A12), B22);
vector<vector<int>> M6 = strassenMultiply(subtractMatrix(A21, A11), addMatrix(B11,
B12));
vector<vector<int>> M7 = strassenMultiply(subtractMatrix(A12, A22), addMatrix(B21,
B22));

// Calculate result matrices


// Calculate result matrices
vector<vector<int>> C11 = subtractMatrix(addMatrix(M1, M4), addMatrix(M5, M7));
vector<vector<int>> C12 = addMatrix(M3, M5);
vector<vector<int>> C21 = addMatrix(M2, M4);
vector<vector<int>> C22 = subtractMatrix(subtractMatrix(addMatrix(M1, M3), M2),
addMatrix(M5, M6));

// newSize is already halved during the recursive calls in the Strassen algorithm

// Combine the result matrices into the final result matrix C


vector<vector<int>> result(n, vector<int>(n, 0));

23
for (int i = 0; i < newSize; ++i) {
for (int j = 0; j < newSize; ++j) {
result[i][j] = C11[i][j];
result[i][j + newSize] = C12[i][j];
result[i + newSize][j] = C21[i][j];
result[i + newSize][j + newSize] = C22[i][j];
}
}

return result;
}

// Function to print a matrix


void printMatrix(const vector<vector<int>>& matrix) {
for (const auto& row : matrix) {
for (int val : row) {
cout << val << " ";
}
cout << endl;
}
}

int main() {
// Example usage
vector<vector<int>> A = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
vector<vector<int>> B = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};

// Ensure matrices are square and of size 2^n


int n = A.size();
int m = B.size();
if (n != m || (n & (n - 1)) != 0) {
cerr << "Matrices must be square and of size 2^n." << endl;
return 1;
}

vector<vector<int>> result = strassenMultiply(A, B);

// Print the result matrix


cout << "Result Matrix:" << endl;
printMatrix(result);

return 0;
}

24
OUTPUT:

25
Week 3
Program 5: Program to find the shortest path using Floyd’s algorithm.
Description:
The Floyd Warshall Algorithm is an all pair shortest path algorithm
unlike Dijkstra and Bellman Ford which are single source shortest path algorithms. This
algorithm works for both the directed and undirected weighted graphs. But, it does not work
for the graphs with negative cycles (where the sum of the edges in a cycle is negative). It
follows Dynamic Programming approach to check every possible path going via every
possible node in order to calculate shortest distance between every pair of nodes.
ALGORITHM:
Step 1 − Construct an adjacency matrix A with all the costs of edges present in the graph. If
there is no path between two vertices, mark the value as ∞.
Step 2 − Derive another adjacency matrix A1 from A keeping the first row and first column
of the original adjacency matrix intact in A1. And for the remaining values, say A1[i,j],
if A[i,j]>A[i,k]+A[k,j] then replace A1[i,j] with A[i,k]+A[k,j]. Otherwise, do not change the
values. Here, in this step, k = 1 (first vertex acting as pivot).
Step 3 − Repeat Step 2 for all the vertices in the graph by changing the k value for every
pivot vertex until the final matrix is achieved.
Step 4 − The final adjacency matrix obtained is the final solution with all the shortest paths.

Time Complexity :
Best Case: O(V3)
• In the best-case scenario, the algorithm completes all iterations of the nested loops
without any relaxation steps needed.
• With V vertices, each loop iterates V times, resulting in a time complexity
of O(V3) for the best case.
Average Case: O(V3)
• The average-case time complexity of the Floyd-Warshall algorithm is also O(V3).
• This complexity holds true across various graph structures and densities, as the
algorithm’s performance primarily depends on the number of vertices and the number
of iterations needed to compute shortest paths between all pairs of vertices.
Worst Case: O(V3)
• Conversely, in the worst-case scenario, the algorithm performs relaxation steps for
each pair of vertices during all iterations of the nested loops.
• With V vertices, each loop iterates V times, resulting in a time complexity
of O(V3) for the worst case as well.

26
SOURCE CODE:
#include<bits/stdc++.h>
# define INF 10000000
using namespace std;

void floydWarshal(vector<vector<int>> &costMat ,int n) {


vector<vector<int>>cost(n,vector<int>(n));
for(int i = 0; i<n; i++)
for(int j = 0; j<n; j++)
cost[i][j] = costMat[i][j];

for(int k = 0; k<n; k++) {


for(int i = 0; i<n; i++)
for(int j = 0; j<n; j++)
if(cost[i][k]+cost[k][j] < cost[i][j])
cost[i][j] = cost[i][k]+cost[k][j];
}
cout << "The matrix:" << endl;
for(int i = 0; i<n; i++) {
for(int j = 0; j<n; j++)
cout << setw(3) << cost[i][j];
cout << endl;
}
}
int main(){
vector<vector<int>> costMat= {
{0, 3, 6, INF, INF, INF, INF},
{3, 0, 2, 1, INF, INF, INF},
{6, 2, 0, 1, 4, 2, INF},
{INF, 1, 1, 0, 2, INF, 4},
{INF, INF, 4, 2, 0, 2, 1},
{INF, INF, 2, INF, 2, 0, 1},
{INF, INF, INF, 4, 1, 1, 0}
};
int n=costMat.size();
floydWarshal(costMat,n);
return 0;
}

27
OUTPUT:

28
Program 6: Program to solve the knapsack problem using greedy method.
Description:
The basic idea of the greedy approach is to calculate the ratio profit/weight for each item and
sort the item on the basis of this ratio. Then take the item with the highest ratio and add them
as much as we can (can be the whole element or a fraction of it).
This will always give the maximum profit because, in each step it adds an element such that
this is the maximum possible profit for that much weight.

ALGORITHM:
• Calculate the ratio (profit/weight) for each item.
• Sort all the items in decreasing order of the ratio.
• Initialize res = 0, curr_cap = given_cap.
• Do the following for every item i in the sorted order:
• If the weight of the current item is less than or equal to the remaining capacity then
add the value of that item into the result
• Else add the current item as much as we can and break out of the loop.
• Return res.

Time Complexity:
• The greedy algorithm sorts the items based on their value-to-weight ratio and selects
items greedily until the knapsack is full.
• Sorting the items takes O(n log n) time, where n is the number of items.
• After sorting, selecting items and calculating the fractional values can be done in
linear time, O(n).
• Therefore, the overall time complexity of the greedy approach is O(n log n) due to
sorting.
Greedy Algorithm of Knapsack : O(n log n)

29
SOURCE CODE:
#include<bits/stdc++.h>
using namespace std;

bool comp(pair<int,int> &p1,pair<int,int> &p2){


double r1=(double)p1.second/p1.first;
double r2=(double)p2.second/p2.first;
return r1>r2;
}
double knapsack(vector<pair<int,int>>& Item,int W){
sort(Item.begin(),Item.end(),comp);
int n=Item.size();
double maxprofit=0.0;
for(int i=0;i<n;i++){
if(Item[i].first<W){
maxprofit+=Item[i].second;
W-=Item[i].first;
}else{
maxprofit+=Item[i].second*((double)W/Item[i].first);
break;
}
}
return maxprofit;
}
int main(){
int n;
cout<<"Enter the number of Items: ";
cin>>n;
vector<pair<int,int>>Item(n);
int W;
cout<<"Enter ther Weight of Bag: ";
cin>>W;
cout<<"Enter the Items Weight and Profit: "<<endl;
for(int i=0;i<n;i++){
cin>>Item[i].first;
cin>>Item[i].second;
}
cout<<"Max Profit is: "<<knapsack(Item,W);
return 0;
}

30
OUTPUT:

31

You might also like