Can anyone help me identify the time complexity of the following code?
Background: this is a HackerRank algorithm problem where the editorial section lists the solution with a time complexity of O(n^2) but I think its O(n).
I believe its O(n) because we're only using one loop to traverse the 2 dimensional array and are not using nested loops and only looping over the array once.
Am I correct or am I missing something?
Solution
//arr is a 2 dimensional array containing an equal number of rows and columns
function diagonalDifference(arr) {
let diff = 0;
const length = arr.length - 1;
for (let i = 0; i < arr.length; i++) {
diff += arr[i][i] - arr[i][length - i];
}
return Math.abs(diff);
}
Problem
Given a square matrix (AKA 2D Array), calculate the absolute difference between the sums of its diagonals.
For example, the square matrix arr is shown below:
1 2 3
4 5 6
9 8 9
The left-to-right diagonal = 1 + 5 + 9 = 15. The right to left diagonal = 3 + 5 + 9 = 17. Their absolute difference is |15 - 17| = 2.
Am I correct or am I missing something?
You are right, but I think their categorization of O(n^2) may be due to the interpretation wherein n refers to the n of the input (i.e. the side-length of the matrix). In this case, the number of elements in the matrix itself is exactly n^2, and thus any solution (since any solution must read in all n^2 inputs) is Ω(n^2) (where Ω roughly means "at least").
Your categorization of the solution as O(n) is correct if we say that n refers to the size of the whole input matrix.
The time complexity of the code is O(n) where is the length of the matrix. As you have correctly said the loop is running only once and that too equals the number of rows/ columns viz the length of the matrix. there are no nested loops. So the time complexity is definitely, O(n) for all the cases best, worst, avg.
My solution:
function diagonalDifference(arr) {
let leftDiagSum = 0;
let rightDiagSum = 0;
for (let i = 0; i < arr.length; i++) {
leftDiagSum += arr[i][i];
rightDiagSum += arr[i][arr[i].length - (i + 1)];
}
let sum = Math.abs(leftDiagSum - rightDiagSum);
return sum;
}
You can try this also:
`
function diagonalDifference(arr) {
let firstDiagonalSum = arr[0][0] + arr[1][1] + arr[2][2];
let secondDiagonalSum = arr[0][2] + arr[1][1] + arr[2][0];
let result = (secondDiagonalSum) - (firstDiagonalSum);
return result;
}`
in python :
def diagonalDifference(arr):
d1 = 0
d2 = 0
n = len(arr)
for i in range(0, n):
for j in range(0, n):
if (i == j):
d1 += arr[i][j]
if (i == n - j - 1):
d2 += arr[i][j]
return abs(d1 - d2)
Related
I been practicing my algorithm solving skill by solving HackerRank problems. However, one of the problems is giving me a difficult time to understand. It is about calculating absolute difference between sum of diagonals in a square matrix "arr".
If the square matrix is like this:
1 2 3
4 5 6
5 2 4
and the solution is like this:
1st diagonal = 1 + 5 + 4 = 10
2nd diagonal = 3 + 5 + 5 = 13
difference = |10 - 13| = 3 <-- This should appear!
So, I came up with this solution:
function diagonalDifference(arr) {
let first = 0;
let second = 0;
let diff = 0;
for(let i = 0; i < arr.length; i++){
first += arr[i][i];
second += arr[i][arr.length-i-1]
}
diff = first - second;
return Math.abs(diff);
}
However, I figure out all other parts except the "second += arr[i][arr.length-i-1]" part.
I don't understand what [arr.length-i-1] is doing. Help me understand this.
When i starts at 0, arr.length - i - 1 starts at the last index in the array and iterates backwards towards the beginning. So, for example, if there are 4 elements, it'll access index [3], then [2], then [1], then [0].
For this problem, the array of arrays is square. arr[i][arr.length - i - 1] will result in arr[0][2], then arr[1][1], then arr[2][0] - which is the diagonal from the top right to the bottom left (which the problem is asking you to calculate).
If there were 5 subarrays, the indicies iterated over would be arr[0][4], arr[1][3], arr[2][2], arr[3][1], arr[4][0]. And so on.
UPDATE AT THE BOTTOM OF THE POST!
This is the first part of a program that will eventually make SVG star polygons.
Only star polygons that can be made in one go, I.e. like a Pentagram, NOT like a Hexagram that can only be made with at least 2 shapes I.e. 2 inverted and overlapping triangles.
I'm almost finished with this. Everything seems to be working well and I'm getting the out put I want except there's an empty array item in every other array produced for some reason.
In this part
I'm printing to the console (for testing) an object with items named with numbers that represent the number of points / sides of a regular polygon. Within each item is an array with the lower half of all the non factoring numbers of that item (number) I.e. an array of numbers that when the number (item name) is divided by them, it will return a fraction.
E.g. Object
starPolygons{'5': 2, '7': 3, 2, '8': 3, '9': 4, 2, …}
(Representation of: Pentagon: Heptagon: Octagon: Nonagon: …)
What "qty_test" functions does
The points variable (= 8 for example) is sent to a function (qty_test) which is used to find the number of divisors I need to test and inside it is divided by 2. If the result is a fraction, it will return the result rounded back, and if its even, it will return the result - 1.
//8 has more divisors to cycle through -so its a better example!
//In the real program, it will be 5.
let points = 8;
let starPolygons = {};
E.g. qty_test
point (=8) ()=> n /= 2 (=4)
if n - n(rounded-back) = 0?
true returns (n -1) and false returns n(rounded-back)
function qty_test(n) {
n /= 2;
let divisorQTY = n - Math.floor(n) !== 0;
return divisorQTY ? Math.floor(n) : (n - 1);
};
What divisor_test functions does
This function uses the number returned from the previous function (n) to set a for loop that tests every cycle if n is not a factor of the value of the points variable. I.e. points divided by n returns a fraction and if it returns true, meaning a fraction was produced n`s value is indexed in an array and decreaces by 1 (--n).
E.g. divisor_test
n(=8) / 2 = 4 << 2 will be ignored.
n(=8) / 3 = 2.666... << 3 will be collected
function divisor_test(n) {
let nonFactors = [];
n = qty_test(n);
for (let index = 0; index <= n; index++) {
let quotient = (points / n) - Math.floor(points / n) !== 0;
quotient ? nonFactors[index] = n-- : --n;
};
return nonFactors;
};
The program cycles through 5 - 360 and indexes an object with numbers (5-360) and array list of their lower half non factor numbers.
while (points <= 360) {
nonFactors = divisor_test(points);
let testArray = nonFactors.length;
if (testArray) starPolygons[String(points)] = nonFactors;
points++;
};
console.log(starPolygons);
AS YOU CAN SEE IN THIS IMAGE. THE PATTERN OF EMPTY ARRAY INDEXES / ITEMS
UPDATE: I NOTICED THE PATTERN OF ARRAYS WITH THE EMPTY SLOTS HAS AN INTERESTING QUALITY TO IT. This is so cool and so confusing as to what causes this...
During the Iteration you are forcing the array to add an empty item.
So, the empty object appears when the array has only two items in it and you are trying to nonFactors[3] = n--;
and thats what causes the issue.
So a more simplified version of the for loop with the solution will be
for (let index = 0; index <= n; index++) {
let quotient = (points / n) - Math.floor(points / n) !== 0;
if(quotient){
if(index > nonFactors.length){
nonFactors[index - 1] = n--
}else{
nonFactors[index] = n--
}
}else{
n = n -1;
}
};
I solved my problem
There's an answer here that already explains what causes the problem and also gives a working solution by Nadav Hury (upvote his answer. I bet his answer generally works better in more situations)! So I will just post my own version of a solution.
for (let index = 0; n >= 2; index++) {
let quotient = (points / n) - Math.floor(points / n) !== 0;
if (quotient) nonFactors[index] = n--;
else --n, --index;
};
The culprit is that in the line with checking the quotient
quotient ? nonFactors[index] = n-- : --n;
you added setting the value only for the case when it is present that is equal to true but you didn't add to the case when it is false correct solution is:
quotient ? nonFactors[index] = n-- : nonFactors[index] = --n;.
I'm trying to solve all the lessons on codility but I failed to do so on the following problem: Ladder by codility
I've searched all over the internet and I'm not finding a answer that satisfies me because no one answers why the max variable impacts so much the result.
So, before posting the code, I'll explain the thinking.
By looking at it I didn't need much time to understand that the total number of combinations it's a Fibonacci number, and removing the 0 from the Fibonacci array, I'd find the answer really fast.
Now, afterwards, they told that we should return the number of combinations modulus 2^B[i].
So far so good, and I decided to submit it without the var max, then I got a score of 37%.. I searched all over the internet and the 100% result was similar to mine but they added that max = Math.pow(2,30).
Can anyone explain to me how and why that max influences so much the score?
My Code:
// Powers 2 to num
function pow(num){
return Math.pow(2,num);
}
// Returns a array with all fibonacci numbers except for 0
function fibArray(num){
// const max = pow(30); -> Adding this max to the fibonaccy array makes the answer be 100%
const arr = [0,1,1];
let current = 2;
while(current<=num){
current++;
// next = arr[current-1]+arr[current-2] % max;
next = arr[current-1]+arr[current-2]; // Without this max it's 30 %
arr.push(next);
}
arr.shift(); // remove 0
return arr;
}
function solution(A, B) {
let f = fibArray(A.length + 1);
let res = new Array(A.length);
for (let i = 0; i < A.length; ++i) {
res[i] = f[A[i]] % (pow(B[i]));
}
return res;
}
console.log(solution([4,4,5,5,1],[3,2,4,3,1])); //5,1,8,0,1
// Note that the console.log wont differ in this solution having max set or not.
// Running the exercise on Codility shows the full log with all details
// of where it passed and where it failed.
The limits for input parameters are:
Assume that:
L is an integer within the range [1..50,000];
each element of array A is an integer within the range [1..L];
each element of array B is an integer within the range [1..30].
So the array f in fibArray can be 50,001 long.
Fibonacci numbers grow exponentially; according to this page, the 50,000th Fib number has over 10,000 digits.
Javascript does not have built-in support for arbitrary precision integers, and even doubles only offer ~14 s.f. of precision. So with your modified code, you will get "garbage" values for any significant value of L. This is why you only got 30%.
But why is max necessary? Modulo math tells us that:
(a + b) % c = ([a % c] + [b % c]) % c
So by applying % max to the iterative calculation step arr[current-1] + arr[current-2], every element in fibArray becomes its corresponding Fib number mod max, without any variable exceeding the value of max (or built-in integer types) at any time:
fibArray[2] = (fibArray[1] + fibArray[0]) % max = (F1 + F0) % max = F2 % max
fibArray[3] = (F2 % max + F1) % max = (F2 + F1) % max = F3 % max
fibArray[4] = (F3 % max + F2 % max) = (F3 + F2) % max = F4 % max
and so on ...
(Fn is the n-th Fib number)
Note that as B[i] will never exceed 30, pow(2, B[i]) <= max; therefore, since max is always divisible by pow(2, B[i]), applying % max does not affect the final result.
Here is a python 100% answer that I hope offers an explanation :-)
In a nutshell; modulus % is similar to 'bitwise and' & for certain numbers.
eg any number % 10 is equivalent to the right most digit.
284%10 = 4
1994%10 = 4
FACTS OF LIFE:
for multiples of 2 -> X % Y is equivalent to X & ( Y - 1 )
precomputing (2**i)-1 for i in range(1, 31) is faster than computing everything in B when super large arrays are given as args for this particular lesson.
Thus fib(A[i]) & pb[B[i]] will be faster to compute than an X % Y style thingy.
https://app.codility.com/demo/results/trainingEXWWGY-UUR/
And for completeness the code is here.
https://github.com/niall-oc/things/blob/master/codility/ladder.py
Here is my explanation and solution in C++:
Compute the first L fibonacci numbers. Each calculation needs modulo 2^30 because the 50000th fibonacci number cannot be stored even in long double, it is so big. Since INT_MAX is 2^31, the summary of previously modulo'd numbers by 2^30 cannot exceed that. Therefore, we do not need to have bigger store and/or casting.
Go through the arrays executing the lookup and modulos. We can be sure this gives the correct result since modulo 2^30 does not take any information away. E.g. modulo 100 does not take away any information for subsequent modulo 10.
vector<int> solution(vector<int> &A, vector<int> &B)
{
const int L = A.size();
vector<int> fibonacci_numbers(L, 1);
fibonacci_numbers[1] = 2;
static const int pow_2_30 = pow(2, 30);
for (int i = 2; i < L; ++i) {
fibonacci_numbers[i] = (fibonacci_numbers[i - 1] + fibonacci_numbers[i - 2]) % pow_2_30;
}
vector<int> consecutive_answers(L, 0);
for (int i = 0; i < L; ++i) {
consecutive_answers[i] = fibonacci_numbers[A[i] - 1] % static_cast<int>(pow(2, B[i]));
}
return consecutive_answers;
}
so , I solved a problem online where I found the Largest product of any 5 consecutive numbers In a 1000-digit number :
var bignumber = "7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450";
var bigarray = bignumber.split("");
var prod = [];
var buy = 1;
var z = 4;
for (var i = 0; i < bigarray.length; i+=z) {
mult = bigarray[i];
for (var x = 1; x <= z; x++) {
mult *= bigarray[i+x];
}
prod.push(mult);
}
prod.sort(function(a, b){return b-a});
document.write(prod[0]);
where z can be the number of the consecutive digits i want minus 1 ,, and the solution was 40824 wich is correct I think solution
Later I found that this problem belonged to Project Euler here , but instead It's 13 consecutive digits , so when I tried to change z = 12 , it gave me an Incorrect solution ,, why ?
Your mistake is in line for (var i = 0; i < bigarray.length; i+=z) {:
Change the loop condition to i < bigarray.length - z. Reason: Since the inner for loop advances up to z indices, the outer loop has to guarantee that there are at least z elements remining from index i.
Change the loop increment to i+=1. Reason: Consider finding the largest product of two consecutive digits in input "1221". 1*2 = 2 and 2*1 = 2 is what your algorithm computes. The solution however is 2*2 = 4 which can only be found when considering every input digit as a possible starting point.
Once you made these changes, you could start optimizing your algorithm. For example, you can kind of re-use the mult value from the previous iteration.
You may do as follows;
function getMaxProductConseq(s,n){
return Math.max(...s.split("")
.map((_,i,a) => a.slice(i,i+n))
.slice(0,s.length-n+1)
.map(sa => sa.reduce((p,c) => +p * +c)));
}
var bignumber = "7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450";
console.log(getMaxProductConseq(bignumber,13));
I have to determine the mathematical formula to calculate a particular repeating position in a series of numbers. The list of numbers repeats ad infinitum and I need to find the number every n numbers in this list. So I want to find the *n*th item in a list of repeating y numbers.
For example, if my list has 7 digits (y=7) and I need every 5th item (n=5), how do I find that item?
The list would be like this (which I've grouped in fives for ease of viewing):
12345 67123 45671 23456 71234 56712 34567
I need to find in the first grouping number 5, then in the second grouping number 3, then 1 from the third group, then 6, then 4, then 2, then 7.
This needs to work for any number for y and n. I usually use a modulus for finding *n*th items, but only when the list keeps increasing in number and not resetting.
I'm trying to do this in Javascript or JQuery as it's a browser based problem, but I'm not very mathematical so I'm struggling to solve it.
Thanks!
Edit: I'm looking for a mathematical solution to this ideally but I'll explain a little more about the problem, but it may just add confusion. I have a list of items in a carousel arrangement. In my example there are 7 unique items (it could be any number), but the list in real terms is actually five times that size (nothing to do with the groups of 5 above) with four sets of duplicates that I create.
To give the illusion of scrolling to infinity, the list position is reset on the 'last' page (there are two pages in this example as items 1-7 span across the 5 item wide viewport). Those groups above represent pages as there are 5 items per page in my example. The duplicates provide the padding necessary to fill in any blank spaces that may occur when moving to the next page of items (page 2 for instance starts with 6 and 7 but then would be empty if it weren't for the duplicated 1,2 and 3). When the page goes past the last page (so if we try to go to page 3) then I reposition them further back in the list to page one, but offset so it looks like they are still going forwards forever.
This is why I can't use an array index and why it would be useful to have a mathematical solution. I realise there are carousels out there that do similar tasks to what I'm trying to achieve, but I have to use the one I've got!
Just loop every 5 characters, like so:
var data = "12345671234567123456712345671234567";
var results = [];
for(var i = 4; i < data.length; i += 5){
results.push(data[i]);
}
//results = [5, 3, 1, 6, 4, 2, 7]
If you want to use a variable x = 5; then your for loop would look like this:
for(var i = x - 1; i < data.length; i += x){...
There is no need to know y
If your input sequence doesn't terminate, then outputting every nth item will eventually produce its own repeating sequence. The period (length) of this repetition will be the lowest common multiple of the period of the input sequence (y) and the step size used for outputting its items (x).
If you want to output only the first repetition, then something like this should do the trick (untested):
var sequence = "1234567";
var x = 5;
var y = sequence.length;
var count = lcm(x, y);
var offset = 4;
var output = [];
for (var i = 0; i < count; i += x)
{
j = (offset + i) % y;
output.push(sequence[j]);
}
You should be able to find an algorithm for computing the LCM of two integers fairly easily.
A purely mathematical definition? Err..
T(n) = T(n-1) + K For all n > 0.
T(1) = K // If user wants the first element in the series, you return the Kth element.
T(0) = 0 // If the user want's a non-existent element, they get 0.
Where K denotes the interval.
n denotes the desired term.
T() denotes the function that generates the list.
Lets assume we want every Kth element.
T(1) = T(0) + K = K
T(2) = T(1) + K = 2K
T(3) = T(2) + K = 3K
T(n) = nk. // This looks like a promising equation. Let's prove it:
So n is any n > 1. The next step in the equation is n+1, so we need to prove that
T(n + 1) = k(n + 1).
So let's have a go.
T(n+1) = T(N+1-1) + K.
T(n+1) = T(n) + K
Assume that T(n) = nk.
T(n+1) = nk + k
T(n+1) = k(n + 1).
And there is your proof, by induction, that T(n) = nk.
That is about as mathematical as you're gonna get on SO.
Nice simple recurrence relation that describes it quite well there.
After your edit I make another solution;)
var n = 5, y = 7;
for (var i = 1; i<=y; i++) {
var offset = ( i*y - (i-1)*n ) % y;
var result = 0;
if (offset === n) {
result = y;
} else {
result = (n - offset) > 0 ? n - offset : offset;
}
console.log(result);
}
[5, 3, 1, 6, 4, 2, 7] in output.
JSFIDDLE: http://jsfiddle.net/mcrLQ/4/
function get(x, A, B) {
var r = (x * A) % B;
return r ? r : B;
}
var A = 5;
var B = 7;
var C = [];
for (var x = 1; x <= B; ++x) {
C.push(get(x, A, B));
}
console.log(C);
Result: [5, 3, 1, 6, 4, 2, 7]
http://jsfiddle.net/xRFTD/
var data = "12345 67123 45671 23456 71234 56712 34567";
var x = 5;
var y = 7;
var results = [];
var i = x - 1; // enumeration in string starts from zero
while ( i <= data.length){
results.push(data[i]);
i = i + x + 1;// +1 for spaces ignoring
}