Even sets of integers - javascript

Given an array of integers heights I would like to split these into n sets, each with equal totalHeight (sum of the values in set), or as close to as possible. There must be a fixed distance, gap, between each value in a set. Sets do not have to have the same number of values.
For example, supposing:
heights[0, 1, 2, 3, 4, 5] = [120, 78, 110, 95, 125, 95]
n = 3
gaps = 10
Possible arrangements would be:
a[0, 1], b[2, 3], c[4, 5] giving totalHeight values of
a = heights[0] + gap + heights[1] = 120 + 10 + 78 = 208
b = heights[2] + gap + heights[3] = 110 + 10 + 95 = 215
c = heights[4] + gap + heights[5] = 125 + 10 + 95 = 230
a[0], b[1, 2, 3], c[4, 5] giving totalHeight values of
a = heights[0] = 120
b = heights[1] + gap + heights[2] + gap + heights[3] = 303
c = heights[4] + gap + heights[5] = 125 + 10 + 95 = 230
And so on. I want to find the combination that gives the most evenly-sized sets. So in this example the first combination is better since it gives an overall error of:
max - min = 230 - 208 = 22
Whereas the second combination gives an error of 183. I'm trying to do this in JavaScript, but I'm just looking for some sort of outline of an algorithm. Pseudo code or whatever would be great. Any help would be highly appreciated.
MY POOR ATTEMPTS: Obviously one way of solving this would be to just try every possible combination. That would be horrible though once heights gets large.
Another method I tried is to get the expected mean height of the sets, calculated as the sum of the values in height / n. Then I tried to fill each set individually by getting as close to this average as possible. It works alright in some cases, but it's too slow.
NOTE: If it helps, I would be happy to have symmetric sets. So for example, with sets (a, b c), a = b. Or with five sets (a, b, c, d, e), a = b and c = d. I think this would be even more difficult to implement but I could be wrong.
EDIT: For anyone who may be interested, the best I could come up with was the following algorithm:
Sort heights in descending order.
Create n sets.
Put the first n values from heights into the first slot of each set. i.e. put the n largest values at the start of each set. Remove the values from heights as they are added.
While heights.count > 0
Find the smallest totalHeight (including gap) in each of the n sets.
Add the next value in heights to this set (and remove the value from heights).
Then there's some little algorithm at the end where each set can make x number of swaps with the other sets, if the totalHeight gets closer to the average. I'm keeping x small because this process could go on forever.
It's not terrible, but obviously not perfect.

Seems like it is NP-complete and reducible to Subset sum problem or more precisely to Partition problem.

Your second approach - finding the mean (height / n), then attempting to fill sets with a mean as close as possible seems like a good practical approach. You say this is too slow... the following implementation is O(n*m log n) where m is the maximum number of elements allowed in a set. If m can be very large, then this could be quite slow, however if m is constrained to be within a certain range then it could approach O(n log n) which is about as fast as you are going to get.
Find the mean height of all values. h_mean = Sum(h) / n; O(n).
Sort all heights. O(n log n).
Examine the highest and lowest height.
Add the value which is furthest from the mean to a new set.
Remove this value from the sorted heights.
Repeat for max_number allowed in set = 1 .. m (m < n / 2)
{
Repeat:
{
If the set mean is higher than the mean.
Add the lowest value from the sorted heights.
Remove this value from the sorted heights.
If the set mean is lower than the mean
Add the highest value from the sorted heights.
Remove this value from the sorted heights.
Recalculate the set mean (taking account of the gap).
If the new set mean is further from the h_mean than the last OR
If the set has too many elements
break
}
Until all numbers are used.
Keep track of the standard deviations for this assignment.
If this assignment is the best so far, keep it.
}
This isn't going to give a provably optimal solution, but it's simple and that has a lot going for it...
Note, in this algorithm, all sets have the same number of elements, m. You repeat an iteration for different values of m, say, 2, 3, 4 (note that m should be a factor of N). Each set ends up with approximately m * mean_height for total height.
You may ask, well what if N is prime?
Then clearly, one set will value short on total value.
Does this mean this algorithm is useless?
Not at all. It's simple and it should produce a good first attempt at a solution. You may wish to use this algorithm first, then refine the first result using optimization techniques (such as selective swapping of heights being sets).

Related

Algorithm for this gpu operation?

I'm using a library that allows computing large matrix operations on the GPU, gpu.js. What I'm trying to do is not really hard I don't think but I can't seem to begin to figure out the algorithm for it. Basically I have an array buffer stored as r,g,b,a repeated for each pixel, so a 4x4 image would be a an array of 64 values.
I want to output an an image X times larger than the input, but with "nearest neighbor" calculation, so every pixel just becomes a 2x2 square, or 3x3, etc.
So the operation is set up like this (note gpu.js requires arrays as inputs) and keep in mind it's iterating over the full size output, so I have to find the correct coordinates that will exist in the smaller sourceBuffer based on the current index in the outputBuffer (index is exposed by the lib as this.thread.x).
var pixelateMatrix = gpu.createKernel(function(inputBuffer, width, height, scale) {
var y = Math.floor(this.thread.x / (width[0] / scale[0]) * 4);
var x = this.thread.x % ((width[0] / scale[0]) * 4);
var remainder = this.thread.x % 4;
return inputBuffer[x * (width[0] * 4) + y * 4 + remainder];
}).setOutput([width * height * 4]);
This is what I tried but right now it's weirdly only outputting the current width of the screen as the value for each entry.
What's the correct algorithm for this? Normally I'm used to doing this kind of thing with a loop iterating over the source, but in this case I have to work with each pixel rgba value individually in a 1 dimentional array and I'm confused on how to do this.
Also obviously I need to do it with as few operations as possible.
Depends a bit if you store the item row or column major. Assuming you do row major.
Each row will look be r1 g2 b1 a1 r2 g2 b2 a2 .... then it will be followed by the next row and so on. You need to know how large is the image (at least how large each row is), i'll call this N. So the get component c from row x column y you need value_pos = x * (N * 4) + y * 4 + c. Everything here is 0 indexed. You can use the value for both reading and writing, just update N between the two since they are supposed to have different sizes.

How can I find all the sum of combinations of elements in an array [duplicate]

I am having issues with understanding dynamic programming solutions to various problems, specifically the coin change problem:
"Given a value N, if we want to make change for N cents, and we have infinite supply of each of S = { S1, S2, .. , Sm} valued coins, how many ways can we make the change? The order of coins doesn’t matter.
For example, for N = 4 and S = {1,2,3}, there are four solutions: {1,1,1,1},{1,1,2},{2,2},{1,3}. So output should be 4. For N = 10 and S = {2, 5, 3, 6}, there are five solutions: {2,2,2,2,2}, {2,2,3,3}, {2,2,6}, {2,3,5} and {5,5}. So the output should be 5."
There is another variation of this problem where the solution is the minimum number of coins to satisfy the amount.
These problems appear very similar, but the solutions are very different.
Number of possible ways to make change: the optimal substructure for this is DP(m,n) = DP(m-1, n) + DP(m, n-Sm) where DP is the number of solutions for all coins up to the mth coin and amount=n.
Minimum amount of coins: the optimal substructure for this is
DP[i] = Min{ DP[i-d1], DP[i-d2],...DP[i-dn] } + 1 where i is the total amount and d1..dn represent each coin denomination.
Why is it that the first one required a 2-D array and the second a 1-D array? Why is the optimal substructure for the number of ways to make change not "DP[i] = DP[i-d1]+DP[i-d2]+...DP[i-dn]" where DP[i] is the number of ways i amount can be obtained by the coins. It sounds logical to me, but it produces an incorrect answer. Why is that second dimension for the coins needed in this problem, but not needed in the minimum amount problem?
LINKS TO PROBLEMS:
http://comproguide.blogspot.com/2013/12/minimum-coin-change-problem.html
http://www.geeksforgeeks.org/dynamic-programming-set-7-coin-change/
Thanks in advance. Every website I go to only explains how the solution works, not why other solutions do not work.
Lets first talk about the number of ways, DP(m,n) = DP(m-1, n) + DP(m, n-Sm). This in indeed correct because either you can use the mth denomination or you can avoid it. Now you say why don't we write it as DP[i] = DP[i-d1]+DP[i-d2]+...DP[i-dn]. Well this will lead to over counting , lets take an example where n=4 m=2 and S={1,3}. Now according to your solution dp[4]=dp[1]+dp[3]. ( Assuming 1 to be a base case dp[1]=1 ) .Now dp[3]=dp[2]+dp[0]. ( Again dp[0]=1 by base case ). Again applying the same dp[2]=dp[1]=1. Thus in total you get answer as 3 when its supposed to be just 2 ( (1,3) and (1,1,1,1) ). Its so because
your second method treats (1,3) and (3,1) as two different solution.Your second method can be applied to case where order matters, which is also a standard problem.
Now to your second question you say that minimum number of denominations can
be found out by DP[i] = Min{ DP[i-d1], DP[i-d2],...DP[i-dn] } + 1. Well this is correct as in finding minimum denominations, order or no order does not matter. Why this is linear / 1-D DP , well although the DP array is 1-D each state depends on at most m states unlike your first solution where array is 2-D but each state depends on at most 2 states. So in both case run time which is ( number of states * number of states each state depends on ) is the same which is O(nm). So both are correct, just your second solution saves memory. So either you can find it by 1-D array method or by 2-D by using the recurrence
dp(n,m)=min(dp(m-1,n),1+dp(m,n-Sm)). (Just use min in your first recurrence)
Hope I cleared the doubts , do post if still something is unclear.
This is a very good explanation of the coin change problem using Dynamic Programming.
The code is as follows:
public static int change(int amount, int[] coins){
int[] combinations = new int[amount + 1];
combinations[0] = 1;
for(int coin : coins){
for(int i = 1; i < combinations.length; i++){
if(i >= coin){
combinations[i] += combinations[i - coin];
//printAmount(combinations);
}
}
//System.out.println();
}
return combinations[amount];
}

Optimizing an algorithm subtracting first elements from last elements

I'm trying to optimize a slow part of a larger algorithm.
I have an array of random-looking numbers:
[295, 292, 208, 393, 394, 291, 182, 145, 175, 288, 71, 86, 396, 422]
(actual arrays are much longer), and an index: N (N = 5)
What I want to do is subtract subtract 1 from the last M elements for each of the first N elements that are smaller
So (pseudo-code):
for a = 1..5
for b = 6..N
if ary[a] < ary[b]
ary[b]--;
Obviously this is a horribly inefficient O(N^2) algorithm. I'm trying to think of a faster way to do it, but can't. It seems like I should be able to pre-compute the values to subtract somehow, and reduce it to:
for a = 1..5
// ???
for b = 6..N
ary[b] -= ???
but I'm missing something.
[edit] I feel like an idiot. I didn't properly explain what I want, fixed.
Let's reorganize the loops:
for b = 6..N
for a = 1..5
if ary[a] < ary[b]
ary[b] -= ary[a];
The result will be the same. But now the logic is more clear: from each element from the second part of array you subtract ary[1], then ary[2] and so on until some ary[a] is bigger that what remains from ary[b].
This can be optimized the following way. Calculate the cumulative sums of the first half of array: let sum[1]=ary[1], sum[2]=sum[1]+ary[2], sum[3]=sum[2]+ary[3], and so on. This can be done in O(N).
Now for each b you need to find such a_last that sum[a_last]<ary[b], but sum[a_last+1]>=ary[b] -- this will mean that from ary[b] you will subtract ary[1]+...+ary[a_last]=sum[a_last]. You can find such a_last using binary search in O(log N), thus making the overall algorithm O(N log N).
The pseudocode:
sum[0] = 0
for a = 1..5
sum[a] = sum[a-1] + ary[a]
for b = 6..N
a_last = maximal a such that sum[a]<ary[b] // use binary search
ary[b] -= sum[a_last]
It seems to me that Petr's solution doesn't follow the specification of the problem: we should subtract 1 (one) for each "hit", not the VALUE of ary[a].
Here's an alternative:
place the testvalues ary[j], j=0..N-1 in a SORTED array: sortedSmallAry[N].
Then run over all b, i.e. b=N, ..., Total-1, and for each ary[b]:
run j=0..N-1 over the sortedSmallAry, test whether sortedSmallAry[j] is smaller than ary[b], count the hits, exit as soon as it fails (because it's sorted). If N is relatively large you can use a binary search in sortedSmallAray to determine how many of its elements satisfy the condition.
Subtract the hitcount from ary[b].

Selecting an element from an array 35% more often

edited
I have a list with N elements, where K elements are "special" and the rest are "normal". What I'm trying to do is pick an element at random, but special elements should be selected 35% more often than normal items.
For example:
var myList = [
{id: 1, special: 0},
{id: 2, special: 1} // <= special item
];
After 600 selections, the normal element should be selected 250 times, and the second should be selected 35% more times than that, or 350 times.
This is different from the suggested duplicate question because my weights do not add up to 1. I can have any arbitrary of elements in my list, and zero or more of them are special. The weight is always 1.35 for special items, and 1.0 for normal items.
Your question is ambiguous with regards to the "35% more often" part. (1) Does it mean that special values as a whole are chosen 35% more than normal values as a whole? (2) Or does it mean that special values are simply weighted 1.35 and normal values are weighted 1?
The two question variants I have described have different answers.
Answer (1)
Note you must always have at least one special and at least one normal value.
We know that every time you sample a value it is either Special or Normal, but not both:
P(Special) + P(Normal) = 1
We know that the likelihood of Special is 35% larger than the likelihood of Normal:
P(Special) = 1.35 * P(Normal)
This is a system of two linear equations with two unknowns. Here is its solution:
P(Normal) = 20 / 47
P(Special) = 27 / 47
Simply divide your set of values into two sets, Specials and Normals. Now to sample do the following:
Sample r uniformly from [0, 1].
If r < 20 / 47, then uniformly sample from Normals.
Else, then uniformly sample from Specials.
Answer (2)
Randomly select an item from the list.
If it is special or Math.random() < 1 / 1.35, then you are done.
Else, return to step 1.
You could take an approach where to first determine if you want to to trigger your "higher probability" code, and then pull from your custom list instead.
if ( Math.random() < .35 ) {
element = randChoice(myCustomListOfMoreProbableChoices);
} else {
element = randChoice(myListOfAllChoices);
}
Create an array for indexes to be chosen for your main array then randomly choose an element from that array to select from you main array:
var mainArray = [1, 2, 3];
var indexesArray = [0, 0, 0, 1, 2]; //1 has 3/5 chance of being chosen while 2 and 3 only have 1/5
Randomly select an index from indexesArray using a random number then:
mainArray[randomFromIndexArray];

board game win situation - searching algorithm

I'm looking for possibly efficient algorithm to detect "win" situation in a gomoku (five-in-a-row) game, played on a 19x19 board. Win situation happens when one of the players manages to get five and NO MORE than five "stones" in a row (horizontal, diagonal or vertical).
I have the following data easily accessible:
previous moves ("stones") of both players stored in a 2d array (can be also json notation object), with variables "B" and "W" to difference players from each other,
"coordinates" of the incoming move (move.x, move.y),
number of moves each player did
I'm doing it in javascript, but any solution that doesn't use low-level stuff like memory allocation nor higher-level (python) array operations would be good.
I've found similiar question ( Detect winning game in nought and crosses ), but solutions given there only refer to small boards (5x5 etc).
A simple to understand solution without excessive loops (only pseudocode provided, let me know if you need more explanation):
I assume your 2-d array runs like this:
board = [
[...],
[...],
[...],
...
];
I.e. the inner arrays represent the horizontal rows of the board.
I also assume that the array is populated by "b", "w", and "x", representing black pieces, white pieces, and empty squares, respectively.
My solution is somewhat divide-and-conquer, so I've divided it into the 3 cases below. Bear with me, it may seem more complex than simply running multiple nested loops at first, but the concept is easy to understand, read, and with the right approach, quite simple to code.
Horizontal lines
Let's first consider the case of detecting a win situation ONLY if the line is horizontal - this is the easiest. First, join a row into a single string, using something like board[0].join(""). Do this for each row. You end up with an array like this:
rows = [
"bxwwwbx...",
"xxxwbxx...",
"wwbbbbx...",
...
]
Now join THIS array, but inserting an "x" between elements to separate each row: rows.join("x").
Now you have one long string representing your board, and it's simply a matter of applying a regexp to find consecutive "w" or "b" of exactly 5 length: superString.test(/(b{5,5})|(w{5,5})/). If the test returns true you have a win situation. If not, let's move on to vertical lines.
Vertical lines
You want to reuse the above code, so create a function testRows for it. Testing for vertical lines is exactly the same process, but you want to transpose the board, so that rows become columns and columns become rows. Then you apply the same testRows function. Transposing can be done by copying values into a new 2-d array, or by writing a simple getCol function and using that within testRows.
Diagonal lines
Again, we want to reuse the `testRows' function. A diagonal such as this:
b x x x x
x b x x x
x x b x x
x x x b x
x x x x b
Can be converted to a vertical such as this:
b x x x x
b x x x
b x x
b x
b
By shifting row i by i positions. Now it's a matter of transposing and we are back at testing for horizontals. You'll need to do the same for diagonals that go the other way, but this time shift row i by length - 1 - i positions, or in your case, 18 - i positions.
Functional javascript
As a side note, my solution fits nicely with functional programming, which means that it can be quite easily coded if you have functional programming tools with you, though it's not necessary. I recommend using underscore.js as it's quite likely you'll need basic tools like map, reduce and filter in many different game algorithms. For example, my section on testing horizontal lines can be written in one line of javascript with the use of map:
_(board).map(function (row) {return row.join("")}).join("x").test(/(b{5,5})|(w{5,5})/);
Even though this is a really old question I want to provide my answer because I took a deeper look into this problem today and solved it in a much (much) more efficient way.
I'm using a bit board, which is used in most of the board games and engines (chess engines) due to the efficiency, to represent my field.
You can do everything you need in this game with bitwise operations.
A bit can just have 2 states (0 and 1) however what we need are 3 states e.g. p1, p2 or empty.
To solve this problem we're going to have 2 boards instead, one for each player.
Another problem is that Gomoku has a lot of fields (19x19) and there is no number type that has that many bits to represent the field.
We will use an array of numbers to represent each line and just use the first lsb 15bits of it.
Vertical rows
A simplified board of player 1 could look like this
000000
101100
001000
011000
000000
Lets say we want to detect 3 in a row. We take the first 3 rows(0-2) and took at them.
000000
001100
101000
With the & (AND) operator you can check if there is a 1 in every row.
var result = field[player][0] & field[player][1] & field[player][2];
In this case the result will be 0 which means no winner. Lets continue... The next step is to take rows 1-3
101100
001000
011000
Apply the AND again and that we will get is 001000. We don't have to care what number this is, just if it's 0 or not. (result != 0)
Horizontal rows
Ok now we can detect vertical rows. To detect the horizontal rows we need to save another 2 boards, again one for each player. But we need to invert x and y axis. Then we can do the same check again to detect horizontal lines. Your array would then be:
//[player][hORv][rows]
var field[2][2][19];
Diagonals :)
The trickiest part are the diagonals of course but with a simple trick you can do the same check as above. A simple board:
000000
010000
001000
000100
000000
Basically we do the same as above but before we do that we need to shift the rows. Lets say we're at row 1-3.
010000
001000
000100
The first row stays as it is. Then you shift the second row one to the left and the third 2 to the left.
var r0 = field[0][0][i];
var r1 = field[0][0][i+1] << 1;
var r2 = field[0][0][i+2] << 2;
What you will get is:
010000
010000
010000
Apply AND you can have your win detection. To get the other diagonal direction just do it again, but instead of shifting to the left <<, shift to the right >>
I hopes this helps someone.
untested:
int left = max(0, move.x-5), right = min(width-1, move.x+5), top = max(0, move.y-5), bottom = min(width-1, move.y+5);
// check the primary diagonal (top-left to bottom-right)
for (int x = left, y = top; x <= right && y <= bottom; x++, y++) {
for (int count = 0; x <= right && y <= bottom && stones[x][y] == lastPlayer; x++, y++, count++) {
if (count >= 5) return true;
}
}
// check the secondary diagonal (top-right to bottom-left)
// ...
// check the horizontal
// ...
// check the vertical
// ...
return false;
alternatively, if you don't like the nested loops (untested):
// check the primary diagonal (top-left to bottom-right)
int count = 0, maxCount = 0;
for (int x = left, y = top; x <= right && y <= bottom; x++, y++) {
if (count < 5) {
count = stones[x][y] == lastPlayer ? count + 1 : 0;
} else {
return true;
}
}

Categories

Resources