How to calculate the proper numbers without doing a switch? - javascript

I need an algorithm to calculate an offset based on an array length. I have manually got the number for each number, but if possible without doing a case/switch.
Given an array length x, I need to come up with something that calculates this:
20 if x == 5
25 if x == 4
35 if x == 3
50 if x == 2
Am I missing something? Do I have to use a switch here?

A random guess, but how about 105 div x?
Or maybe round(20 / x) * 5?
In fact, since you only gave four cases, there are a lot different methods you can use. But perhaps more importantly is - are there any patterns you could follow that enables your code to make more sense?

At first lets make clear that your mapping function not linear, because for the (linear) x the interval between your function results differ.
To find a non-linear function you need to apply some "more difficult" function interpolation (e.g. Lagrange).
My result with this nice online tool for the input [2, 50], [3, 35], [4, 25], [5, 20] is (5x^2-55x+190) / 2.
But to be honest for such few values I would use a switch - it might be more efficient and keeps your code understandable (e.g. if you look at it in a year or so ;-) ).
Hope that helps.
*Jost

Related

Javascript find intersections given two functions

I am trying today to solve for the coordinates where functions intersect. I am using the nerdarmer library right now, but it only returns only one solution out of all the possible solutions. For example, I want the code below to print -1, 0, 1 but it only outputs 0. Another example is if I want to find intersections between y = 0 and sin(x), I want the output to be ..., (-2pi, 0), (-pi, 0), (pi, 0), (2pi, 0), (3pi, 0), ...
intersect("x^3", "x")
function intersect(f1, f2){
var x = nerdamer.solve('f1', 'f2');
console.log(x.toString());
}
Is there any way to get all the possible solutions?
You've misunderstood the syntax of nerdamer.solve
The first argument is the formula or equation.
The second argument is the variable to solve for.
If the first argument is not an equation, it is assumed to be equal 0.
In your case x^3=0. which only has the solution 0.
If you want to intersect the equations you will need to set them equal to each other in the first argument. And in the second argument just specify x. (or change it to suit your needs if required).
intersect("x^3", "x")
function intersect(f1, f2){
var x = nerdamer.solve(f1+"="+f2, "x");
console.log(x.toString()); //outputs [0,1,-1]
}
Edit:
In your example you also directly put the strings "f1" and "f2" into the solve function which seems to just solve f=0;

Unique identifier with smallest base possible, without collisions on old/new set of data

I receive one or more trees of nodes.
Nodes may, or may not have ID properties on them.
Currently I am iterating through the tree and adding random 8 digit numbers on nodes which do not have ID property. As I do not expect more than 10k nodes in the trees chance of having collisions is very small.
Still I am considering how best to reduce the length of the IDs to maybe 4 digits while making sure there are no collisions within one tree. What comes to my mind is to iterate once through the tree gathering existing IDs into a Set and than again adding new IDs while checking against the Set that there are no collisions. Set would have to be reset for each tree.
I would appreciate your opinion on this matter and advice if there are more performant ways of achieving this.
Appendix A:
I am considering following (simplified 0-9) issue. If I have a Set of existing IDs [0, 1, 2, 5, 8, 9] I would have to generate random numbers until I get e.g. 4 (no collision) which I am concerned would be a bit slow on a larger Set and surely not the optimal route.
Here you have a really simple approach which will generate for you an array of not used numbers with a given MAX range.
const MAX = 30;
const usedNumbers = [3, 4, 12, 13, 14, 16, 23, 27];
// https://stackoverflow.com/questions/3746725/how-to-create-an-array-containing-1-n
const notUsedNumbers = Array.from(Array(MAX), (_, i) => i+1).filter(i => !usedNumbers.includes(i));
console.log(notUsedNumbers);
And link to fiddle: https://jsfiddle.net/L9r6anq1/
10^8 possibilities with random selection means you have a 50% chance of collision with only 10^4 objects (see Birthday Paradox); that is not "very small" odds. Reducing that to only 10^4 possibilities with 10^4 objects means collisions will approach 100% as you get toward the end and, if you ever have 10k+1 objects one day, will never terminate.
In general, if you want to use a relatively short ID space, you're going to need a very efficient conflict detection system, e.g. keeping all assigned (or not assigned) values in a scratchpad, or just give up on randomly assigning values and go sequentially.

Simple way to return values approximating a sin/cosin curve, without using trigonometry?

Apologies in advance for a difficult-to-explain question, for which the answer might simply be "no"...
I have a value -1 >= n <= 1
I'd like to increase its amplitude, say by 2: 2*n
I'd also like to adjust its phase, say by .5: 2*n + .5
How can I do this so that when n increases past 1, it flips signs and reduces itself, for example: when n increases to 1.1, the result is actually .9.
I know this can be done using trig, but I'm having difficulty increasing its amplitude and shifting its phase - by the time I'm done doing both I get this expression:
Math.sin(n*1.57 + Math.PI/4)
And to that expression I still need to perform additional calcs/rounding to get my final values; the expression becomes complicated bloated.
I'm wondering if there's a simpler way to get the desired range/values, avoiding trig altogether? I imagine an operator similar to the modulo, except instead of "resetting" to 0, it reduces in value...
Turns out, a triangle wave solves my problem. It gives oscillating values similar to sine (without ease), and it avoids Math.trig and simplifies my formula. I expanded on the formula given in this SO answer as well as these wikipedia formulas.
Fried Brice's answer suggesting sawtooth was on the right track - but triangle wave is better suited for me, and the 1-line formula makes my eyes feel better.
You can explicitly write out the formula in cases for one period, and use recursion for values outside the fundamental period.
E.g.
function notTrig(x) {
switch (true) {
case (x >= 0 && x < 1):
return x
case (x >= 1 && x < 2):
return 2 - x
default:
notTrig(x - 2)
}
}
This should give you a sawtooth signal with mean 1/2, amplitude 1/2, and period 2. You need to handle negatives as well: exercise left to asker ;-)
Edit: It occurs to me after the fact that I'm misusing the term "sawtooth wave" above. The function I am describing is continuous, and the terms I should be using is "triangle wave." That said, I am very pleased with #calipoop's answer.

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].

Even sets of integers

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).

Categories

Resources