Matrix multiplication Big O notation - java

I have a question regarding best case scenario for this piece of code and worst case scenario in Big O notation. From my point of view, it should be O (n^3) for both cases but some people disagree.
public int [][] multiply (int [][] A, int
[][] B, int n) {
int [][] C = new int[n][n]; - 1 ( ignored )
for(int i=0; i<n; i++) { - n
for(int j=0; j<n; j++) { - n
if(A[i][j ]!=0) { - 1 ( ignored )
for (int k=0; k<n; k++) { - n
C[i][k] += A[i][j]*B[j][k]; -
}
}
}
}
return C;
}

It is true that matrix multiplication takes O(n^3) time to run in average and worst cases. For 2 matrices of dimensions m x n and n x p respectively, there is going to be a total of mnp (or n^3 for simplicity) calculations, one for each entry in the resultant matrix. As for the best case, it depends on what your program does when it sees that at least one of the matrices is a zero matrix (all entries in the matrix are 0). If it can spot a zero matrix, then you can short circuit the program. In this case, the running time is going to be O(n^2) (you just scan a couple of n x n matrices at most), which is the best that can be achieved for matrix multiplication. If this optimization is not available, the program will run in O(n^3) in any case. Either way, you can still say that the best case is O(n^3) because of the definition of Big-O notation, but that is not of interest to most people.

The line of code shown above must be corrected.
C[i][k] += A[i][j]*B[j][k];
It should be corrected as follows.
C[i][j] += A[i][k]*B[k][j];

Related

Not understanding big O notation O(∑ i=0 n−1 (∑ j=i+1 n (j−i)))=O(∑ i=0 n−1 2 (1+n−i)(n−i))=O(n^3)

Working on the following problem:
Given a string s, find the length of the longest substring without repeating characters.
I'm using this brute force solution:
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length();
int res = 0;
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
if (checkRepetition(s, i, j)) {
res = Math.max(res, j - i + 1);
}
}
}
return res;
}
private boolean checkRepetition(String s, int start, int end) {
int[] chars = new int[128];
for (int i = start; i <= end; i++) {
char c = s.charAt(i);
chars[c]++;
if (chars[c] > 1) {
return false;
}
}
return true;
}
}
Tbe big O notation is as follows:
I understand that three nested iterations would result in a time complexity O(n^3).
I only see two sigma operators being used on the start of the formula, could someone enlighten me on where the third iteration comes to play in the beginning of the formula?
The first sum from i=0 to n-1 corresponds to the outer for loop of lengthOfLongestSubstring, which you can see iterates from i=0 to n-1.
The second sum from j = i+1 to n corresponds to the second for loop (you could be starting j at i+1 rather than i as there's no need to check length 0 sub-strings).
Generally, we would expect this particular double for loop structure to produce O(n^2) algorithms and a third for loop (from k=j+1 to n) to lead to O(n^3) ones. However, this general rule (k for loops iterating through all k-tuples of indices producing O(n^k) algorithms) is only the case when the work done inside the innermost for loop is constant. This is because having k for loops structured in this way produces O(n^k) total iterations, but you need to multiply the total number of iterations by the work done in each iteration to get the overall complexity.
From this idea, we can see that the reason lengthOfLongestSubstring is O(n^3) is because the work done inside of the body of the second for loop is not constant, but rather is O(n). checkRepitition(s, i, j) iterates from i to j, taking j-i time (hence the expression inside the second term of the sum). O(j-i) time is O(n) time in the worst case because i could be as low as 0, j as high as n, and of course O(n-0) = O(n) (it's not too hard to show that checkRepitions is O(n) in the average case as well).
As mentioned by a commenter, having a linear operation inside the body of your second for loop has the same practical effect in terms of complexity as having a third for loop, which would probably be easier to see as being O(n^3) (you could even imagine the function definition for checkRepitition, including its for loop, being pasted into lengthOfLongestSubstring in place to see the same result). But the basic idea is that doing O(n) work for each of the O(n^2) iterations of the 2 for loops means the total complexity is O(n)*O(n^2) = O(n^3).

How do I remove all "edges" from a node?

A question for my comp sci class goes like this:
We can represent a graph using an "adjacency matrix" int[][] matrix. matrix[i][j] will be non-zero if there is an edge FROM node i TO node j. In other words, node i considers node j to be its "neighbor" in this case. Note that this does not necessarily mean node j considers node i to be ITS neighbor, allowing for asymmetrical relationships. Each value could also be zero, indicating there is no edge/connection. The matrix will be n x n in size, where n is the number of nodes and nodes are numbered 0 to n-1, allowing any node to connect to any other node numbered within this range.
Given an adjacency matrix and int x, remove all edges to and from the node at index x.
I understand what an adjacency matrix is, but I don't understand what the problem means by edges, or what index x is.
My code so far looks like this:
public void removeEdgesFromNode(int[][] matrix, int x) {
for(int i = 0; i < matrix.length; i++) {
for(int j = 0; j < matrix[].length; j++) {
if(j == x || i == x) {
matrix[i, j] = 0;
}
}
}
}
There is an error occurring where it says
Compilation error on line 7: error: class expected
for(int j = 0; j < matrix[].length; j++) {
^
I don't think the way I'm solving the problem is correct, however I am attempting to start the problem at least.
The way you are solving the problem is correct, but not the most optimal.
The edges from x, would mean that matrix[x][j] would be 0 for each j.
The edges to x, would mean that matrix[i][x] would be 0 for each i.
In other words, all the elements matrix[i][j] where i or j are x, should be 0. Which is what you are doing.
First things first. Your error. matrix[].length should be matrix.length. Try your solution. It should work.
For the most optimal solution check Dariosicily answer. However with that one you need to make sure that the matrix is squared and x<n.
Your answer would go over every element of the matrix, so nxn= n^2 elements with complexity O(n^2), but his solution you only need to go over 2*n elements O(n), so when n is very big, you would notice the performance improvement. You can give it a try :)
You were close to the solution. Because you have a square n x n matrix and fixed an index x in the range 0 .. n - 1 you want to remove all elements staying in the xth row or in the xth column you can do it with one loop like below:
public static void removeEdgesFromNode(int[][] matrix, int x) {
int n = matrix.length;
for (int i = 0; i < n; ++i) {
matrix[i][x] = 0;
matrix[x][i] = 0;
}
}
If you use an x index equal or greater than n an ArrayIndexOutOfBoundsException exception will be thrown, it's up to you decide how to handle this scenario.

How to write this algorithm more efficiently

I have an assignment where I have to write an algorithm which 'splits' the array in two. Left side should be odd numbers, and right side should be even numbers. Both sides should be sorted in ascending order. I'm not allowed to use temp arrays or existing api.
I have managed to make a working method, problem is with an array of say 100 000 integers, it takes approximately 15 seconds to finish. The requirement is 0,1 seconds, so I obviously have a lot to improve. I'm not looking for someone to spoon-feed me the answer, just a nudge in the right direction. Please don't write any working code for me, though I would like to know if and why something I've written is bad!
What I have so far:
public static void delsortering(int[] a){
int oddnum = 0;
int n = a.length;
for(int k : a){ //finds how many odd numbers there are
if((k & 1) != 0) oddnum++;
}
for(int i = 0; i < n; i++){
if((a[i] & 1) != 0){ //finds odd numbers
for(int j = 0; j < n; j++){
if((a[j] & 1) == 0) //looks for even numbers to change pos with
switch(a, j, i);
}
}
}
for (int i = 0; i < n; i++){
int from = i < oddnum ? 0 : oddnum;
int to = i < oddnum ? oddnum - i: n - i + oddetall;
int m = maxValue(a, from, to); //finds max value in specified range
switch(a, m, to - 1); //puts said max value at specified index
}
}
Appreciate all the help I can get!
A better solution would be:
Firstly keep two variables that point to the first and last elements of the array e.g x=0; y=N-1.
Then start moving x to the right until you find an even number (all numbers until now are odd !!!), then start moving y to the left until you find an odd number (all number you examine while decreasing-moving left y are even until the first one odd you find !!!)
Swap values x,y ,increase x,y and repeat the same procedure until x,y get crossed.
Then you have the array with evens on the right and odd on the left but not ordered. So you could count during the above procedure the number of odds and evens in order to know where there are separated in the array let's say in k index.
Sort array[0 - k], Sort array[k+1 - N].
Complexity: O(n) for the first part (x,y are only once moving to one direction) and O(nlogn) for both sorts so O(nlogn) which is better than O(n^2) that is your solution.

knapsack 01 with twist

I'm doing a Knapsack in Java where we only use weights no value. The weightlimit is 1000. We get 5 weights scanned from keyboard which we use.
The twist is that you can actually go over 1000 aslong as its the closets to 1000. So in one scenario we have 2 possible weights 990 and 1010 and the program is suposed to pick the higher one.
The scanned numbers can never be higher then 1000.
package kapsackidone;
import java.util.Scanner;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.*;
public class Kapsack {
public static void main(String[] args) throws Exception {
BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
int [] wt=new int[5];
int W = 1000;
System.out.println("Enter Weight 5 weights");
for(int i=0; i<5; i++)
{
wt[i]=Integer.parseInt(reader.readLine());
}
System.out.println(knapsack(wt, W));
}
public static int knapsack(int wt[], int W) {
int N = wt.length;
int[][] V = new int[N + 1][W + 1];
for (int col = 0; col <= W; col++) {
V[0][col] = 0;
}
for (int row = 0; row <= N; row++) {
V[row][0] = 0;
}
for (int item=1;item<=N;item++){
for (int weight=1;weight<=W;weight++){
if(wt[item-1] > weight)
{
V[item][weight] = V[item-1][weight];
}
else if((weight - V[item-1][weight]) < (weight - (V[item-1][weight - wt[item-1]] + wt[item-1])))
{
V[item][weight] = V[item-1][weight];
}
else
{
V[item][weight] = V[item-1][weight - wt[item-1]] + wt[item-1];
}
}
}
return V[N][W];
}
}
I am really struggling with how I can get this done.
Before you ask no its not homework im gonna be a project manager for a new group of people that consist of developers so im just trying to learn some java so that i understand a bit of what they do even tho i doubt i will be able to help with the coding.
I would just run it twice.
In first run find the "classic" solution with best weight less than 1000.
In second run, increase the max value 1000 to the max possible value which is allowed based on previous solution.
Dont worry about "it is two times slower", multiplying complexity by constant does not change the complexity, which is the important thing in knapsack problem.
If your code is working then you can probably count the best solution as this
System.out.println(knapsack(wt,2*W - knapsack(wt, W));
Or you can write it as this to be more clear what is happening (it does exactly the same as that one-line above)
int bestClassicSolution = knapsack(wt, W);
int differenceAgainstMaxWeight = W - bestClassicSolution;
int newMaxWeight = W + differenceAgainstMaxWeight;
int bestSolution = knapsack(wt, newMaxWeight);
System.out.println(bestSolution);
EDIT : The solution above works for this condition select as big solution as possible, but it must not differ from 1000 more than "below 1000" best solution. The OP actually wants little different thing - the "limit" stays, but it should be the closest to the 1000 but as high as possible.
So real solution would to create reversed knapsack method, which will find the solution with minimum value BUT must be bigger than "min" variable.
public static void main(String[] args) throws Exception {
BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
int [] wt=new int[5];
int W = 1000;
System.out.println("Enter Weight 5 weights");
for(int i=0; i<5; i++)
{
wt[i]=Integer.parseInt(reader.readLine());
}
int bestClassicSolution = knapsack(wt, W);
int differenceAgainstMaxWeight = W - bestClassicSolution;
int newMaxWeight = W + differenceAgainstMaxWeight;
int bestMaxSolution = reversedKnapsack(wt, newMaxWeight, W);
int differenceAgainstWeightAboveW = W - bestMaxSolution;
if (differenceAgainstWeightAboveW <= differenceAgainstMaxWeight){
System.out.println(bestMaxSolution);
} else {
System.out.println(bestClassicSolution);
}
}
public static int reversedKnapsack(int wt[], int W, int min) {
//similar to knapsack method, but the solution must be as small as possible and must be bigger than min variable
}
Verbatim from Wikipedia — Subset sum problem.
The problem can be solved in pseudo-polynomial time using dynamic programming. Suppose the sequence is
x1, ..., xN
and we wish to determine if there is a nonempty subset which sums to zero. Define the boolean-valued function Q(i, s) to be the value (true or false) if
"there is a nonempty subset of x1, ..., xi which sums to s".
Thus, the solution to the problem "Given a set of integers, is there a non-empty subset whose sum is zero?" is the value of Q(N, 0).
Let A be the sum of the negative values and B the sum of the positive values. Clearly, Q(i, s) = false, if s < A or s > B. So these values do not need to be stored or computed.
Create an array to hold the values Q(i, s) for 1 ≤ i ≤ N and A ≤ s ≤ B.
The array can now be filled in using a simple recursion. Initially, for A ≤ s ≤ B, set
Q(1, s) := (x1 == s)
where == is a boolean function that returns true if x1 is equal to s, false otherwise.
Then, for i = 2, …, N, set
Q(i, s) := Q(i − 1, s) or (xi == s) or Q(i − 1, s − xi), for A ≤ s ≤ B.
After computing the values of Q, we may loop through them, and take the true value which is closest to the limit.
As for the value of S, we need to take the sum of the weights given to us.
The classical knapsack problem is discussed in a Wikipedia article; the dynamic programming formulation for the classical problem can be adapted to the following problem.
Given weights w_1,...,w_n and a target capacity W, find a subset of the items for which the total weight is minimal, but larger than W.
To avoid pathological cases, we assume that the sum of the weights is largert than W, otherwise there is no solution. Let W_MAX denote the sum of all weights.
For the dynamic programming formulation, let
m[i,j] for each i in 0,...,n and j in 0,...,W_MAX
denote the minimum weight larger than W attainable by discarding weights from 0,...,i with total weight exactly j.
We obtain
m[0,j] = W_MAX for each j in 0,...n
and get the recurrence relation
m[i,j] = min {
m[i-1, i ], // corresponds to keeping weight i
m[i-1, j - w[i]] - w[i] // corresponds to discarding weight i
}
and evaluation can be implemented by iterating i=0,...,n and j=0,...,W_MAX; accedd to m outside of these bounds must be assumed to yield W_MAX. Similar to the classical knapsack problem, the actual set of items to discard can be found by backtracking.
Finally, the given instance can be optimized twice; first with the algorithm above, then with the classical knapsack algorithm.
I would evaluate this problem first as a classical knapsack problem taking value[i] = weight[i] ;, where i is the i'th item and maximum weight to be the given max_wt (1000 kg) and item[] be an array containing the items in ascending order of their weights . Let the answer of this problem be x , say 990 kg , now i would calculate the difference 'd' , d = max_wt - x ; iterate over item[] till item[i] exceeds d : int i = 0 ;
while(item[i] < d )
i++;
,lastly add the first item that exceeds 'd' to the answer you got through the classical knapsack problem :
answer = dp[n-1][w-1] + item[i] \\dp[n-1][w-1] is the answer of the classical
\\knapsack problem

Big O for 2D for loop

I have to write down the Big O notation of an algorithm I had to think up for my homework.
I'm able to tell that the code below is O(n^2). Because for every x I have to go through all of the y's and it becomes slower as the world grows larger.
int[][] world = new world[20][20];
for (int x = 0; x < 20; x++)
{
for (int y = 0; y < 20; y++)
{
..
}
}
But, for another question I have to go through the bottom half of the world, so my y loop gets halved.
int[][] world = new world[20][20];
for (int x = 0; x < 20; x++)
{
for (int y = 10; y < 20; y++)
{
..
}
}
I'm not quite sure what Big O notation is appropriate for the above loop, is it still O(n^2) because it still becomes slower the bigger the world gets? Or is it O(log n) because the y is halved?
it is simply speaking O(n*n/2)=O(n^2) since constants arent considered in big O.
It's O(n^2) as y is still a function of n i.e. O(n^2/2) is still O(n^2).
Both of your algorithms are in fact O(n) (assuming n is the number of bits in the input, which is the common definition). The first one touches each "pixel" in the world array once, so it's easy to see it is O(n). The second one touches half of the pixels, but is still O(n) since O(n/2) = O(n). In both cases, doubling the size of the world array will more or less double the execution time, which is typical of O(n) algorithms.

Categories