Javascript circular array find next element - javascript

I have a array of n elements. I am at a given position say x and I need to move to position y, I need to find out what is the difference or number of steps if I traverse array by going forward and by going backward.
const foo = Array.from(Array(15).keys());
const length = foo.length;
const currentItem = 2;
const next = 12;
let diff = currentItem - next;
if (diff < 0) {
if (next > currentItem) {
diff = next - currentItem;
console.log((diff));
} else {
diff = length - Math.abs(diff);
console.log(-diff);
}
} else {
if (next < currentItem) {
console.log(-(diff));
} else {
console.log(diff);
}
}
I am trying to fin in above code if I need to move forward or backward. In above example I expect answer as -6 but I get answer 10. I am getting bit confused in the loops. Any better and smarter way to do this?

Here is how I'd do it:
// The % operator in JavaScript is the remainder operator.
// The following function defines the modulo operator.
const mod = (x, y) => (x % y + y) % y;
// Law: mod(start + shortestPath(start, end, length), length) === end
const shortestPath = (start, end, length) => {
const forward = mod(end - start, length);
const backward = mod(start - end, length);
return forward > backward ? -backward : forward;
};
const shortestPathLaw = (start, end, length) =>
mod(start + shortestPath(start, end, length), length) === end;
console.assert(shortestPathLaw(4, 6, 10));
console.assert(shortestPathLaw(8, 6, 10));
console.log(shortestPath(4, 6, 10)); // 2
console.log(shortestPath(8, 6, 10)); // -2
We use the modulo operator to calculate the forward distance from point A to point B (i.e. B - A). The backward distance from point A to point B is the same as the forward distance from point B to point A (i.e. A - B). Then we pick the shorter of the two distances, and if the distance we picked is the backward distance, then we negate it to show that we're traveling backward from A to reach B.

Related

Which algorithm should be used to find the point?

You need to find some unknown, predetermined point in three-dimensional space, in the smallest number of attempts, using only a function that can return the distance from
any point you pass to it to the desired unknown point.
To solve the problem, first implement a function f that, by taking the coordinates of any point s(x, y, z), return the distance between that point and a conditionally unknown, randomly generated point
point you arbitrarily generate r(x, y, z), where x, y, z can be integers between
0 и 100.
For example, for an arbitrarily generated point r(0, 0, 10) and a point passed to the function
s(0, 0, 0), the result of the function would be as follows:
f(s) = 10 // the distance between s(0, 0, 0) and r(0, 0, 10) is 10
Next, implement the algorithm itself for the assignment. The algorithm should find the coordinates of
of an arbitrarily generated point with the least number of calls to the function f.
I have a randomizer instead of an algorithm, that's all I got. Help.
const pointToFound = {
x: 12,
y: 9,
z: 76,
};
let attemts = 0;
let isXFound = false;
let isYFound = false;
let isZFound = false;
const pointHistory = [];
const getRandomPoint = () => {
return {
x: isXFound ? isXFound : Math.floor(Math.random() * 101),
y: isYFound ? isYFound : Math.floor(Math.random() * 101),
z: isZFound ? isZFound : Math.floor(Math.random() * 101),
};
};
const getDifference = (point, pointToCompare) => {
return {
x:
Math.max(point.x, pointToCompare.x) - Math.min(point.x, pointToCompare.x),
y:
Math.max(point.y, pointToCompare.y) - Math.min(point.y, pointToCompare.y),
z:
Math.max(point.z, pointToCompare.z) - Math.min(point.z, pointToCompare.z),
};
};
const condition = !isXFound && !isYFound && !isZFound;
while (condition) {
const point = getRandomPoint();
const difference = getDifference(point, pointToFound);
pointHistory.push(point);
attemts += 1;
if (isXFound && isYFound && isZFound) {
console.log("Total attempts: ", attemts);
console.log(point);
break;
}
if (difference.x === 0 && !isXFound) {
isXFound = point.x;
}
if (difference.y === 0 && !isYFound) {
isYFound = point.y;
}
if (difference.z === 0 && !isZFound) {
isZFound = point.z;
}
}
console.log(pointHistory);
I have a randomizer instead of an algorithm, that's all I got. Help.
This can be done with at most 3 guesses and often with 2 guesses:
Let the first guess be [0, 0, 0], and ask for the distance
Find in the 100x100x100 cube all points that have that distance to [0, 0, 0]. There might be around 100-200 points that have that distance: consider all of these candidates.
Take the first candidate as the second guess and ask for the distance
Find among the other candidates the ones that have exactly that distance to the first candidate. Often there will be only one point that satisfies this condition. In that case we can return that candidate and only 2 guesses were necessary.
Otherwise (when there is more than one candidate remaining) repeat the previous step which will now certainly lead to a single point.
Here is an implementation that provides a blackbox function which chooses the secret point in a local variable, and which returns two functions: f for the caller to submit a guess, and report for the caller to verify the result of the algorithm and report on the number of guesses. This is not part of the algorithm itself, which is provided in the findPoint function.
const rnd = () => Math.floor(Math.random() * 101);
const distance = (a, b) =>
a.reduce((acc, x, i) => acc + (x - b[i]) ** 2, 0) ** 0.5;
function findPoint(f) {
// First guess is always the zero-point
let guess = [0, 0, 0];
let dist = f(guess);
if (dist === 0) return guess; // Extremely lucky!
// Find the points in the cube that have this distance to [0,0,0]
let candidates = [];
const limit = Math.min(100, Math.round(dist));
for (let x = 0; x <= limit; x++) {
const p = [x, limit, 0];
// Follow circle in X=x plane
while (p[1] >= 0 && p[2] <= limit) {
const d = distance(p, guess);
const diff = d - dist;
if (Math.abs(diff) < 1e-7) candidates.push([...p]);
if (diff >= 0) p[1]--;
else p[2]++;
}
}
// As long there are multiple candidates, continue with a guess
while (candidates.length > 1) {
const candidates2 = [];
// These guesses are taking the first candidate as guess
guess = candidates[0];
dist = f(guess);
if (dist === 0) return guess; // lucky!
for (const p of candidates) {
let d = distance(p, guess);
let diff = d - dist;
if (Math.abs(diff) < 1e-7) candidates2.push(p);
}
candidates = candidates2;
}
return candidates[0]; // Don't call f as we are sure!
}
function blackbox() {
const secret = [rnd(), rnd(), rnd()];
console.log("Secret", JSON.stringify(secret));
let guessCount = 0;
const f = guess => {
guessCount++;
const dist = distance(secret, guess);
console.log("Submitted guess " + JSON.stringify(guess) + " is at distance " + dist);
return dist;
};
const report = (result) => {
console.log("Number of guesses: " + guessCount);
console.log("The provided result is " + (distance(secret, result) ? "not" : "") + "correct");
}
return {f, report};
}
// Example run
const {f, report} = blackbox();
const result = findPoint(f);
console.log("Algorithm says the secret point is: " + JSON.stringify(result));
report(result);
Each run will generate a new secret point. When running this thousands of times it turns out that there is 1/9 probability that the algorithm needs a third guess. In the other 8/9 cases, the algorithm needs two guesses.
One idea is as follows:
You pick an initial random point, and for each dimension, find the exact value. How? For the sake of symmetry, suppose that you desire to find x of the target point. Increase by one the x, and compute the distance of the new point from the target point. If it goes further, it means that you should move in the opposite direction. Hence, you can run a binary search and get the distance to find the exact x of the target point. Otherwise, it means that you are going in the right direction along X-axis. So, do a binary search between all points with the same y and z such that their x values can change from x+1 to 100. A more formal solution comes in the following (just a pseudo-code).
You should also ask about the complexity of this solution. As the dimension of the point is constant (3) and checking these conditions take a constant time, the complexity of number of calling getDifference function is O(log(n)). What is n here? the length of valid range for coordinates (here is 100).
1. p: (x,y,z) <- Pick a random coordinate
2. dist: (dist_x, dist_y, dist_z) <- getDifference(p, TargetPoint)
3. For each dimension, do the following (i can be 0 (x), 1 (y), 2 (3)):
4. if(dist == 0):
5. isFound[i] <- p[i]
6. continue
7. new_i <- p[i] + 1
8. new_point <- p
9. new_point[i] <- new_i
10. new_dist <- getDifference(new_point, pointToFound)
11. if(new_dist == 0):
12. isFound[i] <- new_point[i];
13. continue
14. if(new_dist[i] > dist[i]):
15. isFound[i] <- binary_search for range [0, p[i]-1] to find the exact value of the pointToFound in dimension i
15. continue
16. else:
17. isFound[i] <- binary_search for range [p[i] + 1, 100] to find the exact value of the pointToFound in dimension i
18. continue
Following method will work for coordinates with positive or negative real values as well.
Let's say you are searching for the coordinates of point P. As the first query point, use origin O. Let the distance to the origin O be |PO|. At this point, you know that P is on the surface of sphere
(P.x)^2 + (P.y)^2 + (P.z)^2 = |PO|^2 (1)
As the second query point, use Q = (|PO|, 0, 0). Not likely but if you find the distance |PQ| zero, Q is the point you are looking for. Otherwise, you get another sphere equation, and you know that P is on the surface of this sphere as well:
(P.x - |PO|)^2 + (P.y)^2 + (P.z)^2 = |PQ|^2 (2)
Now, if you subtract (1) from (2), you get
(P.x - |PO|)^2 - (P.x)^2 = |PQ|^2 - |PO|^2 (3)
Since the only unknown in this equation is P.x you can get its value:
P.x = (((-|PQ|^2 + |PO|^2) / |PO|) + |PO|)/2)
Following similar steps, you can get P.y with R = (0, |PO|, 0) and P.z with S = (0, 0, |PO|). So, by using four query points O, Q, R, and S you can get the coordinates of P.

Algorithm or formula that can take an incrementing counter and make it appear uniquely random

I am wondering if there is a general formula of some sort that can take a single incrementing integer, and run it through a modulus sort of thing to shift it to a random place, so as you increment the counter, its output value jumps around and appears random, yet no value is ever hit twice. Assuming some limit on the set of numbers like 16-bit integers (65536 integers), or 32-bit integers, etc.. Perhaps there is a way to spiral numbers down somehow, I don't know. The sequence would be predictable, but to a layman it would appear random without thinking much of it.
For example, you can multiply a number by 2 to make it not appear directly incremented. But that's not very sophisticated. You could perhaps start the number at the middle of the set (like 30103 for 16-bit integers), then multiply by 2 and rotate the numbers using a modulus, and this would appear even less incremented. But you could still see a pattern.
I'm wondering what sorts of patterns or equations you could run an incremented number through (in a bounded set of integers) so that the output appears the least predictable as possible, and at the same time it never hits the same number twice. This way you could make IDs appear randomly generated to the layman without having to store all the IDs in a database in random order in advance. The formula would generate them from a single stored integer. What is possible in this regard, and what is the equation? How far can it theoretically go?
Maybe you could make the set odd, and skip every 20th number, and somehow prove that it will eventually revolve through the whole set without repeats. I can't figure this out though.
Update: This seems to be in the field of pseudorandom number generation, like this, but I'm not sure if they fit the added constraint of never repeating the number.
Here is what I found and implemented, but it's giving some duplicates :/.
const fetch = (x, o) => {
if (x >= o) {
return x
} else {
const v = (x * x) % o
return (x <= o / 2) ? v : o - v
}
}
const fetch32 = (x) => fetch(x, 4294967291)
const fetch16 = (x) => fetch(x, 65519)
const fetch8 = (x) => fetch(x, 251)
// the last number can be anything.
const build32 = (x, o) => fetch32((fetch32(x) + o) ^ 1542469173)
const build16 = (x, o) => fetch16((fetch16(x) + o) ^ 42703)
const build8 = (x, o) => fetch8((fetch8(x) + o) ^ 101)
let i = 0
let n = Math.pow(2, 32)
while (i < n) {
let j = 0
let r = {}
while (j < n) {
let x = build32(j, i)
if (r[x]) throw `${i}:${j}:${x}`
r[x] = true
j++
}
i++
}
The other linked question in the comment doesn't show a JavaScript implementation that adheres the the uniqueness constraint.
If you are looking for a sequence, where one value is produced from knowing what the previous value was, then what you are looking for could be a Linear congruential generator, with a modulus of a power of 2. There are a few parameters involved:
m: the modulus, which in your case is 28, 216, or 232.
a: the multiplier. To ensure that all values are produced before the first duplicate is generated, this must be a multiple of 4 plus 1 (assuming m is a power of 2).
c: the increment. It must be odd.
You can play with these numbers to arrive at a series that you are satisfied with in terms of "randomness".
The above referenced Wikipedia article lists some parameter choices that are used in some pseudo random generators. I have just selected a=97 and c some odd number half way the range.
Here is some code to prove the uniqueness:
/*
Assuming that m is a power of 2:
- c must be odd
- a % 4 must be 1
*/
function createFetch(m, a, c) { // Returns a function
return x => (a * x + c) % m;
}
const m = 2**16;
const fetch16 = createFetch(m, 97, (m>>1)-1);
const r = new Set;
let x = 1;
for (let i = 0; i < m; i++) {
x = fetch16(x);
if (i < 10) console.log(x);
if (r.has(x)) throw `${i}:${x}`
r.add(x);
}
console.log("...");
console.log(`generated ${r.size} unique numbers`);
NB/ this is a good use case for a generator, which in JavaScript looks like this:
function * fetch(m, a, c, x=1) {
while (true) {
x = (a * x + c) % m;
yield x;
}
}
const m = 2**16;
const fetch16 = fetch(m, 97, (m>>1)-1);
const r = new Set;
for (let i = 0; i < m; i++) {
x = fetch16.next().value;
if (i < 10) console.log(x);
if (r.has(x)) throw `${i}:${x}`
r.add(x);
}
console.log("...");
console.log(`generated ${r.size} unique numbers`);
Any block cipher whose block size is n bits is a permutation of {0,1,2, ..., 2n-1}. Thus, if E is such a block cipher and k is a valid key for E, then Ek(0), Ek(1), ..., Ek(2n-1) are all distinct. If the block cipher is good then the values appear "random" to the naked eye. If you change the key k you get a different permutation.
This is actually mentioned in the link you provided.
Consider this answer as well.
var bottomLimit = 1
var topLimit = 10
var arr = []
for (var i = bottomLimit; i < topLimit; i++) {
arr.push(i)
}
arr = shuffle(arr);
console.log(arr);
//https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array#answer-2450976
function shuffle(array) {
var currentIndex = array.length,
temporaryValue, randomIndex;
while (0 !== currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
Well,you can generate random no. inside the range of two no.
public static int getRandomVal(int min, int max) {
Random random = new Random();
return random.nextInt((max - min) + 1) + min;
}
public static void getRandomNumbers(int size, int min,
int max) {
ArrayList<Integer> numbers = new ArrayList<Integer>();
while (numbers.size() < size) {
int random = getRandomVal(min, max);
if (!numbers.contains(random)) {
numbers.add(random);
System.out.println(" "+random);
}
}
}
now to generate 10 different no. between 50 and 100 you can use
getRandomNumbers(10, 50,100);
This approach is very easy I am creating an array and just checking the random value if it is already present or not. If it is not present I am pushing it to the array and outputting it.
Get yourself a seeded random number generator.
Seed with 1, return next random number. Seed with 2, return next random number. Seed with 3, return next random number...
If you seed with an integer then the next random number will be repeatable and pseudo random.

fast way to check a number is in a range of a group of numbers

I just encounter a scenario like the following. It sounds a little bit like a leetcode question.
Support I can get a list of number in a pattern, to simplify my questions.
For example, [0, 5, 10, 15, 20, 25] or [0,7,14,21], it's already sorted.
then given a range (I can guarantee range will never overlap between two consecutive numbers)
for example, range=2;
If num=11, then I supports to get index of 10 because 11 is in a range of 10-2 to 10+2.
If num=12.5, then I will just return -1 or anything else to indicate we does not find.
I can simply go through the list and check if the number is in the range of each number but I feel like there is a O(1) solution since the list itself has some pattern exist. any help is greatly appreciated.
Diff is also provided, the above example diff=5.
I does not encounter any performance issue now with O(N) list checking, just want to make thing better.
You could use Array.some to check if the number (and range) overlaps with your list, for example:
This will be O(n), and I suspect you will need a very large list indeed to justify creating an O(1) type solution.
We can go the other way, creating an array of candidate numbers (numbers within the range, e.g. for 10 +- 2 this would be [8,9,10,11,12]. NB: This approach will not work for floating point values.
We check each number for membership of a set created from the list. This will still technically be O(n), but N will most likely be small (e.g. 5).
let list = [0, 5, 10, 15, 20, 25];
// This solution will need at most N iterations, where N is the length of list
function checkInRange(value, range, list) {
return list.some((el) => {
return (el >= (value - range)) && (el <= (value + range));
})
}
// This solution will need at most N iterations, where N is the length of a, e.g. 2 * range + 1
function checkInRangeSet(value, range, list) {
// Create an array of matching numbers, e.g. 8,9,10,11,12
let a = Array.from({ length: 2*range + 1 }, (v,k) => value - range + k);
let set = new Set(list);
return a.some((el) => {
return set.has(el);
})
}
console.log("Solution with simple loop");
console.log(checkInRange(11, 1, list));
console.log(checkInRange(10, 0, list));
console.log(checkInRange(30, 5, list));
console.log(checkInRange(9, 0, list));
console.log(checkInRange(100, 20, list));
console.log("Solution with Set");
console.log(checkInRangeSet(11, 1, list));
console.log(checkInRangeSet(10, 0, list));
console.log(checkInRangeSet(30, 5, list));
console.log(checkInRangeSet(9, 0, list));
console.log(checkInRangeSet(100, 20, list));
Here's my shot at an O(1) solution.
const list = [0, 7, 14, 21, 28];
const interval = list[1];
const range = 2;
function getIndex(num, range) {
const halfItvl = interval / 2;
const inRange = Math.abs((num % interval) - halfItvl) >= halfItvl - range;
return inRange ? Math.trunc((num + halfItvl) / interval) : -1;
}
// Test for all numbers within the list
for (var i = 0; i < list[list.length - 1]; i++) {
const res = getIndex(i, range);
if (res === -1) {
console.log(`${i} is out of range ${range}`);
} else {
console.log(`${i} is within range ${range} of index ${res}`);
}
if (i % interval === interval - 1) {
console.log("---------------------");
}
}
Actually, it seems a good bit simpler if I establish the index first. Then all we need to do is subtract the current number from the value at the found index, take its absolute value, and see if it's less than or equal to the given range number.
const list = [0, 7, 14, 21, 28];
const interval = list[1];
const range = 2;
function getIndex(num, range) {
const idx = Math.trunc((num + (interval / 2)) / interval);
return Math.abs(list[idx] - num) <= range ? idx : -1;
}
// Test for all numbers within the list
for (var i = 0; i < list[list.length - 1]; i++) {
const res = getIndex(i, range);
if (res === -1) {
console.log(`${i} is out of range ${range}`);
} else {
console.log(`${i} is within range ${range} of index ${res}`);
}
if (i % interval === interval - 1) {
console.log("---------------------");
}
}

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)

Generic formula for build an array of numbers of base-n

Say I want to build an array of numbers base 8, or base 26, I'm not sure how to approach a general formula for doing this:
console.log(arrayOfNumbersOfBase(8, 0, 10));
console.log(arrayOfNumbersOfBase(26, 0, 10));
function arrayOfNumbersOfBase(base, start, size)
{
var array = [];
for (var i = start, n = size; i < n; i++)
{
array.push(i * (base));
}
return array;
}
You can take the next approach as a starting point, basically I had to define some utility methods:
mapToChar(n) maps a number n to a character representation, for example, 10 is mapped to 'A'.
convertToBaseN(n, base) converts the number n to his representation on the given base. This method uses a recursive approach and utilizes the previous one.
Finally, generateNumbersOfBase(base, start, size) generates an array of size elements starting with the number start for the given base.
CODE:
// Next utility method map a decimal number to a character representation.
const mapToChar = (n) =>
{
n = (n >= 0 && n <= 9) ? '0'.charCodeAt() + n : n - 10 + 'A'.charCodeAt();
return String.fromCharCode(n);
}
// Next utility method convert a decimal number to his base-n representation.
const convertToBaseN = (n, base, res = "") =>
{
if (n <= 0)
return (res && res.split("").reverse().join("")) || "0";
// Convert input number to given base by repeatedly
// dividing it by base and taking remainder.
res += mapToChar(n % base);
return convertToBaseN(Math.floor(n / base), base, res);
}
// Next method generates an array of numbers for a given base.
const generateNumbersOfBase = (base, start, size) =>
{
return Array(size).fill(0).map((x, idx) => convertToBaseN(start + idx, base));
}
// Finally, generate some arrays.
let base10Array = generateNumbersOfBase(10, 15, 5);
let base2Array = generateNumbersOfBase(2, 5, 9);
let base16Array = generateNumbersOfBase(16, 10, 12);
let base8Array = generateNumbersOfBase(8, 1, 12);
console.log(
JSON.stringify(base10Array),
JSON.stringify(base2Array),
JSON.stringify(base16Array),
JSON.stringify(base8Array),
);
Now, if you need to convert some base-n representation back to decimal number, you can use next approach:
const convertToDec = (str, base) =>
{
let codeA = 'A'.charCodeAt();
let code0 = '0'.charCodeAt();
return str.split("").reverse().reduce((acc, c, idx) =>
{
let code = c.charCodeAt();
c = code + ((c >= '0' && c <= '9') ? -code0 : -codeA + 10);
return acc += c * Math.pow(base, idx);
}, 0);
}
// Lets convert back some arrays generated on the previous exampel
let base2Array = ["101","110","111","1000","1001","1010","1011","1100","1101"];
let base16Array = ["A","B","C","D","E","F","10","11","12","13","14","15"];
let res2 = base2Array.map(x => convertToDec(x, 2));
let res16 = base16Array.map(x => convertToDec(x, 16));
console.log(
JSON.stringify(res2),
JSON.stringify(res16)
);

Categories

Resources