My 2d array is not updating even after assignment - javascript

After debugging for more than an hour, I found the problem in my code. I do not understand why it updates board[y][x] but not board itself.
Here is the code:
puzzle = [
[0, 0, 7, 0, 0, 6, 2, 0, 0],
[1, 0, 0, 8, 9, 0, 7, 6, 0],
[8, 0, 2, 0, 7, 3, 0, 5, 0],
[0, 0, 0, 1, 0, 9, 5, 0, 6],
[0, 1, 0, 0, 0, 0, 0, 8, 0],
[7, 0, 3, 6, 0, 8, 0, 0, 0],
[0, 7, 0, 2, 4, 0, 6, 0, 3],
[0, 4, 1, 0, 6, 7, 0, 0, 5],
[0, 0, 9, 3, 0, 0, 1, 0, 0]
];
function find_empty(board) {
for (let y = 0; y < 9; ++y) {
for (let x = 0; x < 9; ++x) {
if (board[y][x] === 0) {
return [y, x];
}
}
}
return null;
}
function is_valid(board, num, pos) {
let y = pos[0];
let x = pos[1];
// Horizontal checking...
for (let i = 0; i < 9; ++i) {
if (board[y][i] === num && x !== i) {
return false;
}
}
// Vertical checking
for (let i = 0; i < 9; ++i) {
if (board[i][x] === num && y !== i) {
return false;
}
}
// 3*3 box checking
box_start_y = (Math.floor(y / 3)) * 3;
box_start_x = (Math.floor(x / 3)) * 3;
for (let i = box_start_y; i < box_start_y + 3; ++i) {
for (let j = box_start_x; j < box_start_x + 3; ++j) {
if (board[i][j] === num && y !== i && x !== j) {
return false;
}
}
}
return true;
}
function solve_board(board) {
console.log("start", board);
empty_pos = find_empty(board);
console.log("empty_pos", empty_pos)
if (empty_pos === null) {
return board;
}
let y = empty_pos[0];
let x = empty_pos[1];
for (let num = 1; num < 10; ++num) {
console.log(board)
if (is_valid(board, num, empty_pos)) {
console.log(true, num)
console.log("Before assignment", board[y][x]);
board[y][x] = num;
console.log("y", y, ", x", x);
console.log("After true", board[y][x])
console.log("After true", board);
let solved_board = solve_board(board)
console.log("solved", solved_board);
if (solved_board !== false) {
return solved_board;
}
console.log(num, "did not work!!")
board[y][x] = 0;
}
console.log(false, num)
}
return false;
}
console.log("result", solve_board(puzzle))
The problem is on line 72 in solve_board function.
It updates board[y][x] as seen in the console.logs, but does not affect board. Is it some reference issue or pass by value problem?
If anyone can help me solve this issue, I would be really grateful.

you are assigning the new value correctly. Arrays are passed to the function by reference, so there should be no problem. You can verify it with this code snippet:
const arr = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
function foo(x) {
x[0][0] = 2
}
foo(arr)
console.log(arr[0][0]) // 2
There probably is some issue with the game logic itself, the conditions in the is_valid don't look right. Consider taking a look on this article explaining how to solve sudoku programmatically with logic and without brute force.
Alternatively, I suggest you look at the backtracking algorithm, which tries all possible options, which should be straightforward to implement.

Related

trying to create sudoko matrix help (JS)

I'm trying to create a random board for my project but it finishes without filling the whole board(leaves 0)
I converted the original code from this site:
www.101computing.net/sudoku-generator-algorithm/
from python to javascript manually and checked all the functions that I created but some things were wrong.
would love to get some help here thnks!
the code:
let grid = Array(9).fill().map(()=>Array(9).fill(0))
let numberList = [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(grid);
fillGrid(grid)
console.log(grid)
function fillGrid(grid){
//var counter;
// Find next empty cell
for (i in range(0,81)){
row = ~~(i / 9);
col = i % 9;
if (grid[row][col]== 0){
shuffle(numberList);
for (g in numberList){
value = numberList[g]
// Check that this value has not already be used on this row and col
if (!checkForNumebrInRaw(grid,row,value) && !checkForNumebrInColomn(grid,col,value)){
// Identify which of the 9 squares we are working on
square = []
if (row<3){
if (col<3){
square = getSquare(grid,0,3,0,3);
}else if (col<6){
square = getSquare(grid,3,6,0,3);
}else{
square = getSquare(grid,6,9,0,3);
}
}
else if (row<6){
if (col<3){
square = getSquare(grid,0,3,3,6);
}else if (col<6){
square = getSquare(grid,3,6,3,6);
}else{
square = getSquare(grid,6,9,3,6);
}
}
else{
if (col<3){
square = getSquare(grid,0,3,6,9);
}else if (col<6){
square = getSquare(grid,3,6,6,9);
}else{
square = getSquare(grid,6,9,6,9);
}
}
// Check that this value has not already be used on this 3x3 square
if (!checkForNumInSquare(square,value)){
grid[row][col] = value;
if (checkGrid(grid)){
return true;
}else
{
if (fillGrid(grid)){
return true;
}
}
}
}
break
}
grid[row][col] = 0;
}
}
}
function range(start, end) {
let res=[];
for (i=start;i<end;i++){
res.push(i)
}
return res
};
function getSquare(mat,a,b,c,d) {
var _pj_a = []
_pj_b = range(c, d);
for (var _pj_c = 0, _pj_d = _pj_b.length; _pj_c < _pj_d; _pj_c += 1) {
var i = _pj_b[_pj_c];
_pj_a.push(mat[i].slice(a, b));
}
return _pj_a;
};
function checkForNumebrInRaw(grid,raw,num){
for (let i = 0; i<grid.length;i++){
if (grid[raw][i] == num){return true;}
}
return false;
}
function checkForNumebrInColomn(grid,colomn,num){
for (let i = 0; i<grid.length;i++){
if (grid[i][colomn] == num){return true;}
}
return false;
}
function shuffle(array) {
let currentIndex = array.length, randomIndex;
// While there remain elements to shuffle...
while (currentIndex != 0) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
// And swap it with the current element.
[array[currentIndex], array[randomIndex]] = [
array[randomIndex], array[currentIndex]];
}
return array;
}
function checkForNumInSquare(grid,num){
for (let i=0;i<grid.length;i++){
for(let j=0;j<grid[i].length;j++){
if (grid[i][j]== num){return true;}
}
}
return false
}
// A function to check if the grid is full
function checkGrid(grid){
for (let i=0;i<grid.length;i++){
for(let j=0;j<grid[i].length;j++){
if (grid[i][j]== 0){return false;}
}
}
return true
}
output:
[
[
0, 0, 0, 0, 0,
0, 0, 0, 0
],
[
0, 0, 0, 0, 0,
0, 0, 0, 0
],
[
0, 0, 0, 0, 0,
0, 0, 0, 0
],
[
0, 0, 0, 0, 0,
0, 0, 0, 0
],
[
0, 0, 0, 0, 0,
0, 0, 0, 0
],
[
0, 0, 0, 0, 0,
0, 0, 0, 0
],
[
0, 0, 0, 0, 0,
0, 0, 0, 0
],
[
0, 0, 0, 0, 0,
0, 0, 0, 0
],
[
0, 0, 0, 0, 0,
0, 0, 0, 0
]
]
[
[
3, 8, 7, 4, 1,
2, 5, 6, 9
],
[
5, 9, 6, 3, 7,
8, 1, 4, 0
],
[
1, 2, 4, 5, 6,
9, 7, 3, 8
],
[
9, 4, 3, 1, 5,
7, 0, 8, 2
],
[
2, 1, 8, 0, 9,
4, 3, 7, 6
],
[
7, 0, 5, 6, 8,
3, 9, 1, 4
],
[
4, 7, 2, 8, 0,
1, 6, 9, 5
],
[
0, 6, 9, 2, 3,
5, 8, 0, 7
],
[
8, 3, 1, 7, 0,
6, 4, 2, 0
]
]
Ok so after a million tries i made it!
// true - row and colomn without the number
function rowAndColomnCheck(grid,row,col,num){
function checkForNumebrInRaw(grid,row,num){
for (let i = 0; i<grid.length;i++){
if (grid[row][i] == num){return true;}
}
return false;
}
function checkForNumebrInColomn(grid,colomn,num){
for (let i = 0; i<grid.length;i++){
if (grid[i][colomn] == num){return true;}
}
return false;
}
if (!checkForNumebrInColomn(grid,col,num) && !checkForNumebrInRaw(grid,row,num)){
return true;
}else{
return false;
}
};
function range(start, end) {
let res=[];
for (i=start;i<end;i++){
res.push(i)
}
return res
};
function getSquare(mat,a,b,c,d) {
var _pj_a = []
_pj_b = range(c, d);
for (var _pj_c = 0, _pj_d = _pj_b.length; _pj_c < _pj_d; _pj_c += 1) {
var i = _pj_b[_pj_c];
_pj_a.push(mat[i].slice(a, b));
}
return _pj_a;
};
function shuffle(array) {
let currentIndex = array.length, randomIndex;
// While there remain elements to shuffle...
while (currentIndex != 0) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
// And swap it with the current element.
[array[currentIndex], array[randomIndex]] = [
array[randomIndex], array[currentIndex]];
}
return array;
};
// check if number is in sqare given
function checkForNumInSquare(grid,num){
for (let i=0;i<grid.length;i++){
for(let j=0;j<grid[i].length;j++){
if (grid[i][j]== num){return true;}
}
}
return false
};
// A function to check if the grid is full
function checkGrid(grid){
for (let i=0;i<grid.length;i++){
for(let j=0;j<grid[i].length;j++){
if (grid[i][j]== 0){return false;}
}
}
// complete
return true
};
function fillSudoku(mat){
for(let i in range(0,81)){
var mainRow = ~~(i / 9);
var mainCol = i % 9;
if (mat[mainRow][mainCol] == 0){
shuffle(numberList);
for (let value of numberList){
if(rowAndColomnCheck(mat,mainRow,mainCol,value)){
var mySquare = [[]];
if(mainRow<3){
if (mainCol<3){
mySquare = getSquare(mat,0,3,0,3);
}
else if (mainCol<6){
mySquare = getSquare(mat,3,6,0,3);
}
else{
mySquare = getSquare(mat,6,9,0,3);
}
}
else if(mainRow<6){
if (mainCol<3){
mySquare = getSquare(mat,0,3,3,6);
}
else if (mainCol<6){
mySquare = getSquare(mat,3,6,3,6);
}
else{
mySquare = getSquare(mat,6,9,3,6);
}
}
else{
if (mainCol<3){
mySquare = getSquare(mat,0,3,6,9);
}
else if (mainCol<6){
mySquare = getSquare(mat,3,6,6,9);
}
else{
mySquare = getSquare(mat,6,9,6,9);
}
}
bol_t = checkForNumInSquare(mySquare,value);
if (bol_t == false){
mat[mainRow][mainCol] = value;
if (checkGrid(mat)){
return true;
}
else{
if (fillSudoku(mat)){
return true;
}
}
}
}
}
break
}
}
mat[mainRow][mainCol] = 0;
};
var numberList = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var grid = Array(9).fill().map(()=>Array(9).fill(0));
fillSudoku(grid);
console.log(grid);

BFS that returns shortest path on a 2nd array /unwighted graph

I know this has been asked before but I can't seem to figure out the solution from the examples and translate them to javascript. not even when following :
https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
I have an unweighted graph or 2d array for example that looks like this:
const testMatrix = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0]
];
I then Traverse that using BFS: (i currently hardCoded the item to find as element 2,2 in the array).
And return the seen list item list. but I can't figure out how the seen list supposes to show the way to the shortest path.
const traversalBFS = function(matrix) {
counter = 0;
const seen =
new Array(matrix.length).fill(0).map(() => new Array(matrix[0].length).fill(false));
const values = [];
const queue = [[0, 0]];
while(queue.length) {
const currentPos = queue.shift();
const row = currentPos[0];
const col = currentPos[1];
if(row < 0 || row >= matrix.length || col < 0 || col >= matrix[0].length || seen[row][col] || matrix[row][col] === 1) {
continue;
}
counter++;
seen[row][col] = true;
values.push(matrix[row][col]);
if(row === 2 && col === 2) {
return seen;
}
for(let i = 0; i < directions.length; i++) {
const currentDir = directions[i];
queue.push([row + currentDir[0], col + currentDir[1]]);
}
}
return false;
}
even if I run this code
temp = traversalBFS(testMatrix);
let path = [];
for(i = 0; i <= 2; i++) {
for(j = 0; j <= 2; j++) {
if(temp[i][j]) {
path.push([i, j]);
}
}
}
it will return:
0: (2) [0, 0]
1: (2) [0, 1]
2: (2) [0, 2]
3: (2) [1, 0]
4: (2) [1, 1]
5: (2) [1, 2]
6: (2) [2, 0]
7: (2) [2, 2]
which is not a correct path in any way, and also not the shortest path.
expected result example:
hmmm lets say end point will be 1,1 and start will be 0, 0
the expected result is an array with the shortest path:
[[0,0], [0, 1], [1,1]]
if the start point is 0, 0 and the end point is 2,2:
I think the result should be:
[[0,0], [0, 1], [1,1],[1,2],[2,2];
using the test matrix I wrote as an example. as there are no 1 aka walls in the way.
For getting an ide how Dijkstra's algorithm work, you could take the approach of the given link and take a large value for a start and take smaller values as soon as the neighbours have them.
This example uses (step) values instead of indices, but it could be replaced by the coordinates of the path to the target.
This approach uses obstacle avoidance and a short cicuit if the target is found.
const
travel = (array, from, to) => {
const
go = (i, j, smallest) => {
if (array[i]?.[j] === 1) return;
if (i === to[0] && j === to[1]) return;
if (unvisited[i]?.[j] > smallest) {
unvisited[i][j] = smallest;
go(i + 1, j, smallest + 1);
go(i - 1, j, smallest + 1);
go(i, j + 1, smallest + 1);
go(i, j - 1, smallest + 1);
}
},
unvisited = testMatrix.map(a => a.map(_ => Number.MAX_VALUE));
go(from[0], from[1], 0);
return unvisited;
},
testMatrix = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0]],
result = travel(testMatrix, [0, 0], [3, 2]);
result.forEach(a => console.log(a.map(v => v === Number.MAX_VALUE ?'*': v).join(' ')));

Uncaught RangeError: Maximum call stack size exceeded with sudoku solver

// global variable
// accessible to all functions
var sol =
[[0, 7, 0, 2, 3, 8, 0, 0, 0],
[0, 0, 0, 7, 4, 0, 8, 0, 9],
[0, 6, 8, 1, 0, 9, 0, 0, 2],
[0, 3, 5, 4, 0, 0, 0, 0, 8],
[6, 0, 7, 8, 0, 2, 5, 0, 1],
[8, 0, 0, 0, 0, 5, 7, 6, 0],
[2, 0, 0, 6, 0, 3, 1, 9, 0],
[7, 0, 9, 0, 2, 1, 0, 0, 0],
[0, 0, 0, 9, 7, 4, 0, 8, 0]];
//this function prints the board
var printBoard = function () {
for (let row = 0; row < sol.length; row += 1) {
for (let col = 0; col < sol[row].length; col += 1) {
let id = 'r' + (row + 1) + (col + 1);
let elem = document.getElementById(id);
elem.innerHTML = sol[row][col];
}
}
};
function isValid(sol, row, col, k) {
for (let i = 0; i < 9; i++) {
const m = 3 * Math.floor(row / 3) + Math.floor(i / 3);
const n = 3 * Math.floor(col / 3) + i % 3;
if (sol[row][i] == k || sol[i][col] == k || sol[m][n] == k) {
return false;
}
}
return true;
}
var solve = function() {
for (let row = 0; row < 9; row++) {
for (let col = 0; col < 9; col++) {
if (sol[row][col] == 0) {
for (let k = 1; k <= 9; k++) {
if (isValid(sol, row, col, k) == true) {
sol[row][col] == '${k}';
if(solve() == true) {
return true;
} else {
sol[row][col] == 0;
}
}
}
return false;
}
}
}
return true;
}
printBoard();
I am having trouble debugging this. I keep getting an error message when I click on my solve button, which will take the grid and complete a sudoku puzzle: "Uncaught RangeError: Maximum call stack size exceeded". Looking at the file, it seems that my isValid() function, is causing the issues here. I think I am stuck in a continuous loop somewhere? Any help would be appreciated. It also says the error lies on lines 40 and 24, which are:
function isValid(sol, row, col, k) {
and
if (isValid(sol, row, col, k) == true) {
Please help, as I am not even a CS major and this is for a class requirement, and I am not exactly experience with this type of stuff. I tried to search for help thorugh other resources but it is just confusing me even more.
EDIT:
//global variable
//accesible to all functions
var sol =
[[0, 7, 0, 2, 3, 8, 0, 0, 0],
[0, 0, 0, 7, 4, 0, 8, 0, 9],
[0, 6, 8, 1, 0, 9, 0, 0, 2],
[0, 3, 5, 4, 0, 0, 0, 0, 8],
[6, 0, 7, 8, 0, 2, 5, 0, 1],
[8, 0, 0, 0, 0, 5, 7, 6, 0],
[2, 0, 0, 6, 0, 3, 1, 9, 0],
[7, 0, 9, 0, 2, 1, 0, 0, 0],
[0, 0, 0, 9, 7, 4, 0, 8, 0]];
//this function prints the board
var printBoard = function () {
for (let row = 0; row < sol.length; row += 1) {
for (let col = 0; col < sol[row].length; col += 1) {
let id = 'r' + (row + 1) + (col + 1);
let elem = document.getElementById(id);
elem.innerHTML = sol[row][col];
}
}
};
function isValid(sol, row, col, k) {
for (let i = 0; i < 9; i++) {
const m = 3 * Math.floor(row / 3) + Math.floor(i / 3);
const n = 3 * Math.floor(col / 3) + i % 3;
if (sol[row][i] == k || sol[i][col] == k || sol[m][n] == k) {
return false;
}
}
return true;
}
var solve = function(sol) {
for (let row = 0; row < 9; row++) {
for (let col = 0; col < 9; col++) {
if (sol[row][col] == 0) {
//set the check for the row and column
for (let k = 1; k <= 9; k++) {
if (isValid(sol, row, col, k) == true) {
sol[row][col] == k;
//check if its not true, then check the row and column again
if(solve() == true) {
return true;
} else {
sol[row][col] = 0;
}
}
}
return false;
}
}
}
return true;
};
printBoard();
You never change sol before recursively calling solve() again.
if (isValid(sol, row, col, k) == true) {
sol[row][col] == '${k}'; // <- does not update sol
if(solve() == true) {
return true;
} else {
sol[row][col] == 0; // <- does not update sol
}
}
When you find an option for k that is a valid you should update sol before calling solve() again. Otherwise you will create an endless recursive loop which will eventually exceed the maximum stack size and result in the error you receive.
Use the assignment operator (=) instead of the equality operator (==) to assign a new value. Furthermore '${k}' will result in the literal ${k} use backticks (`) if you want to use string interpolation. I don't see a reason to use a string here, simply assigning k would be sufficient.
if (isValid(sol, row, col, k) == true) {
sol[row][col] = k; // <- assign k to a cell in the matrix
if(solve() == true) {
return true;
} else {
sol[row][col] = 0; // <- reset the cell back to 0 if it cannot be solved
}
}
I would also suggest using guard clauses. Instead of nesting the whole body of the for-loop inside an if-statement, use a guard and skip to the next iteration with continue:
for (...) {
if (check) {
// code
}
}
Can be changed to:
for (...) {
if (!check) continue;
// code
}
This generally improves the code indention and readability.

How to convert function in recursion [duplicate]

Could you please tell me how to find all subarray with sum equal to number
Example
arr[] = [2, 4, 45, 6, 0, 19]
x = 51
Output: [2,4,45]
Or
arr[] = [1, 11, 100, 1, 0, 200, 3, 2, 1, 280]
x = 280
Output: [280]
I tried like that but not getting correct output
function getSubArray(arr, num) {
var sum = 0,
blank = [];
var bigArr = []
for (var i = 0; i < arr.length; i++) {
sum = arr[i];
if (blank.length === 0) {
blank.push(arr[i]);
}
for (var j = 1; i < arr.length; j++) {
sum += arr[j];
if (sum < num) {
blank.push(arr[j])
} else if (sum > num) {
sum = 0;
blank = [];
break;
} else {
blank.push(arr[j])
bigArr.push(blank);
sum = 0;
blank = [];
}
}
}
return bigArr
}
console.log(getSubArray([1, 3, 6, 11, 1, 5, 4], 4));
for this expected output is
console.log(getSubArray([1, 3, 6, 11, 1, 5,4],4));
output: [1,3]
[4]
expected output
[[1,3], [4]] is my expected output
You could iterate the array and take either the next element or if no element is taken before omit this element.
function getSubset(array, sum) {
function iter(temp, delta, index) {
if (!delta) result.push(temp);
if (index >= array.length) return;
iter(temp.concat(array[index]), delta - array[index], index + 1);
if (!temp.length) iter(temp, delta, index + 1);
}
var result = [];
iter([], sum, 0);
return result;
}
console.log(getSubset([2, 4, 45, 6, 0, 19], 51)); // [2, 4, 45], [45, 6], [45, 6, 0]
console.log(getSubset([1, 11, 100, 1, 0, 200, 3, 2, 1, 280], 280)); // [280]
console.log(getSubset([1, 3, 6, 11, 1, 5, 4], 4)); // [1, 3], [4]
This might not be exactly what's needed - might require tweaking as the logic may be flawed here.
I have commented the code for clarification.
var arr = [1, 3, 6, 11, 1, 5,4]; // Define array
var target = 31; // Define target
// filter the numbers higher than target and sort rest ascending
var withinRange = arr.filter(x => x <= target).sort((a, b) => a - b);
if(arr.reduce((a,b) => a + b) < target) // Check if we have enough numbers to make up that number
throw "The max you can get out of your selection is: " + arr.reduce((a,b) => a + b);
// grab the highest number as a starting point and remove it from our array of numbers
var numbers = [withinRange.pop()];
var toFind = target - getSum(); // get remainder to find
for(var i = withinRange.length - 1; i > -1; i--) // iterate from the top
{
if(toFind == withinRange[i]){ // check if number is exactly what we need
numbers.push(withinRange[i]);
break;
}else if(withinRange[i] <= toFind){ // if number is smaller than what we look for
numbers.push(withinRange[i]);
toFind -= withinRange[i];
}
}
function getSum(){ // sum up our found numbers
if(numbers.length == 0) return 0;
return numbers.reduce((a,b) => a + b);
}
console.log([numbers, [target]]); // print numbers as desired output
console.log(target, getSum()) // print the target and our numbers
function combinations(array) {
return new Array(1 << array.length).fill().map(
(e1,i) => array.filter((e2, j) => i & 1 << j));
}
function add(acc,a) {
return acc + a
}
combinations([2, 4, 45, 6, 0, 19]).filter( subarray => subarray.reduce(add, 0) == 51 )
output
[[2,4,45],[45,6],[2,4,45,0],[45,6,0]]
combinations([1, 11, 100, 1, 0, 200, 3, 2, 1, 280]).filter( subarray => subarray.reduce(add, 0) == 280 )
output
[[280],[0,280]]
It will give all the available case. And I use the test case of #Nina Scholz
const sum = arr => arr.reduce((a,b) => a + b)
function cal(arr, x) {
const rs = []
for (let i = 0; i< arr.length; i++) {
const tmp = []
for (let j=i; j<arr.length; j++ ) {
tmp.push(arr[j])
if(sum(tmp) === x) rs.push([...tmp])
}
}
return rs
}
console.log(cal([1, 11, 100, 1, 0, 200, 3, 2, 1, 280], 280)) // -> [280]
console.log(cal([2, 4, 45, 6, 0, 19], 51)); // -> [2, 4, 45] [45, 6] [45, 6, 0]
console.log(cal([1, 3, 6, 11, 1, 5, 4], 4)); // -> [1,3] [4]
This will try every possible permutation of the array (will stop further permutations once limit is reached)
function test(arr, num) {
// sorting will improve time as larger values will be eliminated first
arr = arr.sort(function(a, b) {
return b - a;
});
var allLists = [];
var start = Date.now();
helper(0, 0, []);
console.log("Ms elapesed: " + (Date.now() - start));
return allLists || "Not found";
function helper(start, total, list) {
var result = [];
// Using for loop is faster because you can start from desired index without using filter, slice, splice ...
for (var index = start; index < arr.length; index++) {
var item = arr[index];
// If the total is too large the path can be skipped alltogether
if (total + item <= num) {
// Check lists if number was not included
var test = helper(index + 1, total, list.concat(result)); // remove for efficiency
total += item;
result.push(item);
//if (total === num) index = arr.length; add for efficiency
}
}
if (total === num) allLists.push(list.concat(result));
}
}
console.log(test([2, 4, 45, 6, 0, 19], 51)); // [2,4,45] [2,4,45,0] [6,45] [6,45,0]
console.log(test([1, 11, 100, 1, 0, 200, 3, 2, 1, 280], 280)); // [280] [280,0]
If you want to make it more efficient and just return one of the resulted array just comment out the recursive call. You can also un-comment the line that exits the loop once the limit has been reached (will skip 0s).
If the question is about finding all subsets (rather than subarrays) with the given cross sum it is also known as the perfect sum problem.
https://www.geeksforgeeks.org/perfect-sum-problem-print-subsets-given-sum/
// A recursive function to print all subsets with the
// help of dp[][]. Vector p[] stores current subset.
function printSubsetsRec(arr, i, sum, p)
{
// If we reached end and sum is non-zero. We print
// p[] only if arr[0] is equal to sun OR dp[0][sum]
// is true.
if (i == 0 && sum != 0 && dp[0][sum])
{
p.push(arr[i]);
console.log(p);
return;
}
// If sum becomes 0
if (i == 0 && sum == 0)
{
console.log(p);
return;
}
// If given sum can be achieved after ignoring
// current element.
if (dp[i-1][sum])
{
// Create a new vector to store path
var b = p.slice(0);
printSubsetsRec(arr, i-1, sum, b);
}
// If given sum can be achieved after considering
// current element.
if (sum >= arr[i] && dp[i-1][sum-arr[i]])
{
p.push(arr[i]);
printSubsetsRec(arr, i-1, sum-arr[i], p);
}
}
// Prints all subsets of arr[0..n-1] with sum 0.
function printAllSubsets(arr, sum)
{
var n = arr.length
if (n == 0 || sum < 0)
return;
// Sum 0 can always be achieved with 0 elements
dp = [];
for (var i=0; i<n; ++i)
{
dp[i] = []
dp[i][0] = true;
}
// Sum arr[0] can be achieved with single element
if (arr[0] <= sum)
dp[0][arr[0]] = true;
// Fill rest of the entries in dp[][]
for (var i = 1; i < n; ++i)
for (var j = 0; j < sum + 1; ++j)
dp[i][j] = (arr[i] <= j) ? dp[i-1][j] ||
dp[i-1][j-arr[i]]
: dp[i - 1][j];
if (dp[n-1][sum] == false)
{
console.log("There are no subsets with sum %d\n", sum);
return;
}
// Now recursively traverse dp[][] to find all
// paths from dp[n-1][sum]
var p = [];
printSubsetsRec(arr, n-1, sum, p);
}
printAllSubsets([1,2,3,4,5], 10);
Solution
'use strict';
function print(arr[], i, j) {
let k = 0;
for (k = i; k <= j; k += 1) {
console.log(arr[k]);
}
}
function findSubArrays(arr[], sum) {
let n = arr.length;
let i;
let j;
let sum_so_far;
for (i = 0; i<n; i+= 1) {
sum_so_far = 0;
for (j = i; j < n; j++) {
sum_so_far += arr[j];
if (sum_so_far === sum) {
print(arr, i, j);
}
}
}
}
I would first loop depending on the size of expected arrays.
After that loop for looking for first part of the array which should be filled with positions that will match the desired number.
For example for x= 4 having arr=[5,4,32,8,2,1,2,2,3,4,4]
It would first take the 4's. Output will start on [ [4], [4], [4], ..... ] for positions 1,9,10 (respectively)
Then go for the arrays resulting sum of 2 elements [ ... [2,2], [2,2],[2,2], [1,3] ...] ( positions 4+6, position 4+7 position6+7 and position 5+8)
You would probably want to use another function to sum and check at this point.
Now will do the same for sum of 3 elements (if any) and so on, having max loop set at number of original array (the resulting number could be the sum of all the elements in the array).
The resulting example would be [ [4], [4], [4], [2,2], [2,2],[2,2], [1,3]]
If the elements would be strictly positive, one could collect such subsequences in a single pass, progressing in a worm/caterpillar-like way: stretching its front in order to grow the sum (when it is bellow the target) and contracting its back in order to lower the sum:
function worm(arr,target){
var ret=[];
var head=0;
var tail=0;
var sum=0;
while(head<arr.length){
while(sum<=target && head<arr.length){
sum+=arr[head++];
if(sum===target)
ret.push(arr.slice(tail,head));
}
while(sum>=target && tail<head){
sum-=arr[tail++];
if(sum===target)
ret.push(arr.slice(tail,head));
}
}
return JSON.stringify(arr)+": "+JSON.stringify(ret);
}
console.log(worm([2, 4, 45, 6, 19], 51));
console.log(worm([1, 11, 100, 1, 200, 3, 2, 1, 280], 280));
console.log(worm([1, 3, 6, 11, 1, 5, 4], 4));
console.log("But it only occasionally finds 0+... / ...+0 sums:");
console.log(worm([2, 4, 45, 6, 0, 19], 51));
console.log(worm([2, 4, 0, 45, 0, 6, 0, 19], 51));
console.log(worm([0, 2, 4, 0, 45, 0, 6, 0, 19], 51));
One way to deal with the problem related to bounding zeroes is to throw such sequences away. This snippet keeps tail and head(-1) on non-zero elements:
function worm(arr,target){
var ret=[];
var head=0;
while(head<arr.length && arr[head]===0)head++;
var tail=head;
var sum=0;
while(head<arr.length){
while(sum<=target && head<arr.length){
while(head<arr.length && arr[head]===0)head++;
sum+=arr[head++];
if(sum===target)
ret.push(arr.slice(tail,head));
}
while(sum>=target && tail<head){
sum-=arr[tail++];
while(tail<head && arr[tail]===0)tail++;
if(sum===target)
ret.push(arr.slice(tail,head));
}
}
return JSON.stringify(arr)+": "+JSON.stringify(ret);
}
console.log(worm([2, 4, 45, 6, 19], 51));
console.log(worm([1, 11, 100, 1, 200, 3, 2, 1, 280], 280));
console.log(worm([1, 3, 6, 11, 1, 5, 4], 4));
console.log(worm([2, 4, 45, 6, 0, 19], 51));
console.log(worm([2, 4, 0, 45, 0, 6, 0, 19], 51));
console.log(worm([0, 2, 4, 0, 45, 0, 6, 0, 19], 51));
console.log(worm([0, 2, 4, 0, 45, 0, 6, 0, 19, 26], 51));
console.log(worm([0, 2, 4, 0, 45, 0, 6, 0, 19, 26, 0], 51));
console.log(worm([1,8,2], 10));
console.log(worm([0,1,0,8,2,0], 10));
console.log(worm([0,8,2,8,0], 10));
console.log(worm([0,8,0,2,0,8,0], 10));
And the code loses all of its remaining beauty if someone actually needs those 0+... / ...+0 sequences, as they have to be generated in a post-processing step:
function worm(arr,target){
var pairs=[];
var head=0;
while(head<arr.length && arr[head]===0)head++;
var tail=head;
var sum=0;
while(head<arr.length){
while(sum<=target && head<arr.length){
while(head<arr.length && arr[head]===0)head++;
sum+=arr[head++];
if(sum===target)
pairs.push([tail,head]);
}
while(sum>=target && tail<head){
sum-=arr[tail++];
while(tail<head && arr[tail]===0)tail++;
if(sum===target)
pairs.push([tail,head]);
}
}
var ret=[];
for([tail,head] of pairs){
(function pre(tail,head){
ret.push(arr.slice(tail,head));
if(tail>0 && arr[tail-1]===0)
pre(tail-1,head);
(function post(tail,head){
if(head<arr.length && arr[head]===0){
ret.push(arr.slice(tail,head+1));
post(tail,head+1);
}
})(tail,head);
})(tail,head);
}
return JSON.stringify(arr)+": "+JSON.stringify(ret);
}
console.log(worm([2, 4, 45, 6, 19], 51));
console.log(worm([1, 11, 100, 1, 200, 3, 2, 1, 280], 280));
console.log(worm([1, 3, 6, 11, 1, 5, 4], 4));
console.log(worm([2, 4, 45, 6, 0, 19], 51));
console.log(worm([2, 4, 0, 45, 0, 6, 0, 19], 51));
console.log(worm([0, 2, 4, 0, 45, 0, 6, 0, 19], 51));
console.log(worm([0, 2, 4, 0, 45, 0, 6, 0, 19, 26], 51));
console.log(worm([0, 2, 4, 0, 45, 0, 6, 0, 19, 26, 0], 51));
console.log(worm([1,8,2], 10));
console.log(worm([0,1,0,8,2,0], 10));
console.log(worm([0,8,2,8,0], 10));
console.log(worm([0,8,0,2,0,8,0], 10));
I think it works (for non-negative elements), but the first one was simpler for sure.
With map and filter
const arr = [2, 4, 45, 6, 0, 19]
let t = 0
const result = arr.map((v,i)=>{
return [v, t += v]
}).filter((v,i)=>v[1]<=51)
console.log(result)

Flood-fill algorithm in Javascript not filling the entire grid when using for loops

I've been working at this all day, and I can't work out why this algorithm doesn't cover the entire grid when starting at position grid[1][1].
I start by setting up the grid and calculating the number of rows and columns in the grid to give me a limit on the edges of the array.
I then call the function, setting position grid[1][1] = 1, calculating the offset and making sure it is not outside the array and making sure the new position has not already been called before calling the function recursively.
I just can't figure out why this isn't working!
var grid = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]];
var cols = grid.length;
var rows = grid[0].length;
floodFill(1, 1)
function floodFill(col, row) {
grid[col][row] = 1;
for (c_off = -1; c_off < 2; c_off++) {
for (r_off = -1; r_off < 2; r_off++) {
var i = col + c_off;
var j = row + r_off;
if (i > -1 && i < cols && j > -1 && j < rows) {
if (grid[i][j] != 1) {
floodFill(i, j);
}
}
}
}
}
grid;
/*
output:
[ [ 1, 1, 1, 1, 1 ],
[ 1, 1, 1, 1, 1 ],
[ 1, 1, 1, 1, 1 ],
[ 1, 1, 1, 1, 0 ],
[ 1, 1, 0, 0, 0 ] ]
expected output:
[ [ 1, 1, 1, 1, 1 ],
[ 1, 1, 1, 1, 1 ],
[ 1, 1, 1, 1, 1 ],
[ 1, 1, 1, 1, 1 ],
[ 1, 1, 1, 1, 1 ] ]
*/
That's because c_off and r_off are not defined as local variables (via the var or let keywords) so they are treated like a global variables which means that a recursive invocation of floodFill() overwrites the values for its calling invocation thereby interfering with the caller's iteration order.
The fix is simple: just add a var keyword in both for loops:
function floodFill(col, row) {
grid[col][row] = 1;
for (var c_off = -1; c_off < 2; c_off++) {
for (var r_off = -1; r_off < 2; r_off++) {
var i = col + c_off;
var j = row + r_off;
if (i > -1 && i < cols && j > -1 && j < rows) {
if (grid[i][j] != 1) {
floodFill(i, j);
}
}
}
}
}
Appendix
You can move the detection of out-of-grid condition, and the check whether the given point is already filled, to the beginning of the function (instead of doing it just before the recursive call). Some may argue that the resulting code is simpler to understand:
function floodFill(col, row) {
if (col < 0 || col >= cols || row < 0 || row >= rows) {
return;
}
if (grid[col][row] == 1) {
return;
}
grid[col][row] = 1;
for (var c_off = -1; c_off < 2; c_off++) {
for (var r_off = -1; r_off < 2; r_off++) {
floodFill(col + c_off, row + r_off);
}
}
}
I think Itay Maman answer is probably better. I solved it this way. by changing c_off < 5 and r_off < 5
var grid = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]];
var cols = grid.length;
var rows = grid[0].length;
floodFill(1, 1)
function floodFill(col, row) {
grid[col][row] = 1;
for (c_off = -1; c_off < 5; c_off++) {
for (r_off = -1; r_off < 5; r_off++) {
var i = col + c_off;
var j = row + r_off;
if (i > -1 && i < cols && j > -1 && j < rows) {
if (grid[i][j] != 1) {
floodFill(i, j);
}
}
}
}
}
console.log(grid);

Categories

Resources