Varying number of related nested for loops - javascript

I am attempting to determine all possible sums from rolling n dice, where n is not known at compile time.
For two dice, the solution is straightforward, just iterate through both dice and add each possible side to each other. If passing in 2 6-sided dice, the sorted results would be: [2,3,3,4,4,4,5,5,5,5,6,6,6,6,6,7,7,7,7,7,7,8,8,8,8,8,9,9,9,9,10,10,10,11,11,12]
I tried to expand this solution to any n dice, but I realized that I need n for loops.
for(let i = 0; i < numDice; i++)
{
dice.push(sides);
}
for(let i = 0; i < numSides; i++)
{
for(let j = 1; j < dice.length; j++)
{
for(let k = 0; k < numSides; k++)
{
results.add(dice[0][i] + dice[j][k]);
}
}
}
I also attempted a recursion-based approach as the first question below suggested. I believe it will loop the correct number of times, but I couldn't figure out how to define my summing function without introducing yet more loops.
function doCallMany(numDice, numSides, sumFunc)
{
if(numDice == 0)
{
sumfunc(numDice, numSides) //?
}
else
{
for(let i = 0; i < numSides; i++)
{
doCallMany(numDice--, numSides, sumFunc)
}
}
}
I looked at similar questions here and here but they do not answer my question. The first doesn't because the action I need to perform in the loops is not independent. The second is close, but the answers rely on Python-specific answers.

The comment about the complexity of the solutions is correct. It gets big quickly. Having said that, to address your original question, you can do this with a fairly simple recursive function for small input. Basically you start with an array of dice, pop one off add it to a sum and recurse with that sum and the rest of the array.
For example:
function sums(dice, sum = 0, ans = []) {
if (dice.length === 0) ans.push(sum) // edge case, no more dice
else {
let d = dice[0]
for (let i = 1; i <= d; i++) {
sums(dice.slice(1), sum + i, ans) // recurse with remaining dice
}
return ans
}
}
// two six-sided dice
let ans = sums([6, 6])
console.log(JSON.stringify(ans.sort((a, b) => a - b)))
// three three-sided dice
ans = sums([3, 3, 3])
console.log(JSON.stringify(ans.sort((a, b) => a - b)))

I suggest you use the backtracking method.
It lets you vary the number of loops you want to execute. You can even execute a random number of loops, as the number of loops can be held in a variable.

Related

Biggest prime number using sieve of Eratosthenes

I managed to solve the problem but I am exceeding the time limit.
What to fix: If the given number n is very large it takes ridiculous amount of time.
Question: Is there a way I can tweak the code to get the last prime number faster?
const n = 126;
let lastPrime = 0;
for (let j = 2; j <= n; j++) {
let counter = 0;
for (let i = 1; i <= j; i++) {
if (j % i === 0) counter++;
}
if (counter === 2) lastPrime = j;
}
print(lastPrime); // Biggest prime number of 126 is 113
Thanks!
A basic question I ask myself when looking at sieve code is: "does this code use the modulo (%) operator?" If it does, it isn't the Sieve of Eratosthenes. You're doing trial division, which is vastly slower. Now there are certainly places and reasons for trial division, but based on your question title, you intended to use the SoE.
See Wikipedia pseudocode for example. The only operations are the two loops that involve only simple additions, a test, and a set. That's it. No multiplies, no divides, no modulos. This is absolutely key to why the algorithm is fast. The inner loop also quickly becomes sparse, in the sense that the increment keeps getting larger, so we actually run the inner loop code fewer times as we go on. Contrast to the initial code you posted, where the inner loop is running more times.
To do the basic SoE, you need a small array, then exactly and only 4 lines of code to do the sieving, with the outer loop going to sqrt(n). Then you can examine the array which will have only primes left marked. In your case, you can walk it backwards and return when you find the first occurrence. There are countless methods for optimization, but it's surprising how fast the simple basic SoE is for relatively small n (e.g. under 10^9, after which you really need to do a segmented SoE).
All that said, you got working code, which is a great first step. It's much easier to try different methods once you have something working.
There are plenty of optimizations to be done here - a quick read here (https://math.stackexchange.com/questions/889712/the-fastest-way-to-count-prime-number-that-smaller-or-equal-n/893767) would help.
But for starters, you can change a few things in your code to make it trivially faster:
Step 1: Reduce the number of outer iterations, since we know all even numbers are non-prime.
for (let j = 3; j <= n; j += 2) {
...
}
Step 2: Reduce the inner-loop by only iterating up to a max of Sqrt of the max number. Also break out of the inner loop once we find even one factor. No need to iterate till the end. These two will give you the biggest wins.
let prime = true;
for (let i = 2; i <= Math.sqrt(j); i++) {
if (j % i === 0) {
prime = false;
break;
}
}
if (prime) {
lastPrime = j;
}
Step 3: Stop computing Math.sqrt(j) since you already know the previous max value. sqrt is a (relatively) expensive operation. We can avoid it by making use of previous value.
let maxBound = 2;
let maxSquare = maxBound * maxBound;
for (let j = 3; j <= n; j += 2) {
if (maxSquare < j) {
maxBound++;
maxSquare = maxBound * maxBound;
}
for (let i = 2; i <= maxBound; i++) {
...
}
}
Step 4: If all you want is the biggest prime, walk the loop backwards and break as soon as you find one prime.
And here's the finished program which should be approximately 2 orders of magnitude faster than yours. Note that while I provided some trivial optimizations for your program, this will always pale in comparison to algorithmic optimizations that you can find here: https://math.stackexchange.com/a/893767
function getMaxPrime(n) {
for (let j = n; j >= 3; j --) {
let prime = true;
for (let i = 2; i <= Math.sqrt(j); i++) {
if (j % i === 0) {
prime = false;
break;
}
}
if (prime) {
maxPrime = j;
break;
}
}
console.log(maxPrime);
}

The for loop in this program isn't working as expected

It is supposed to print prime n numbers. The for loop will run from 2 to x which will iterate each time. if i == x then it means that the number was not divisible and so it should be printed as prime
var n;
var x = 2;
var i;
function prime(n) {
while (n) {
for (i = 2; i < x; i++) {
if (x % i == 0) {
break;
}
if (i == x) {
document.write(i + " ");
n--;
}
x++;
}
}
}
prime(10);
When you try to execute this code, this will never get into the for loop and goes into an infinite while loop. You have got:
i = 2; i < x;
The i will never be less than x. And it doesn't enter the for loop and comes out. And n will always be 10, that goes on into an infinite loop.
You need to use the modulus operator to check if a number is divisible by them.
Maybe change your approach a bit and try to find the first X prime number using just for loops.
var n;
var x = 2;
var i;
function prime(n) {
if (n <= 0) return;
var i, j, p_no = 0, res = [];
for (i = 2; ; i++) {
var ifPrime = true;
for (j = 2; ifPrime && j <= Math.sqrt(i); j++) {
if (i % j === 0) ifPrime = false;
}
if (ifPrime) {
res.push(i);
console.log(i + ' ');
p_no++;
if (p_no === n) return res.toString();
}
}
}
document.getElementById('prime').innerHTML = prime(10);
<p id="prime"></p>
What's happening when the code runs is what Praveen describes. I want to address how you got to your algorithm in the first place.
It looks like you're trying to print all primes less than a specific number n. You've jumbled different aspects of your algorithm together. Specifically, you've combined a loop that exists to find whether a number is prime with a loop over all numbers less than n.
The first thing you can do to help manage this complexity is to use methods. If you had a method isPrime(k) that returns true or false if a given number is prime, then your function's main loop looks much simpler, and separates the two problems from each other:
function prime(n) {
for (var i = n; i > 1; i--) {
if (isPrime(i)) {
document.write(i + " ");
}
}
}
Then you can focus on defining the isPrime method separately, without getting its parts confused with the main loop:
function isPrime(k) {
for (var i = 2; i < k; i++) {
if (k % i == 0) {
return false;
}
}
return true;
}
Methods are a fantastic way of keeping algorithms simpler by isolating their components. They're building blocks one can use to make more complex systems without having to keep track of the whole. It lets you make smaller changes, each encapsulated from other concerns, meaning you have less to keep in your mind while you're making those changes. The less you have to keep in your mind, the easier it is to spot mistakes.

If I use two variables to iterate through a 2D array is that still O(n^2) time complexity?

for (let i = 0; i < array.length; i += 1) {
const row = array[i];
for (let j = 0; j < row.length; j += 1) {
const el = row[j];
}
}
Would be a typical way to iterate through a 2D array nxn matrix and I'd consider that O(n^2) time complexity.
If I did this instead
let count = 0;
let i = 0;
let j = 0;
const n = arr.length;
const max = n * n;
while (count !== max) {
const ele = arr[i][j];
if (j === n - 1) {
j = 0;
i += 1;
} else j += 1;
count += 1;
}
Would it still be O(n^2)? Kind of a stupid question, I think the answer is yes, but I just want to double check. Obviously, the first method is much clearer, but then lower time complexity is good as well.
Well, it's not really O(n2) in the first place.
Big-O tells you the worst-case performance of an algorithm by giving an idea of how its time of execution scales with the number of elements handled by the algorithm grows.
In the case of a 2-D matrix, while the matrix is, indeed, square (or, at least, rectangular), it's not really appropriate to use the length of the matrix as n here. Rather, you should be using the number of cells in the matrix (i x j).
A 2-D matrix is, effectively, an array of arrays and your algorithm is simply stepping through each cell once, making it O(n) in both cases. You could say that it's O(ixj), but it's still a linear algorithm.
Given n = max(array.length, array[0].length):
Yes - they are both O(n^2). Even though it's one loop, the number of elements that the while loop goes through is the same as the number of elements that the 2 for loops go through.
In other words, with the for loop you're going through (approximately) n-sized chunks n times, and with the while loop you're going through an n^2-sized chunk once.

Is there a faster way to find a value in a multidimensional array and return all of its indexes than for(for())?

I need to find the value of a 2-dimensional array and return its indexes. For example if my searched term is in array[i][j], then I want [i, j] returned.
Naturally, I came up with this simple solution:
function find(str){
for(let o = 0; o < array.length; o++){
for(let k = 0; k < array[o].length; k++){
if(array[o][k] == str){
return [o, k];
}
}
}
}
Now I need to use this method a couple hundred times as part of an algorithm, and it gets quite time-costy. Is there a more efficient way?
I have created a simple full example including a 'benchmark':
// setup to hide foo in an array
var array = [];
for(let i = 0; i < 100; i++){
array.push([])
for(let j = 0; j < 100; j++){
if(i == 99 && j == 99) array[i].push("foo"); // intentionally hiding the searched term at the worst-case position for the algorithm of find()
else array[i].push("bar");
}
}
// function to find foo
function find(str){
for(let o = 0; o < array.length; o++){
for(let k = 0; k < array[o].length; k++){
if(array[o][k] == str){
return [o, k];
}
}
}
}
// lets say we need to find foo 200 times
var a = window.performance.now();
for(let i = 0; i < 200; i++){
console.log(i, find("foo")); // if you're happy and you know it, tell us what you found
}
var b = window.performance.now();
// print performance result
$('body').html((b-a) + " ms");
JSfiddle for the benchmark example: http://jsfiddle.net/3t0db1cq/11/
(note: in that benchmark example I searched 'foo' 200 times, so you may ask why I don't simply cache it. In reality I will search different terms, so caching will barely improve the performance. Also I intentionally did put the searched term in the worst-case position of the array for this benchmark)
Can you help me find a better algorithm for find()? To be fair for the performance test, re-position the searched term at the worst-case position in the array for your algorithm, if you want to compare the results.
(Target for this is websites, so all common browsers should support it)
Seems to me that you are mapping a key (pair of integers) to a string value and you want to return the key for that value.
As you are using an array each search operation is always O(n^2) worse case, there's no "smart" way using that datastructure
As #Richrd said, you can build a reverse mapping from the string values to a pair of integers and search that. Easy way is to use a javascript Map() (hash map). Though you may want to look into a trie implementation for the string to integer map.
But this begs the question: if you are performing a lot of these reverse lookups then why store this data as a 2d-array of strings in the first place? You could save more time by storing this data as a map of strings to ints in the first place.
If you are familiar with SQL one way of doing this is to use sqllite. It is a very easy way to run sql in the browser.
https://www.tutorialspoint.com/html5/html5_web_sql.htm
Unfortunately it is not supported on all browsers so you would have to have a general idea of your audience.
Alternatively as long as all of your values are different you could reverse map your array and then search as much as you want with no cost. For instance:
// setup to hide foo in an array
var array = [];
for(let i = 0; i < 100; i++){
array.push([])
for(let j = 0; j < 100; j++){
if(i == 99 && j == 99) array[i].push("foo"); // intentionally hiding the searched term at the worst-case position for the algorithm of find()
else array[i].push("bar");
}
}
//Create your reverse mapped array. This only runs once at startup, but now allows you to
function buildReverse(arr) {
var reverseArr = {};
for(let o = 0; o < arr.length; o++){
for(let k = 0; k < arr[o].length; k++){
reverseArr[arr[o][k]] = [o, k];
}
}
return reverseArr
}
var reverseArr = buildReverse(array);
function find(str){
if (reverseArr[str] != undefined) {
return reverseArr[str];
// or
//return [reverseArr[str][0], reverseArr[str][1]]
//, etc...
}
return "";
}
// lets say we need to find foo 200 times
var a = window.performance.now();
for(let i = 0; i < 200; i++){
console.log(i, find("foo")); // if you're happy and you know it, tell us what you found
}
var b = window.performance.now();
// print performance result
$('body').html((b-a) + " ms");

string addition I coderbyte completely stumped

Can anyone explain the solution to me like a 6 year old? I haven't been able to make sense of the solutions. Maybe some code comments?
Thank you.
I've spent the last 2 hours on coderbyte trying to solve this one. Here's the question:
Have the function ArrayAdditionI(arr) take the array of numbers stored
in arr and return the string true if any combination of numbers in the
array can be added up to equal the largest number in the array,
otherwise return the string false. For example: if arr contains [4, 6,
23, 10, 1, 3] the output should return true because 4 + 6 + 10 + 3 =
23. The array will not be empty, will not contain all the same elements, and may contain negative numbers.
I've scoured the internet, read through a bunch of people's solutions from the people's answers on coderbyte itself, but without any comments I'm really struggling to figure out how this is done. I've started over countless times, so I'm not even sure if this last attempt is any better or not.
I understand that I'll need to loop through somehow and test every combination index 5, index 4, index 3, index 2, index 1, AND every combination of less than all of those (ie just index 5 and index 3). I just can't figure out how to do that. If I knew the list would always be an array of 5 numbers, and it required all 5, I would write 5 loops all equally nested inside of one big one, right? However, with the added complexity of not requiring all numbers and not knowing the length of the array for all cases, I am completely stumped. I tried using Math.floor(Math.random() * array.length); to generate a list of numbers... but that didnt work either.
function ArrayAdditionI(arr) {
var longest = arr.sort( function(a,b) { return a-b });
var longest = longest[longest.length - 1];
var sumArr = function (arrb) {
var sum = 0;
for (var z = 0; z < arrb.length; z++){
sum += arrb[z];
}
return sum;
};
for (var i = 0; i > arr.length; i++) {
for (var y = 0; y > arr.length; i++) {
testArr.push(arr[i]);
if (sumArr(testArr) === longest) {
return true;
}
testArr.push(... its 4am and I'm stumped!...)
}}
// code goes here
return false;
}
// keep this function call here
// to see how to enter arguments in JavaScript scroll down
ArrayAdditionI(readline());
A fairly simple to understand and common solution to the problem is as follows. It basically starts by looping forward through the array (loop i) by adding each subsequent number (loop j). If loop j finishes without a solution, loop k begins and removes each subsequent number. Then i increments and the loops start over.
function ArrayAdditionI(arr) {
arr.sort(function(a,b){return a - b})
var largest = arr.pop(); // Set largest to last (largest) array value
var sum = 0;
for (var i = 0; i < arr.length; i++){ // Start outer loop
sum += arr[i];
for (var j = 0; j < arr.length; j++){ // Start inner to begin sum
if (i != j) { // Ensure we don't add the same array index to itself
sum += arr[j];
console.log(sum);
if (sum == largest) {
return true;
}
}
}
for (var k = 0; k < arr.length; k++) { // If no match, start 2nd loop to re-iterate removing index values
if (i != k) {
sum -= arr[k];
console.log(sum);
if (sum == largest) {
return true;
}
}
}
sum = 0; // Reset sum for outer loop
}
return false;
}
The comment by thefourtheye basically told you the name of the problem and what to search for on google.
Solutions in java code would be the following...
1) find all subsets that sum to a particular value
This is the theory and pseudocode.
2) How to implement the Sum of Subsets problem in Java
Change the code to return what you wish if number of sets > 0 .
3) Modify GetAllSubsetByStack in below code to stop when a set is found:
https://codereview.stackexchange.com/questions/36214/find-all-subsets-of-an-int-array-whose-sums-equal-a-given-target
We did a similar recursion for a "MakeChange" algorithm at a meetup I went to last night. This morning I took a look at this problem with fresh eyes after not looking at it for a few weeks. Here's what I came up with.
Essentially: sort the array to biggest to smallest, shift that one off and declare it "goal", then recursively reduce an array by slicing it smaller and smaller each recurse, when the array reaches 0 length, randomize the original array and begin reducing again.
return true if goal = total (ie the reduce)
return false if ive randomized the array more than 1000 times.
function ArrayAdditionI(arr) {
var originalArr = arr.sort(function(a,b) {return b-a});
var goal = arr.shift();
var counter = 0;
function randomArray(array) {
for (var i = array.length - 1; i > 0; i -= 1){
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
function recurse(arr) {
if (arr.length == 0){
counter++
var newArr = randomArray(originalArr);
return recurse(newArr);
} else {
var total = arr.reduce(function(a,b) {return a+b});
if (goal == total){
return true
} else if (counter == 1000) {
return false
} else {
newArr = arr.slice(1);
return recurse(newArr);
}
}
}
// code goes here
return recurse(originalArr);
}

Categories