JS inverting probabilities - javascript

I'm generating a grid of objects, each of which has one of 3 colors. Assume I'm filling a particular grid cell. I know that around this cell there are, for example, 1 object of color 0, 1 of color 1 and 2 of color 2, so:
const surroundings = { 0: 1, 1: 1, 2: 2 }
Now I want to assign a color to the current cell I'm working with. I need to count probabilities for each color to be used. To make it look pleasing, I want it more likely to be different from its surroundings. So in the case above the probabilities could be { 0: 0.4, 1: 0.4, 2: 0.2 }.
I'm sure there is an implementation of such operation in the probability theory, but I'm having trouble finding the correct term for it. In the example I gave the probabilities could be different, because I have no idea how to calculate them. But for colors 0 and 1 they should certainly be equal and for color 2 it should be the smallest.

You could get the reciprocal and the sum and return the part of it.
function getPro(array) {
var inv = array.map(v => 1 / v),
sum = inv.reduce((a, b) => a + b);
return inv.map(v => v / sum);
}
console.log(getPro([1, 1, 2]));
console.log(getPro([1, 1, 1]));
console.log(getPro([2, 4, 6]));

I would use the sum of surrounding frequency (usually 4, except on grid boundaries) to get the inverse:
let surrounding = [1, 2, 1];
let sum = surrounding[0] + surrounding[1] + surrounding[2];
let inverted = [sum-surrounding[0], sum-surrounding[1], sum-surrounding[2]];
console.log(inverted);
// Pick a random color according to inverted probabilities:
let invSum = 2*sum;
let rnd = Math.floor(Math.random() * invSum);
let color = rnd < inverted[0] ? 0
: rnd < inverted[0] + inverted[1] ? 1
: 2;
console.log(color);
This will also work when a particular color does not occur at all in the surroundings, for example with surrounding = { 0: 0, 1: 2, 2: 2 }

Related

Find the maximum product that can be formed by taking any one element from each sub-array

I am trying to write an efficient algorithm in JavaScript to solve this task. Please see the next examples of input data and correct results:
Array: [ [-3,-4], [1,2,-3] ] Result: (-4)*(-3) = 12
Array: [ [1,-1], [2,3], [10,-100,20] ] Result: (-1)*3*(-100) = 300
Array: [ [-3,-15], [-3,-7], [-5,1,-2,-7] ] Result: (-15)*(-7)*1 = 105
It can be any number of sub-arrays and any number of elements in each sub-array. What I already found is that I probably should leave only min and max values in the each sub-array, I did it using .map(a => [Math.min(...a), Math.max(...a)]) and sort them using .sort((a, b) => a[0] - b[0]).
And now I am stuck. Probably there is a way to calculate all possible products but I am sure that it's not an effective way to solve this task.
Please help!
The problem you post can be solved with a simple algorithm. We just need to keep tracking the maximum/minimum when iterating over each sub-array. We can keep finding the next maximum/minimum by multiplying the current maximum/minimum with the max/min value in each sub-array. We pick the maximum when the iterating is over. Its time complexity is O(n) where n is total number of elements in an array (i.e. sum of number of elements in each sub-array).
Here's the complete code. find_maximum_product function keeps tracking the minimum/maximum and returns the maximum eventually, and it also keeps tracking the multipliers and return it:
/**
* arr: array of any number of sub-arrays and
* any number of elements in each sub-array.
* e.g. [[1, -1], [2, 3], [10, -100, 20]]
*/
function find_maximum_product(arr) {
let max = 1;
let min = 1;
let max_multipliers = [];
let min_multipliers = [];
for (let i = 0; i < arr.length; i++) {
const a = Math.max(...arr[i]);
const b = Math.min(...arr[i]);
const candidates = [max * a, max * b, min * a, min * b];
max = Math.max(...candidates);
min = Math.min(...candidates);
let new_max_multipliers;
let new_min_multipliers;
switch (max) {
case candidates[0]:
new_max_multipliers = max_multipliers.concat(a);
break;
case candidates[1]:
new_max_multipliers = max_multipliers.concat(b);
break;
case candidates[2]:
new_max_multipliers = min_multipliers.concat(a);
break;
case candidates[3]:
new_max_multipliers = min_multipliers.concat(b);
break;
}
switch (min) {
case candidates[0]:
new_min_multipliers = max_multipliers.concat(a);
break;
case candidates[1]:
new_min_multipliers = max_multipliers.concat(b);
break;
case candidates[2]:
new_min_multipliers = min_multipliers.concat(a);
break;
case candidates[3]:
new_min_multipliers = min_multipliers.concat(b);
break;
}
max_multipliers = new_max_multipliers;
min_multipliers = new_min_multipliers;
}
if (max >= min) {
return [max, max_multipliers];
}
return [min, min_multipliers];
}
const arrays = [
[
[-3, -4],
[1, 2, -3],
],
[
[1, -1],
[2, 3],
[10, -100, 20],
],
[
[-3, -15],
[-3, -7],
[-5, 1, -2, -7],
],
[
[14, 2],
[0, -16],
[-12, -16],
],
[
[-20, -4, -19, -18],
[0, -15, -10],
[-13, 4],
],
[
[-2, -15, -12, -8, -16],
[-4, -15, -7],
[-10, -5],
],
];
for (let i = 0; i < arrays.length; i++) {
const [max, max_multipliers] = find_maximum_product(arrays[i]);
console.log('Array:', JSON.stringify(arrays[i]));
console.log('Result:', `${max_multipliers.join(' * ')} = ${max}`);
console.log('');
}
UPDATE
Simpler version for just getting the maximum, not getting the multipliers:
/**
* arr: array of any number of sub-arrays and
* any number of elements in each sub-array.
* e.g. [[1, -1], [2, 3], [10, -100, 20]]
*/
function get_maximum_product(arr) {
return arr
.map((a) => [Math.min(...a), Math.max(...a)])
.reduce(
(acc, current) => {
const candidates = [
acc[0] * current[0],
acc[0] * current[1],
acc[1] * current[0],
acc[1] * current[1],
];
return [Math.min(...candidates), Math.max(...candidates)];
},
[1, 1]
)[1];
}
Here is a top-down recurrence that could be adapted to bottom-up (a loop) and utilises O(n) search space.
Until I can complete it, the reader is encouraged to add a third return value in the tuple, largest_non_positive for that special case.
// Returns [highest positive, lowest negative]
// Does not address highest non-positive
function f(A, i){
const high = Math.max(...A[i]);
const low = Math.min(...A[i]);
if (i == 0){
if (low < 0 && high >= 0)
return [high, low];
if (low <= 0 && high <= 0)
return [-Infinity, low];
if (low >= 0 && high >= 0)
return [high, Infinity];
}
const [pos, neg] = f(A, i - 1);
function maybeZero(prod){
return isNaN(prod) ? 0 : prod;
}
let hp = maybeZero(high * pos);
let hn = maybeZero(high * neg);
let ln = maybeZero(low * neg);
let lp = maybeZero(low * pos);
if (low < 0 && high >= 0)
return [Math.max(hp, ln), Math.min(hn, lp)];
if (low <= 0 && high <= 0)
return [ln, lp];
if (low >= 0 && high >= 0)
return [hp, hn];
}
var As = [
[[-3,-4], [1,2,-3]],
[[1,-1], [2,3], [10,-100,20]],
[[-3,-15], [-3,-7], [-5,1,-2,-7]],
[[-11,-6], [-20,-20], [18,-4], [-20,1]],
[[-1000,1], [-1,1], [-1,1], [-1,1]],
[[14,2], [0,-16], [-12,-16]],
[[-20, -4, -19, -18], [0, -15, -10],[-13, 4]]
];
for (let A of As){
console.log(JSON.stringify(A));
console.log(f(A, A.length - 1)[0]);
console.log('');
}
Sort values in arrays by their absolute value in descending order
Check the product of first array elements if its positive its the answer
Otherwise lets call product p and we know p < 0, so if we change some positive element to some negative element or vice verse we will improve answer
we can simply check all possible elements to change, for each array a element x we can check if p / a[0] * x is better than current result if it is we update our answer
*Special case: all elements in arrays are negative and we have odd number of arrays, then we simply sort in increasing order
Complexity: O(n log n) where n is total amount of elements across all arrays
Take the product of the highest number of all the arrays that have at least one positive number.
If there's an odd number of remaining arrays (with only negatives), find the one with the highest (closest to zero) negative number, and set its absolute aside.
Take the arrays that remain after step 2, take the product of their lowest number (furthest from zero), and multiply it by the products from step 1 and (if any) step 2.
(also, avoid 0 if it would be the chosen number)
The first thing to notice is that there are only two specific cases where it's not possible to get a positive product. So I think an algorithm should first check if those specific cases are happening, then call a different subalgorithm for each of the three possible situations:
it is possible to get a positive product, so we want to find the highest positive product;
one of the arrays is full of zeroes, so all products are zero;
it is impossible to get a positive product, because no array has both positive and negative numbers, and there is an odd number of arrays with only negative numbers, so we want to find the closest to zero negative product.
The second and third cases lead to trivial algorithms.
Let's consider the first case.
For every array, the only numbers that can be useful in the highest product are the highest positive number, and the lowest negative number. If an array only has positive numbers or only has negative numbers, then there is only one useful number in that array, which can be chosen immediately.
For all the remaining arrays, you have to choose whether to use the positive or the negative number. Ideally, you want to use the one with the highest absolute value; but if you do that for every array, then the result might be negative.
This leads to a linear algorithm:
For all of the remaining arrays, initially select the number with the highest absolute value
If the resulting product is positive, you're done.
If the resulting product is negative, then a compromise has to be done in one of the arrays. For every array, compute the "cost" of this compromise (equal to the difference between the absolute values of the two interesting numbers in that array, multiplied by the product of all the other selected numbers).
Finally, choose the array whose cost is the lowest, and change the chosen number in that array.
Here is an example execution of the algorithm on the list of arrays [[18,19,20,-23], [12,-10,9,8],[-10,-3],[5,3],[-10,-5]].
Here we notice that it is possible to find a positive solution, because at least one of the arrays contains both negative and positive numbers.
For the last three arrays we have no choice between positive and negative: so we can already choose -10, 5 and -10 as the three numbers for these three arrays. For the first array, we'll have to choose between 20 and -23; and for the second array we'll have to choose between 12 and -10.
So the final product will be: (20 or -23) * (12 and -10) * (-10) * 5 * (-10).
Ideally, we would prefer 23 to 20, and 12 to 10. That would result in:
(-23) * 12 * (-10) * 5 * (-10)
Unfortunately, this is negative. So the question is: do we replace -23 with 20, or 12 with -10?
The cost of replacing -23 with 20 would be (23-20) * 11 * (10*5*10) = 33 * (10*5*10).
The cost of replacing 12 with -10 would be (12-10) * 21 * (10*5*10) = 42 * (10*5*10).
Finally, we choose to replace -23 with 20, because that is the less costly compromise.
The final product is 20 * 12 * (-10) * 5 * (-10).

LeetCode 120: Triangle - Minimum path sum

I'm doing this problem on leetcode:
Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.
My logic is to find the minimum number in each array and add that to the sum.
This is my code in javascript:
var minimumTotal = function(triangle) {
let sum = 0;
for (let i = 0; i < triangle.length; i++) {
sum += Math.min.apply(null, triangle[i])
}
return sum;
};
But it doesn't work for this test case: [[-1],[2,3],[1,-1,-3]].
The expected output is -1. I'm confused how it should equal -1, because -1 + 2 = 1 and none of the numbers in third array equal -1 when summed with 1.
I looked at the discussion answers and they all used some sort of dynamic programming solution.
What am I doing wrong?
There is a path where the output should be -1.
[
[ -1 ],
[ 2, 3 ],
[ 1, -1, -3 ],
]
-1 + 3 - 3 = -1
The problem is that you only have 2 possible branches at each fork, whereas it appears you're taking the lowest number from the entire row.
Under the rules of the challenge, you shouldn't be able to go from 2 in the second row to -3 in the third row, which would be the most efficient path under your approach.
What you are doing does not seem to follow the desired data structure of the problem. You are generating an int, while the desired output should be an array.
The correct answers are already posted in the discussion board:
This solution is an accepted one (just tested it):
JavaScript
var minimumTotal = function(triangle) {
for (let row = triangle.length - 2; row > -1; row--)
for (let col = 0; col < triangle[row].length; col++)
triangle[row][col] += Math.min(triangle[-~row][col], triangle[-~row][-~col])
return triangle[0][0]
}
-~row is the same as row + 1 (bitwise version).
Reference
Explains it here
If you might be interested in Python and Java, these are "accepted" solutions:
Python
class Solution:
def minimumTotal(self, triangle):
if not triangle:
return
dp = triangle[-1]
for row in range(len(triangle) - 2, -1, -1):
for col in range(len(triangle[row])):
dp[col] = min(dp[col], dp[-~col]) + triangle[row][col]
return dp[0]
Java
class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
int[] dp = new int[-~triangle.size()];
for (int row = triangle.size() - 1; row > -1; row--)
for (int col = 0; col < triangle.get(row).size(); col++)
dp[col] = Math.min(dp[col], dp[-~col]) + triangle.get(row).get(col);
return dp[0];
}
}
For each step, you may move to an adjacent number of the row below. More formally, if you are on index i on the current row, you may move to either index i or index i + 1 on the next row.
The above statement is part of the question and it helps to create a graph like this.
Dynamic programming is used where we have problems, which can be divided into similar sub-problems, so that their results can be re-used. We start from the bottom and determine which minimum value we take and then we are going to be using that minimum value above. That is why we use dynamic programming here. Now each row gets 1 size bigger, so you can imagine under the last row, we have 4 0's.
that is why in dynamic programming we always create an array whose size is always 1 greater than the original array. We fill the array with default values. I will be explaining in python but later on I will challenge myself to write in javascript. first, initialize the dp array
dp=[0]*(len(triangle)+1) #[[0],[0],[0],[0]]
Now, we are starting from the level=[1,-1,3]. For this level, since the bottom is full of 0's, our dp array will be
dp=[1,-1,-3,0]
now we are moving above level, level=[2,3], but we are using the data from the bottom. (That is why we are using dynamic programming). From 2, its index is 0, so I ask myself what is the minimum between 0'th and 1'st value of the dp array. Whichever is minimum we add it to 2? Obviously, -1 is minimum, 2+(-1)=1 and dp gets updated.
dp=[1, -1, -3, 0]
We do the same for 3. its index is 1, and we ask what is the min(-1,3), because 3 is pointing to -1 and 3 and then we add -1 to 3 which is 2. new dp
dp=[1,0,-3,0]
Now we are at the root level. -1 and its index is 0. We ask what is min value of index 0'th and index 1'st of the dp array. min(1,0)=0 and we add it to -1. dp gets updated
dp=[-1,0,3,0]
and finally, we return dp[0]=0
Here is the code in python:
from typing import List
class Solution:
def min_total(self,triangle:List[List[int]])->int:
# dp[] is the bottom row
dp=[0]*(len(triangle)+1)
// triangle[::-1] says start from the last element of triangle
for row in triangle[::-1]:
for i,n in enumerate(row):
dp[i]=n+min(dp[i],dp[i+1])
return dp[0]
Here is javascript code:
function minPath(triangle) {
let dp = new Array(triangle.length + 1).fill(0);
for (let row = triangle.length - 1; row >= 0; row--) {
for (let i = 0; i <= triangle[row].length - 1; i++) {
dp[i] = triangle[row][i] + Math.min(dp[i], dp[i + 1]);
}
}
console.log(dp);
return dp[0];
}
const triangle = [[-1], [2, 3], [1, -1, -3]];
console.log(minPath(triangle));
As this was brought back up, it's worth pointing out that the dynamic programming technique discussed in answers and comments can be done very simply with a reduceRight:
const minSum = (triangle) =>
triangle .reduceRight ((ms, ns) => ns .map (
(n, i) => n + (i > ms.length ? ms[i] : Math .min (ms [i], ms [i + 1]))
)) [0]
console .log (minSum ([[-1], [2, 3], [1, -1, -3]])) //=> -1
console .log (minSum ([[2], [3, 4], [6, 5, 7], [4, 1, 8, 3]])) //=> 11
console .log (minSum ([[-10]])) //=> -10
At each step we calculate the minimum paths through all the elements in a row. ms is the minimum paths through the row below (and on the initial pass will just be the bottom row) and for each n in our row ns, if we're at the rightmost element, we just copy the value from the corresponding row below, otherwise we take the minimum of the element right below and the one to its right.
Since this is called a triangle, I think we can assume that the first row has only one element. But if that's not the case, we could simply take the minimum of the results rather than taking the first element in the final value. That is, we could replace triangle .reduceRight ( ... ) [0] with Math .min (... triangle .reduceRight ( ... )).
/*
[120] Triangle
https://leetcode.com/problems/triangle/description/
*/
import java.util.List;
class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
if (triangle.size() == 0)
return 0;
int rows = triangle.size();
int cols = triangle.get(rows - 1).size();
int[] tmp = new int[cols];
int index = 0;
for (int var : triangle.get(rows - 1)) {
tmp[index++] = var;
}
for (int i = rows - 2; i >= 0; i--) {
for (int j = 0; j <= triangle.get(i).size() - 1; j++) {
tmp[j] = triangle.get(i).get(j) + Math.min(tmp[j], tmp[j + 1]);
}
}
return tmp[0];
}
}

Finding all possible combined (plus and minus) sums of n arguments?

I'm trying to build a function that takes a variable number of arguments.
The function takes n inputs and calculates all possible sums of addition and subtraction e.g. if the args are 1,2,3
1 + 2 + 3
1 - 2 - 3
1 + 2 - 3
1 - 2 + 3
Finally, the function outputs the sum that is closest to zero. In this case, that answer would just be 0.
I'm having a lot of problems figuring out how to loop n arguments to use all possible combinations of the + and - operators.
I've managed to build a function that either adds all or subtracts all variables, but I'm stuck on how to approach the various +'s and -'s, especially when considering multiple possible variables.
var sub = 0;
var add = 0;
function sumAll() {
var i;
for (i = 0; i < arguments.length; i++) {
sub -= arguments[i];
}
for (i = 0; i < arguments.length; i++) {
add += arguments[i];
}
return add;
return sub;
};
console.log(add, sub); // just to test the outputs
I'd like to calculate all possible arrangements of + and - for any given number of inputs (always integers, both positive and negative). Suggestions on comparing sums to zero are welcome, though I haven't attempted it yet and would rather try before asking on that part. Thanks.
I'd iterate through the possible bits of a number. Eg, if there are 3 arguments, then there are 3 bits, and the highest number representable by those bits is 2 ** 3 - 1, or 7 (when all 3 bits are set, 111, or 1+2+4). Then, iterate from 0 to 7 and check whether each bit index is set or not.
Eg, on the first iteration, when the number is 0, the bits are 000, which corresponds to +++ - add all 3 arguments up.
On the second iteration, when the number is 1, the bits are 001, which corresponds to -++, so subtract the first argument, and add the other two arguments.
The third iteration would have 2, or 010, or +-+.
The third iteration would have 3, or 011, or +--.
The third iteration would have 4, or 100, or -++.
Continue the pattern until the end, while keeping track of the total closest to zero so far.
You can also return immediately if a subtotal of 0 is found, if you want.
const sumAll = (...args) => {
const limit = 2 ** args.length - 1; // eg, 2 ** 3 - 1 = 7
let totalClosestToZeroSoFar = Infinity;
for (let i = 0; i < limit; i++) {
// eg '000', or '001', or '010', or '011', or '100', etc
const bitStr = i.toString(2).padStart(args.length, '0');
let subtotal = 0;
console.log('i:', i, 'bitStr:', bitStr);
args.forEach((arg, bitPos) => {
if (bitStr[args.length - 1 - bitPos] === '0') {
console.log('+', arg);
subtotal += arg;
} else {
console.log('-', arg);
subtotal -= arg;
}
});
console.log('subtotal', subtotal);
if (Math.abs(subtotal) < Math.abs(totalClosestToZeroSoFar)) {
totalClosestToZeroSoFar = subtotal;
}
}
return totalClosestToZeroSoFar;
};
console.log('final', sumAll(1, 2, 3));
You can "simplify" by replacing the [args.length - 1 - bitPos] with [bitPos] for the same result, but it'll look a bit more confusing - eg 3 (011, or +--), would become 110 (--+).
It's a lot shorter without all the logs that demonstrate that the code is working as desired:
const sumAll = (...args) => {
const limit = 2 ** args.length - 1;
let totalClosestToZeroSoFar = Infinity;
for (let i = 0; i < limit; i++) {
const bitStr = i.toString(2).padStart(args.length, '0');
let subtotal = 0;
args.forEach((arg, bitPos) => {
subtotal += (bitStr[bitPos] === '0' ? -1 : 1) * arg;
});
if (Math.abs(subtotal) < Math.abs(totalClosestToZeroSoFar)) {
totalClosestToZeroSoFar = subtotal;
}
}
return totalClosestToZeroSoFar;
};
console.log('final', sumAll(1, 2, 3));
You can cut the number of operations in half by arbitrarily choosing a sign for the first digit. Eg. currently, with sumAll(9, 1), both an answer of 8 (9 - 1) and -8 (1 - 9) would be valid, because they're both equally close to 0. No matter the input, if +- produces a number closest to 0, then -+ does as well, only with the opposite sign. Similarly, if ++--- produces a number closest to 0, then --+++ does as well, with the opposite sign. By choosing a sign for the first digit, you might be forcing the calculated result to have just one sign, but that won't affect the algorithm's result's distance from 0.
It's not much of an improvement (eg, 10 arguments, 2 ** 10 - 1 -> 1023 iterations improves to 2 ** 9 - 1 -> 511 iterations), but it's something.
const sumAll = (...args) => {
let initialDigit = args.shift();
const limit = 2 ** args.length - 1;
let totalClosestToZeroSoFar = Infinity;
for (let i = 0; i < limit; i++) {
const bitStr = i.toString(2).padStart(args.length, '0');
let subtotal = initialDigit;
args.forEach((arg, bitPos) => {
subtotal += (bitStr[bitPos] === '0' ? -1 : 1) * arg;
});
if (Math.abs(subtotal) < Math.abs(totalClosestToZeroSoFar)) {
totalClosestToZeroSoFar = subtotal;
}
}
return totalClosestToZeroSoFar;
};
console.log('final', sumAll(1, 2, 3));
The variable argument requirement is unrelated to the algorithm, which seems to be the meat of the question. You can use the spread syntax instead of arguments if you wish.
As for the algorithm, if the parameter numbers can be positive or negative, a good place to start is a naive brute force O(2n) algorithm. For each possible operation location, we recurse on adding a plus sign at that location and recurse separately on adding a minus sign. On the way back up the call tree, pick whichever choice ultimately led to an equation that was closest to zero.
Here's the code:
const closeToZero = (...nums) =>
(function addExpr(nums, total, i=1) {
if (i < nums.length) {
const add = addExpr(nums, total + nums[i], i + 1);
const sub = addExpr(nums, total - nums[i], i + 1);
return Math.abs(add) < Math.abs(sub) ? add : sub;
}
return total;
})(nums, nums[0])
;
console.log(closeToZero(1, 17, 6, 10, 15)); // 1 - 17 - 6 + 10 + 15
Now, the question is whether this is performing extra work. Can we find overlapping subproblems? If so, we can memoize previous answers and look them up in a table. The problem is, in part, the negative numbers: it's not obvious how to determine if we're getting closer or further from the target based on a subproblem we've already solved for a given chunk of the array.
I'll leave this as an exercise for the reader and ponder it myself, but it seems likely that there's room for optimization. Here's a related question that might offer some insight in the meantime.
This is also known as a variation of the partition problem, whereby we are looking for a minimal difference between the two parts we have divided the arguments into (e.g., the difference between [1,2] and [3] is zero). Here's one way to enumerate all the differences we can create and pick the smallest:
function f(){
let diffs = new Set([Math.abs(arguments[0])])
for (let i=1; i<arguments.length; i++){
const diffs2 = new Set
for (let d of Array.from(diffs)){
diffs2.add(Math.abs(d + arguments[i]))
diffs2.add(Math.abs(d - arguments[i]))
}
diffs = diffs2
}
return Math.min(...Array.from(diffs))
}
console.log(f(5,3))
console.log(f(1,2,3))
console.log(f(1,2,3,5))
I like to join in on this riddle :)
the issue can be described as fn = fn - 1 + an * xn , where x is of X and a0,...,an is of {-1, 1}
For a single case: X * A = y
For all cases X (*) TA = Y , TA = [An!,...,A0]
Now we have n! different A
//consider n < 32
// name mapping TA: SIGN_STATE_GENERATOR, Y: RESULT_VECTOR, X: INPUT
const INPUT = [1,2,3,3,3,1]
const SIGN_STATE_GENERATOR = (function*(n){
if(n >= 32) throw Error("Its working on UInt32 - max length is 32 in this implementation")
let uint32State = -1 >>> 32-n;
while(uint32State){
yield uint32State--;
}
})(INPUT.length)
const RESULT_VECTOR = []
let SIGN_STATE = SIGN_STATE_GENERATOR.next().value
while (SIGN_STATE){
RESULT_VECTOR.push(
INPUT.reduce(
(a,b, index) =>
a + ((SIGN_STATE >> index) & 1 ? 1 : -1) * b,
0
)
)
SIGN_STATE = SIGN_STATE_GENERATOR.next().value
}
console.log(RESULT_VECTOR)
I spent time working on the ability so apply signs between each item in an array. This feels like the most natural approach to me.
const input1 = [1, 2, 3]
const input2 = [1, 2, 3, -4]
const input3 = [-3, 6, 0, -5, 9]
const input4 = [1, 17, 6, 10, 15]
const makeMatrix = (input, row = [{ sign: 1, number: input[0] }]) => {
if(row.length === input.length) return [ row ]
const number = input[row.length]
return [
...makeMatrix(input, row.concat({ sign: 1, number })),
...makeMatrix(input, row.concat({ sign: -1, number }))
]
}
const checkMatrix = matrix => matrix.reduce((best, row) => {
const current = {
calculation: row.map((item, i) => `${i > 0 ? item.sign === -1 ? "-" : "+" : ""}(${item.number})`).join(""),
value: row.reduce((sum, item) => sum += (item.number * item.sign), 0)
}
return best.value === undefined || Math.abs(best.value) > Math.abs(current.value) ? current : best
})
const processNumbers = input => {
console.log("Generating matrix for:", JSON.stringify(input))
const matrix = makeMatrix(input)
console.log("Testing the following matrix:", JSON.stringify(matrix))
const winner = checkMatrix(matrix)
console.log("Closest to zero was:", winner)
}
processNumbers(input1)
processNumbers(input2)
processNumbers(input3)
processNumbers(input4)

Selecting from an array based on size

I'm currently working on a small game and got stuck on something I can't figure out.
I have an array with 5 items in, these items represent different positions, like this:
[1, 2, 3, 4, 5]
These are basically different lines that are used to place objects as the players goes further into the game.
Every 5 second new objects should appear, these come from another array, that looks something like this:
[1, 2]
// 1 = single item
// 2 = double item
1 and 2 are different types of objects, both require a different amount of lines.
The problem is that these items sometimes overlap and I can't figure out how to stop this.
You can see the problem from the output:
Items: [1, 2]
[Item of type 1] line: 3
[available lines] [1, 2, 3, 4, 5]
[Item of type 2] line: 2
[available lines] [1, 2, 4, 5]
[1, 5]
So item 1 (1 line) is placed on line 3, but for item 2 (2 lines), line 2 is select, because it's 2 lines wide, this will overlap with my first item.
So basically for item 2 only line 1 and 4 could be selected, but I can't seem to find any logic that would only select these.
It's probably going to be expanded into something around 20 lines with more objects, so I would need to find a way to select a line where the next one is available too.
Any ideas on how I can get started on this?
What I currently use is this:
var random = Math.floor(Math.random() * (start_lines.length - 1));
var line = start_lines[random];
// If it's a barrier, delete 2 rows instead (35)
if(type == 2) {
if(line == 5) {
xPos = this._lines[random - 1] - 35;
start_lines.splice(random - 1, 2);
} else {
xPos = this._lines[random] - 35;
start_lines.splice(random, 2);
}
} else {
start_lines.splice(random, 1);
}
What I did for now was first loop trough the array with lines and check if the next line is available, I then push those 2 lines into another array which I can select from.
if(type == 2) {
var options = [];
for(var i = 0 ; i < start_lines.length; i++) {
var f = start_lines[i];
var n = start_lines[i + 1];
if((f + 1) == n) {
options.push([f, n]);
}
}
var random = Math.floor(Math.random() * (options.length - 1));
var option = options[random];
var f = option[0];
var n = option[1];
xPos = this._lines[f] - 30;
start_lines.splice(f - 1, 2);
start_lines.splice(n - 1, 2);
} else {
var random = Math.floor(Math.random() * (start_lines.length - 1));
var line = start_lines[random];
start_lines.splice(random, 1);
}
I don't know whether this approach fits for your requirement, but it would be better if you can set the lines initially and randomly pick one when needed. This avoids a lot of confusion.
function getLines(){
var start_lines = [1, 2, 3, 4, 5], lines = [];
while(start_lines.length){
var type = Math.ceil(Math.random() * 2);
lines.push(start_lines.splice(0, type));
}
return lines;
}
var lines = getLines();
var random = Math.floor(Math.random() * lines.length);
console.log(lines.splice(random, 1));
Here I am selecting type randomly - either 1 or 2. You will have to make some changes in this.
jsbin : http://jsbin.com/upabom/3/edit

Self adjusting random list

I have a grid of 9 columns by 3 rows (so each column has 3 slots). A minimum of one slot in every column must be marked and no more than 3 can be marked. A mark is represented by a 1,2 or 3 digit.
There must always be 15 marked slots in total. To achieve this I tried the following:
var valueLeft = 15;
while (valueLeft > 0)
{
var ranValue = utils.getRandomInt(1,3);
console.log('slots remaining: ' + (valueLeft - ranValue));
if (ranValue >= valueLeft)
{
ranValue = valueLeft;
}
valueList.push(ranValue);
valueLeft -= ranValue;
}
console.log(valueList);
But this often gives me an array of numbers with less than 9 elements inside. I can see that my logic is flawed but I am pulling my hair out trying to figure out how I can get it to always give a set of 9 elements which all add up to 15.
For example what I might end up with is:
[2, 1, 1, 1, 1, 2, 3, 1, 2, 1]
When what I need for example is:
[2, 2, 1, 1, 1, 1, 3, 2, 2]
or
[2, 2, 1, 2, 1, 1, 3, 2, 1]
and so on.
Any advice appreciated. Feel free to flame me for my poor sense of logic :)
This answer shows a similar approach to many of those already posted, but I feel as though they're making it too complicated. It can be very straightforward:
function pad(list, size) {
var total = list.length;
while (total != size) {
var i = utils.getRandomInt(0, 8);
if (list[i] < 3) {
list[i]++;
total++;
}
}
return list;
}
var valueList = pad(new Array(1,1,1,1,1,1,1,1,1), 15);
You don't need a lot of cases. Just -- as many others have already said -- init the array with 1's. Then, simply add one to random elements (whose value isn't already 3) until the total is 15.
why don't you do this:
start off with an array that looks like this: 1,1,1,1,1,1,1,1,1
then make a function that picks a random number between 0 and 8 6 times.
if the same number has been picked more than twice, skip it and pick a new one
then correlate those 6 numbers to the index of the array and add 1 for each time it picks that number.
var i; var valueList = new Array(1,1,1,1,1,1,1,1,1);
for(i=0;i<6;i++)
{
var ranNum = utils.getRandomInt(0,8);
if(valueList[ranNum]<3) valueList[ranNum]+=1;
else i--;
}
just tested it, changed <=6 to <6 and it's working for me. Good luck!
Following logic should work. You should select a random value (within 1-3) such that choosing that would not lead us to not able to select a random value for further slots.
var gridLeft = 9
var valueLeft = 15
while(gridLeft>0) {
var ranValue
while(true) {
ranValue = utils.getRandomInt(1,3);
if (valueLeft-ranValue > 3*(gridLeft-1))
continue;
else if (valueLeft-ranValue < 1*(gridLeft-1))
continue;
else
break;
}
valueList.push(ranValue);
valueLeft -= ranValue;
gridLeft -= 1
}

Categories

Resources