I'm looking for an algorithm that will allow me to quickly access (not necessarily print) every X item subset of a 300 item set that, when the values of the items in the subset are added up, equals Y. Repetition is allowed and order is important. All values of the 300 item set are positive.
So for example,
300 item set: [1.5, 1.34, 3, .25, 2.333, 1.75, .125, .675, 2, 4, .75, ....]
X = 5
Y = 6
Algorithm generates:
[2, 2, 1.5, .25, .25]
[2, 2, .25, 1.5, .25]
[2, 1.5, 2, .25, .25]
[1.5, 2, 2, .25, .25]
[2, 1.75, .5, .5, 1.25]
[2, 1.75, .5, 1.25, .5]
[2, 1.75, 1.25, .5, .5]
[2, 1.25, .5, .5, 1.75]
[1.25, 2, .5, .5, 1.75]
[.5, 2, .5, 1.25, 1.75]
[3, 1, 1, .5, .5]
[1, 3, 1, .5, .5]
[1, 1, 3, .5, .5]
[1, 1, .5, 3, .5]
[1, 1, .5, .5, 3]
And so on....
[2, 2, 1.5, .25, .25] is allowed and
[2, 1.75, .5, .5, 1.25] is not the same thing as [1.25, .5, .5, 1.75, 2].
I realize that this is a variation of the Subset Sum problem but I can't seem to find any helpful examples of this variation anywhere online. Right now my current solution implements nested loops (the number of nested loops is determined by the value of X) That works fine when X is small but quickly get very slow when X starts going up. Any input would be appreciated!
An approach is to collect subsets and check if the length of the subset and sum fits.
If necessary, you could permutate the subsets.
function subsetSum(array, sum, items) {
function iter(taken, index, subsum) {
if (taken.length === items && subsum === sum) return result.push(taken);
if (taken.length === items || subsum === sum || index === array.length) return;
iter([...taken, array[index]], index, subsum + array[index]);
iter(taken, index + 1, subsum);
}
var result = [];
iter([], 0, 0);
return result;
}
var result = subsetSum([1.5, 1.34, 3, .25, 2.333, 1.75, .125, .675, 2, 4, .75], 6, 5);
console.log(result.map(a => a.join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
So if I have a 2D array such as
const array = [
[1, 2, 3, 4, 5],
[9, 8, 7, 6, 5],
[1, 8, 3, 6, 5],
[9, 8, 7, 6, 5],
[1, 8, 3, 6, 5],
[1, 2, 3, 4, 5]
];
which could for the point of the question be any size, square or not square.
and I want to extract 3x3 arrays out of it, for instance at 1,1 that would be const sub = [[8, 7, 6], [8, 3, 6], [8, 7, 6]]. So far so good - I can do this. However I am flattening the 2D array so that its represented as a 1D array (long story as to why), i.e
const array = [1, 2, 3, 4, 5, 9, 8, 7, 6, 5, 1, 8, 3, 6, 5, 9, 8, 7, 6, 5, 1, 8, 3, 6, 5, 1, 2, 3, 4, 5];
What I'm trying to do is extract the same (or any) 3x3 array out of this but while its represented as a 1D array, so I would then get back [8, 7, 6, 8, 3, 6, 8, 7, 6].
I almost got there, however I made an error of only working with arrays that were 9x9 and always extracting 3x3 subsets which mean that my solution only works for that specific case, and the more I stare at my solution, the more I cannot work out what the generic solution would look like.
My solution:
const extractSubsetFrom1D = (array, subHeight, subWidth, startRow, startCol) => {
const kWH = subWidth * subHeight
const subset = array.slice(((((kWH - 2) * startRow) + startCol) * kWH), ((((kWH - 2) * startRow) + startCol) * kWH) + kWH)
return subset
}
In my case subHeight and subWidth were always equalling 3 respectively, and as the array itself was always 9x9 I believe I accidentally stumbled on a solution for that specific case as they divide nicely into each other.
To be clear my solution will fail for the startRow = 1 startCol = 0 for the provided array (it works for the startRow = 0 scenario
It's not entirely clear to me how you came to your current implementation, but I can at least tell:
✅ You correctly determined the size of the sub grid array to return (kWH)
❌ You incorrectly assume you can slice out a sub grid as one continuous part of the original 1d array
🟠 The calculation of the first element seems kind-of-right but is actually wrong (probably because of the previous mistake)
From (y,x) to i
Let's start from scratch and work our way up to a nice one liner.
In a 2d-array, you can get a cell's value by doing:
cellValue = grid2d[y][x]
Once you flatten it, you'll need to do:
cellValue = grid1d[y * GRID_WIDTH + x]
y * GRID_WIDTH takes you to the start of the right row, and + x gets you to the right column.
As you can see, you need to know the original grid's size before you can even query a specific cell. That means your extract function would need an argument to pass the original width (or, if the grids are guaranteed to be square, you can do Math.sqrt(array.length).
A slice per row
Let's use this math to find the indices of a 2x2 sub grid at (1,1) extracted from a 3x3 source grid:
0 1 2
3 [4][5]
6 [7][8]
As you can see, the resulting indices are [4,5,7,8]. There is no way to slice these indices out of the source array directly because we don't want to include the 6.
Instead, we can use a nested loop to skip the gaps between our rows:
const to1d = (x, y, w) => y * w + x;
const extractSubGrid1D = (grid, gridWidth, x, y, w, h) => {
const yTop = y;
const yBottom = y + h
const xLeft = x;
const xRight = x + w;
const subgrid = [];
for (let y = yTop; y < yBottom; y += 1) {
for (let x = xLeft; x < xRight; x += 1) {
const index = to1d(x, y, gridWidth);
subgrid.push(grid[index]);
}
}
return subgrid;
}
const originalGrid = [
0, 1, 2,
3, 4, 5,
6, 7, 8
];
console.log(
extractSubGrid1D(originalGrid, 3, 1, 1, 2, 2)
)
Once you get a feel for the logic, feel free to refactor.
The other way around
To go from a 1d-index to a 2d coordinate, you can do:
x = i % w
y = Math.floor(i / w)
Applying this logic, you can also fill your sub grid like so:
Create a new array of the right size
For each of its indices, determine the original grid's (x, y) coordinate
Transform that coordinate back to an index to query the original grid with
const to1d = (x, y, w) => y * w + x;
const extractSubGrid1D = (grid, gridWidth, x, y, w, h) => Array.from(
{ length: w * h },
(_, i) => grid[to1d(x + i % w, y + Math.floor(i / w), gridWidth)]
)
const originalGrid = [
0, 1, 2,
3, 4, 5,
6, 7, 8
];
console.log(
extractSubGrid1D(originalGrid, 3, 1, 1, 2, 2)
)
Let's suppose I have the following grid and each cell of the grid has an index mapped to a 1d array.
0, 1, 2
3, 4, 5,
6, 7, 8
I could represent this with a 1d array like: [0, 1, 2, 3, 4, 5, 6, 7, 8]
I would like to know a simple way to map a 2d coordinate like (3,1) to its index in the array, in this case, would be 2.
After researching a lot, I found a lot of people suggesting this equation: index = x + (y * width), but it doesn't seem to work in my tests.
For example for (1, 1), the result would be index = 1 + (1 * 3) = 4, and for (3, 1) would be index = 3 + (1 * 3) = 6, which does not make any sense to me.
Is it possible to achieve this in a simple way? Or I would need to use iterators like a for?
2D matrix notation is commonly (row, col), with indexes starting at 0.
Thus, (3, 1) is invalid: only 3 rows, from 0 to 2. (1, 1) means 2nd row, 2nd colum, which is 4 in your example. The formula is thus:
(row * width) + col
(2, 1) = 2*3+1 = index 7
once again using 0 for the first row/col.
If you really want to keep thinking with indexes starting at one, just change the formula to:
((row - 1) * width) + (col - 1) = 1D index
In your case it would be index = (x - 1) + ((y - 1) * width) as your coordinate system starts from 1 and arrays start from 0.
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8];
function getPosition(x, y, width) {
return x - 1 + (y - 1) * width;
}
console.log({
position: getPosition(3, 1, 3),
element: arr[getPosition(3, 1, 3)]
});
It is indeed index = x + y * width (the parens are unnecessary) or index = y + x * width, depending on whether you want your flat array to keep the rows together as in your question ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], index = x + y * width), or keep columns together ([0, 3, 6, 1, 4, 7, 2, 5, 8], index = y + x * width). But indexes usually start at 0, not 1. So your (1, 1) would be (0, 0) and your (3, 1) would be (2, 0).
Here's the first:
// 0, 1, 2
// 3, 4, 5
// 6, 7, 8
const a = [0, 1, 2, 3, 4, 5, 6, 7, 8];
let x = 0, y = 1;
let index = x + y * 3;
console.log(`(${x}, ${y}) is index ${index}, value ${a[index]}`);
x = 2;
y = 0;
index = x + y * 3;
console.log(`(${x}, ${y}) is index ${index}, value ${a[index]}`);
Here's the second:
// 0, 1, 2
// 3, 4, 5
// 6, 7, 8
const a = [0, 3, 6, 1, 4, 7, 2, 5, 8];
let x = 0, y = 1;
let index = y + x * 3;
console.log(`(${x}, ${y}) is index ${index}, value ${a[index]}`);
x = 2;
y = 0;
index = y + x * 3;
console.log(`(${x}, ${y}) is index ${index}, value ${a[index]}`);
I am trying to develop a 2 player checkers game, for my college, but I am stuck at getting the index of the 2D array when I click on the piece.
I divided my HTML code in:
table - the game board
row - each row is the height of the array
cell - each cell is a piece and the width of the array
Then I setted a default array to start the game:
var board = [
[0, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 1, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[2, 0, 2, 0, 2, 0, 2, 0],
[0, 2, 0, 2, 0, 2, 0, 2],
[2, 0, 2, 0, 2, 0, 2, 0],
];
Where the:
- 0 - empty cell
- 1 - Player 1 pieces
- 2 - Player 2 pieces
To get the position I am using this code
function getPosition() {
$('.row').on('click', function() {
console.log( $('.row').index(this) );
});
$('.cell').on('click', function() {
console.log( $('.cell').index(this) );
});
}
Get the height array position which should be between 0 and 7 are ok, but the cell from the row should be between 0 and 7 too, but using this I am getting from 0 to 63, using this parameters I have no idea how to start the next comparisons of the game.
Here is the code from codepen
http://codepen.io/michaelthompson/pen/jVdrav
In each instance you can simply use $(this).index() which will return the index within the element's siblings
But since clicking on a row always means clicking a cell you could combine them and do
$('.cell').on('click', function() {
var $cell = $(this),
columnIndex = $cell.index(),
rowIndex = $cell.parent().index();
});
What $('.cell').index(this) is doing is taking the whole collection of the class within the page and that's why you are getting 0-63
I'm creating an isometric tile map for a small www game, I have created an array to store map data, column and row numbers aswell as height and width of tile. Then I found function getTile on web which is on the end of this map array. It should be giving me value of the tile later on. It looks like this:
var map = {
cols: 13,
rows: 11,
twidth: 200,
theight: 100,
tiles: [
1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1,
1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 1,
1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1,
2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1,
],
getTile: function(col, row) {
return this.tiles[row * map.cols + col]
}
};
Then I have a code to draw something on the canvas:
for (var c = 0; c < map.cols; c++) {
for (var r = 0; r < map.rows; r++) {
var tile = map.getTile(c, r);
var screenX = (c - r) * map.twidth/2;
var screenY = (c + r) * map.theight/2;
var screenX = screenX+1000;
if(tile == 1) {
var element = "area_grass_01";
}
if(tile == 2) {
var element = "area_road_01";
}
var img = document.getElementById(element);
ctxObj.drawImage(img, (tile - 1) * map.twidth, 0, map.twidth, map.theight, screenX, screenY, map.twidth, map.theight);
}
}
Now, when I run console_log or alert with tile variable in it when the loop is run. It shows all the numbers that are included in map.tiles one by one. However when I try to find out which image should be drawn like this:
if(tile == 1) {
var element = "area_grass_01";
}
if(tile == 2) {
var element = "area_road_01";
}
var img = document.getElementById(element);
It only draws the title with value 1. Rest stays undrawn. Like this:
Map render
Now I want to ask you how do I actually set the image according to the array number I've put inside map.tiles?
And another question that I have to anyone familiar with this. If I have a tile width 200 and height 100, how do I draw let's say a building which is much higher? Do I find the height of that drawing and set it higher by the drawing size - tile height (which is 100), or do you have any other advice on how to draw higher elements? And do I still use the same drawing technique for this:
var screenX = (c - r) * map.twidth/2;
var screenY = (c + r) * map.theight/2;
But with adjustment of map.theight in screenY?
You are using an overload of drawImage of which the second and third parameters are the source X and Y values. You only need to use these if you are making use of a spritesheet which it doesn't seem like you are. Try replacing the second parameter of that function call with just 0.
Take a look at the sx, sy, swidth and sheight parameters here for more information.
I've a list of coordinates that define a grid of elements with different sizes.
Each element is positioned starting from the top left corner, giving the starting column, the starting row and its width and height.
{
"col": 1,
"row": 1,
"size_x": 3,
"size_y": 2
},
{
"col": 4,
"row": 1,
"size_x": 3,
"size_y": 2
},
{
"col": 1,
"row": 3,
"size_x": 3,
"size_y": 2
},
{
"col": 4,
"row": 3,
"size_x": 3,
"size_y": 2
},
{
"col": 1,
"row": 5,
"size_x": 3,
"size_y": 2
},
{
"col": 4,
"row": 5,
"size_x": 3,
"size_y": 2
}
This set of coordinates was defined on a 6 columns grid.
Now the grid has 12 columns (and each row is the half of before) so I need to multiply by 2 the size_x and size_y.
But this is not enough, I need also to update the col and row coordinates to prevent collisions and achieve the same layout of the old 6 columns grid.
This is the script I've prepared, it enlarges the size_x and size_y by 2, and fixes the col somehow...
But I can't find a way to complete it and make it reliable.
http://codepen.io/FezVrasta/pen/qbrYGv?editors=001
The layout, once applied to the UI, should look like this:
[---][---]
[---][---]
[---][---]
- = 1 col
[ = 2 rows
I am not 100% clear on the question, but I assume you want a six-column grid to evolve to a 12-column grid, and with some basic Math that is quite easy. I have renamed some variables to what I find comfortable to work with when talking about positioning, but the following logs the grid when it was 12 columns wide, and it will work with any grid you pass it that is structured as an array of objects (which hopefully only contain numbers).
Update I have corrected some stupid mistakes in my script and changed the output and input so you can see them directly in the snippet. Hope it helps!
var grid = [
{ x: 1, y: 1, w: 3, h: 2 },
{ x: 4, y: 1, w: 3, h: 2 },
{ x: 1, y: 3, w: 3, h: 2 },
{ x: 4, y: 3, w: 3, h: 2 },
{ x: 1, y: 5, w: 3, h: 2 },
{ x: 4, y: 5, w: 3, h: 2 }
];
document.getElementById('input').textContent = JSON.stringify(grid);
function resizeGrid(grid, from, to){
// Loop through the grid
for(var i = 0; i < grid.length; i++)
// Loop through the values of the grid
for(var key in grid[i])
// This is a safety check to not enumerate inherited properties
// not necessary, but I find it safer when doing for..in loops
if(grid[i].hasOwnProperty(key))
// Simply divide the value by the original and
// multiply by the desired output
grid[i][key] = grid[i][key] / from * to
return grid;
}
document.getElementById('output').textContent = JSON.stringify(resizeGrid(grid, 6, 12));
<div id="input"></div>
<div id="output"></div>
I am also not 100% sure how you set up your grid, but it might be best to keep your top-left most coordinate as 0 - the reason being that if you have something at 0, 0 it will remain in that corner when you resize the grid - if your coordinates start at 1, this becomes much more complicated as 1,1 will now become 2,2, even though it should not change. Always count from 0!
In order to fix this 1,1 problem, you can simply replace this line:
grid[i][key] = grid[i][key] / from * to;
with this line:
grid[i][key] = grid[i][key] === 1
? 1
: grid[i][key] / from * to;
Heres an implementation:
var grid = [
{ x: 1, y: 1, w: 3, h: 2 },
{ x: 4, y: 1, w: 3, h: 2 }
];
document.getElementById('input').textContent = JSON.stringify(grid);
function resizeGrid(grid, from, to){
// Loop through the grid
for(var i = 0; i < grid.length; i++)
for(var key in grid[i])
if(grid[i].hasOwnProperty(key))
switch(key){
case 'w': case 'h': grid[i][key] = grid[i][key] / from * to; break;
case 'x': case 'y': grid[i][key] = (grid[i][key] - 1) / from * to + 1; break;
}
return grid;
}
document.getElementById('output').textContent = JSON.stringify(resizeGrid(grid, 6, 12));
<div id="input"></div>
<div id="output"></div>