Double loop don't know how to get rid - javascript

I have a function that gets a number and has to calculate the multiplication for each number with the rest of the numbers in the sequence.
If the input is 10, it should calculate the multiplication between 1x1, 1x2, 1x3, .... 10x1, 10x2, 10x3, .... 10x10. (passing through all the numbers sequentially)
So I thought at first sight that I need a double loop to do all possible multiplications but for big numbers it executes following O(n*n) which is too slow.
I heard there is a way to use only one loop. Do you know any post related with this subject? The only ones I found doesn't take into count that I need to perform the calculation foreach number by the rest of the numbers of the array.
Here the code:
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
// do i*j
}
}

Here's a way to do it with one loop (map), with the help of recursion.
const order = 10;
let sequence = [];
for (let i = 0; i < order; i++) {
sequence.push(i + 1);
}
const getMulTable = (order, table = []) => {
if (order === 1) {
table.push(sequence);
return table;
}
table = getMulTable(order - 1, table);
table.push(sequence.map(el => el * order));
return table;
}
console.log(getMulTable(order));
I'm not sure if this reduces the time complexity, but this is the shortest way I know of to doing it.

Use collection methods like map.
let n = 10
let nCopy = n
let arr = []
while (nCopy > 0) {
arr.push(nCopy)
nCopy--
}
arr.sort((a, b) => a - b)
let res = []
for (let i = 1; i <= n; i++) {
res.push(arr.map(a => a * i))
}
console.log(res.flat())

Related

Can we change the iteration direction of Array.find?

Look at this crazy question... I have an array with 30.000 items, and I have to run something like this over it:
const bigArray = [
{ createdAt: 1 },
{ createdAt: 2 },
{ createdAt: 3 },
// And so on... 30.000
];
const found = bigArray.find(x => x.createdAt > 29950)
And the thing here, is that I know that 100% of the time, that element will be in the index 29.950 (approx). Because that array is already sorted by createdAt (coming from the backend)
How does .find works? Does it iterates starting from the first element? Is there a way to say "I know it's closer to the end... Change your behavior"?
Of course there is the alternative of doing something like:
bigArray.reverse()
const prevIndex = bigArray.findIndex(x => x.createdAt <= 29950);
const found = bigArray[prevIndex - 1];
bigArray.reverse()
But I'm not sure if that's gonna be actually worst (because of the fact that there we'll also have some multiples unnecessary iterations... I guess).
Who can give me some clues about this?
It's not that I have a bug here... Not even a performance issue (because 30.000 is not that much), but, feels like there should be something there, and I never hear about it in ~16 years working on JavaScript
Thanks so much!
Based upon the documentation here, it appears that find is O(n) time complexity, where n is length of the array.
Since your elements are sorted, you can try to do binary search and reduce time complexity to O(log n).
This is the basic binary search iterative algorithm:
function binarySearchIterative (nums, target) {
let res = -1;
let left = 0;
let right = nums.length;
while (left <= right && res === -1) {
const mid = Math.floor(left + (right - left)/2);
if (nums[mid] === target) {
res = mid;
}
else if (nums[mid] > target) {
right--;
}
else {
left++;
}
}
return res;
};
I'm not aware of any options for Array.prototype.findIndex that start from the end... I do know for sure that using Array.prototype.reverse is very expensive and you could make your own algorithm like this if you know that you're likely to find the result you need near the end:
const bigArray = [
{ createdAt: 1 },
{ createdAt: 2 },
{ createdAt: 3 }
];
// Add the function to Array.prototype
Array.prototype.findIndexFromEnd = function (cond) {
for(let i = this.length - 1; i >= 0; i--) {
if(cond(this[i])) return i;
}
return -1;
}
// Gives 1 as expected
console.log(bigArray.findIndexFromEnd(x => x.createdAt == 2));
// Or use an external function if you don't want to edit the prototype
function findIndexFromEnd(array, cond) {
for(let i = array.length - 1; i >= 0; i--) {
if(cond(array[i])) return i;
}
return -1;
}
// Gives 1 as expected
console.log(findIndexFromEnd(bigArray, (x) => x.createdAt == 2));

How to reduce the time complexity of this problem?

After having a long difficult coding challenge, there was a problem that bugged me. I thought about it for an adequate time but couldn't find the way to solve it. Here, I am providing a problem and example below.
Input
v : an array of numbers.
q : 2 dimensional array with 3 elements in nested array.
Description
v is an array and q is a commands that does different thing according to its nested element.
if first element of nested array is 1 => second and third element of the nested array becomes the index and it returns sum[second:third+1] (As you can see, it is inclusive)
if first element of nested array is 2 => element of second index becomes the third. same as v[second] = third
Input example
v : [1,2,3,4,5]
q : [[1,2,4], [2,3,8], [1,2,4]]
Example
With a provided example, it goes like
command is [1,2,4] => first element is 1. it should return sum from v[2] to v[4] (inclusive) => 12.
command is [2,3,8] => first element is 2. it switches v[3] to 8. (now v is [1,2,3,8,5])
command is [1,2,4] => first element is 1. it should return sum from v[2] to v[4] (inclusive) => 16, as the third index has been changed from the previous command.
So the final answer is [12, 16]
Question.
The code below is how I solved, however, this is O(n**2) complexity. I wonder how I can reduce the time complexity in this case.
I tried making a hash object, but it didn't work. I can't think of a good way to make a cache in this case.
function solution(v, q) {
let answer = [];
for (let i = 0; i < q.length; i++) {
let [a, b, c] = q[i];
if (a === 1) {
let sum = 0;
for (let i = b; i <= c; i++) {
sum += v[i];
}
answer.push(sum);
} else if (a === 2) {
v[b] = c;
}
}
return answer;
}
This type of problem can typically be solved more efficiently with a Fenwick tree
Here is an implementation:
class BinaryIndexedTree extends Array {
constructor(length) {
super(length + 1);
this.fill(0);
}
add(i, delta) {
i++; // make index 1-based
while (i < this.length) {
this[i] += delta;
i += i & -i; // add least significant bit
}
}
sumUntil(i) {
i++; // make index 1-based
let sum = 0;
while (i) {
sum += this[i];
i -= i & -i;
}
return sum;
}
}
function solution(values, queries) {
const tree = new BinaryIndexedTree(values.length);
values.forEach((value, i) => tree.add(i, value));
const answer = [];
for (const [a, b, c] of queries) {
if (a === 1) {
answer.push(tree.sumUntil(c) - tree.sumUntil(b - 1));
} else {
tree.add(b, c - values[b]);
values[b] = c;
}
}
return answer;
}
let answer = solution([1,2,3,4,5], [[1,2,4], [2,3,8], [1,2,4]]);
console.log(answer);
Time Complexity
The time complexity of running tree.add or tree.sumUntil once is O(log𝑛), where 𝑛 is the size of the input values (values.length). So this is also the time complexity of running one query.
The creation of the tree costs O(𝑛), as this is the size of the tree
The initialisation of the tree with values costs O(𝑛log𝑛), as really each value in the input acts as a query that updates a value from 0 to the actual value.
Executing the queries costs O(𝑚log𝑛) where 𝑚 is the number of queries (queries.length)
So in total, we have a time complexity of O(𝑛 + 𝑛log𝑛 + 𝑚log𝑛) = O((𝑚+𝑛)log𝑛)
Further reading
For more information on Fenwick trees, see BIT: What is the intuition behind a binary indexed tree and how was it thought about?

Picking Non-duplicate Random Numbers using JavaScript

How would I pick 5 random lottery numbers without having duplicate numbers? The code below is what I have so far and I just can't figure out where to insert the code to loop through to pick out duplicate numbers and reassign new numbers? I've tried adding if and else along with forEach function but it didn't work. This is the code I have so far. Thank you in advance.
let lotto = [];
for(let i = 0; i < 5; i++){
lotto[i] = Math.floor(Math.random() * 69) + 1;
}
const sorting = lotto.sort((a,b) => a - b);
console.log(sorting);
Two solutions:
Create a list of your numbers, then pick (and remove) 5 from them.
Create a loop that keeps generating numbers until it has 5 unique ones.
Your attempt can be adapted for solution 2:
let lotto = [];
while(lotto.length < 5) {
console.log('Got', lotto.length, 'numbers!');
// Changed 69 to 5 to "force" clashes (well, make it very likely)
const num = Math.floor(Math.random() * 5) + 1;
if (!lotto.includes(num)) lotto.push(num);
}
const sorting = lotto.sort((a, b) => a - b);
console.log(sorting);
Considering the process will run at leats one time, the best solution is to use a do while loop and verify if this number already exist in the list.
const lotto = [];
do {
const random = Math.floor(Math.random() * 69) + 1;
if(!lotto.includes(random)) lotto.push(random);
} while(lotto.length < 5);
const sorting = lotto.sort((a, b) => a - b);
console.log(sorting);

Can't detect the cause of infinite loop in a 'while loop' in JS

I've got an infinite loop inside my while loop and I can't find the cause.
It's a simple function that returns the sum of the argument's digits. I use a while loop because it needs to add up the digits until it lands at a one digit number.
I made sure that I added a statement that would make sure that at a certain point the loop will break., But it obviously doesn't.
function digital_root(n) {
num = n;
sum = 0;
while (num.toString().length>1){
for (i=0; i<num.toString().length; i++) {
sum += parseInt(num.toString()[i])
}
num = sum;
}
return sum;
}
digital_root(456)
I get a warning that I have an infinity loop on line 4 (while loop).
I hoped that num=sumwould reassign the new integer (with reduced number of digits) to the numvariable and thus at some point break out of the loop. Is this wrong?
What further confuses me is that most of the JS editors I've used to debug the problem return an output, but it takes ages. So is it an infinite loop or is it not?
You have a nested loop structure where the first condition is always true.
For getting only a number below 10, you could call the function again as recursion.
function digital_root(n) {
var num = n.toString(), // declare and use the string value
sum = 0,
i;
for (i = 0; i < num.length; i++) {
sum += parseInt(num[i], 10)
}
return sum > 9
? digital_root(sum)
: sum;
}
console.log(digital_root(456));
After re-reading the question I noticed that you're trying to reduce an integer down to a single number. The issue with your code was that sum was set to 0, only before the while loop. Meaning that it didn't reset for the second, third, ... run.
Moving sum = 0 into the while loop resolves this issue. I've also added variable declarations at the top to avoid setting global variables.
function digital_root(n) {
var num, sum, i;
num = n;
while (num.toString().length > 1) {
sum = 0;
for (i = 0; i < num.toString().length; i++) {
sum += parseInt(num.toString()[i]);
}
num = sum;
}
return sum;
}
console.log(digital_root(456));
Here written in a recursive manner, a style that I personally more prefer:
function digital_root(integer) {
// guard against things that might result in an infinit loop, like floats
if (!Number.isInteger(integer) || integer < 0) return;
const chars = integer.toString().split("");
if (chars.length == 1) return integer;
return digital_root(
chars.map(char => parseInt(char))
.reduce((sum, digit) => sum + digit)
);
}
console.log(digital_root(456));
Since you already got the answer, here is another way to meet the result
function digital_root(n) {
// convert the number to string
// use split to create an array of number viz ['4','5','6']
// use reduce to sum the number after converting to number
// 0 is the initial value
return n.toString().split('').reduce((a, c) => a + parseInt(c, 10), 0)
}
console.log(digital_root(456))
Avoiding all the nested loops that lead to a situation such as that you're facing, I'd rather do it in a more readable way.
function digital_root(n) {
sum = n;
while(sum.toString().length > 1) {
sum = sum.toString()
.split('')
.map(digit => parseInt(digit, 10))
.reduce((acc, cur) => acc + cur);
}
return sum;
}
console.log(digital_root(456));

Getting new colors randomly with loop

I am trying to randomize colors by generating random number, then applying
it to array to get an color array containing font-color and background-color.
At every "skill" I want to have unique color scheme. So each time I loop skill array I loop color array to fetch color scheme. If this color scheme number (which is same as the randomNumber) is already in use I random again. I do this with do/while loop. When color is not found it pushes it to usedColors array and paints the picture.
For some reason I am still getting same colors. I pasted two pictures to the bottom. Console.log image is about usedColors array (the randomly generated numbers)
var usedColors = [];
$.each(knowledges, (i, knowledge) => {
do {
var r = Math.floor(Math.random() * Math.floor(colors.length)),
rColors = colors[r];
} while ($.inArray(r, usedColors) == 0);
usedColors.push(r);
$("#knowledges div").append(
$("<p />").addClass("knowledge").text(knowledge).css({"background-color": rColors[0], "color": rColors[1]})
);
});
inArray gives position of the matching element. So compare against -1, to know that element is not present in the usedColors array.
var usedColors = [];
$.each(knowledges, (i, knowledge) => {
do {
var r = Math.floor(Math.random() * Math.floor(colors.length)),
rColors = colors[r];
} while ($.inArray(r, usedColors) != -1);
usedColors.push(r);
$("#knowledges div").append(
$("<p />").addClass("knowledge").text(knowledge).css({"background-color": rColors[0], "color": rColors[1]})
);
});
To generate array of unique numbers from certain interval you can do this.
In your case the range will be 0, arr.length - 1.
// function that will generate random unique random numbers between start
// and end, and store already generated numbers in closure
function generateUniqueRandom(start, end) {
const used = [];
function generateInner() {
let r;
while (!r) {
r = Math.floor(Math.random() * (end - start) + 1) + start;
if (used.includes(r)) {
r = null;
} else {
used.push(r);
}
}
return r;
}
return generateInner;
}
const random1 = generateUniqueRandom(0, 20);
const nums1 = [];
for (let i = 0; i < 10; i++) {
nums1.push(random1());
}
console.log(nums1);
const random2 = generateUniqueRandom(0, 20);
const nums2 = [];
for (let i = 0; i < 20; i++) {
nums2.push(random2());
}
console.log(nums2);
But you need to be careful not to generate more numbers that the specified range is, otherwise you will be stuck in an infinite loop.
In your while loop, are you checking if the array is unique? If so, it looks like you may not be using $.inArray correctly.
Put this in your while loop:$.inArray(r, usedColors) !== -1
jQuery.inArray(), how to use it right?
I think your loop method has many interactions, I mean your loop is traveling so much that it only ends until you find the random number that is not in the array (A short performance problem). An alternative method so that the array elements are random:
function shuffleArray(a) {
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
const colors = [["black","green"], ["white","blue"], ["pink","white"]];
let usedColors = shuffleArray(colors);
//You can now do this:
$.each(knowledges, (i, knowledge) => {
$("#knowledges div").append(
$("<p />").addClass("knowledge").text(knowledge).css({"background-color": usedColors[i][0], "color": usedColors[i][1]})
);
});

Categories

Resources