Javascript number interval search optimization - javascript

As topic says i ran into optimization problem when it comes to a large amount of intervals
Variables: FirstPrice, LastPrice, Increment and the number that user writes
Task: Need to round number to one side or another in specific interval.
Example:
FirstPrice = 0.99, LastPrice = 9.99, Increment = 1 User number = 5.35
Workflow:
I need to find interval in which one number exists, and what i could think of is to push numbers into array. So for this example array would be:
["0.99","1.99","2.99","3.99","4.99","5.99","6.99","7.99","8.99","9.99"].
Then i use for loop to find in which interval (in this case number = 5.35) number exists. In this case interval would be from 4.99 to 5.99. And then user number is updating to 4.99 or 5.99.
Problem:
It works fine doing that with low amount of numbers. But it comes really hard to execute high ranges. E.g. if FirstPrice = 1 LastPrice = 1000000 and Increment = 1. Then my array gets thousands of values and it takes way too long to push every value into array and find the number. Array gets ~999999 values and then loop goes through all of them to find specific interval.
So i think my problem is clear. The optimization. I need a better way of doing this. I tried cutting price range to half and a half but then intervals are wrong. Tried working with the inserted number but the same problem occurred.

Correct me if I'm wrong, but isn't this simply:
lower = floor((q - s) / i) * i + s
upper = lower + i
where:
q = User number
s = FirstPrice
i = Increment
function foo(q, s, i) {
const lower = Math.floor((q - s) / i) * i + s;
const upper = lower + i;
return [lower, upper];
}
console.log(
foo(5.35, 0.99, 1),
foo(5.35, 0.99, 2),
foo(5.35, 0.99, 3)
);

Related

How to create number suffixes such as "100K" without having to be repetitive?

I've been having a problem that when my auto clicker in my clicker game goes fast enough to get to 200 thousand, it starts to lag, and then it doesn't function properly, or as fast.
Is there a way to make 100 thousand turn into 100K, and 101 thousand turn into 101K without being repetitive?
I tried this with my original code, and realized putting up to 1000 suffixes into each function would be a little too hard:
if (number >= 100000) {
document.getElementById(ID).innerHTML = "100K"
}
if (number >= 101000) {
document.getElementById(ID).innerHTML = "101K"
}
and on and on.
I don't want multiple if statements!
This would work, but it would take up way too much space, and I know there is an easier way to it, but I just couldn't find it. Can anyone provide a way to do this?
Try separating the job of formatting your number into a different function.
SUFFIXES = 'KMBTqQsSOND' // or whatever you'd like them to be
function getSuffixedNumber(num) {
var power = Math.floor(Math.log10(num));
var index = Math.floor(power / 3);
num = Math.round(num / Math.pow(10, (index * 3))); // first 3 digits of the number
return num + (SUFFIXES[index - 1] || ''); // default to no suffix if we get an out of bounds index
}
You can call the function like this: var x = getSuffixedNumber(101000), the value of x will be "101K".

how to generate unique ID character using letters a-z

Here is a little challenge, I hope it will be useful for others too.
Task is to obtain an ID character from alphabets of english language. a-to-z
My solution currently allows ID (words) of 26 diff length (max possible). with 90 possible words.
I know this can be increased if we pick random characters after single character IDs are obtained (done with.) (But) I am finding it hard to figure out how to manage NOT hitting a combination already found (ID has to be unique). As we see it takes a long time if it starts finding the same combinations over and over. this probability increases as we obtain more and more ID-combinations.
Here is my code and fiddle to test and check:
fiddle
code:
html
<p>
start
</p>
jquery:
function addto(t) {
$("p").append("<b>" + t + "</b>");
}
global_ID_array = [];
lowerAlpha = "abcdefghijklmnopqrstuvwxyz";
var myIDlength = 1;
function getIDChar (){
do {
var myIDchar = lowerAlpha.substr(0, myIDlength);
lowerAlpha = lowerAlpha.replace(myIDchar,'');
if (lowerAlpha.length < myIDlength){
lowerAlpha = "abcdefghijklmnopqrstuvwxyz"; //reset
myIDlength++;
}
} while (global_ID_array.indexOf(myIDchar) > -1)
global_ID_array.push(myIDchar);
addto(myIDlength+':'+global_ID_array.length+',');
}
do{
getIDChar();
}while (myIDlength < 26);
addto('<br \>myIDlength='+myIDlength);
addto('<br \>global_ID_array last val='+global_ID_array[global_ID_array.length-1]+'<p>');
To get started, instead of thinking about the IDs in terms of letters, think about it in terms of numbers.
Since there are 26 possible characters, each character can be considered as a digit of a base-26 numeric system, with a == 0, and z == 25. The problem now boils down to generating a number and converting it to base-26 using the alphabets as digits. Since the ID length is 26 characters, we can generate up to 26^26 unique IDs.
Now, to make sure that the ID generated is unique (up to 26^26 generated IDs), we need to find a way to generate a unique number every time. The simplest way to do this is to initialize a counter at 0 and use it to generate the ID. Every time an ID is done generating, increment the counter. Of course, this is a very deterministic generation algorithm, but you could use a seeded random number generator that guarantees the uniqueness of random numbers generated in a range.
The algorithm may look as follows:
n = random(0, 26^26 - 1)
id = "a" * 26
// chars[i] is the character representing the base-26 digit i;
// you may change/shuffle the list as you please
chars = ['a', 'b', 'c', ..., 'y', 'z']
for i in 0..26 {
// Get the character corresponding to the next base-26 digit of n (n % 26)
id[26 - i] = chars[n % 26]
n /= 26
}

Where is my logic going wrong in trying to calculate the presidential outcome?

Let me explain what I'm trying to do. I have data like
const dataByState = {
'Washington' : { ElectoralVotes : 12, RChance: 54, DChance: 46 },
'Oregon': { ElectoralVotes: 7, RChance: 51, DChance: 49 },
.
.
.
'Hawaii' : { ElectoralVotes: 4, RChance : 40, DChance: 60 }
};
where one of the above key-value pairs like
'Hawaii' : { ElectoralVotes: 4, RChance : 40, DChance: 60 }
means "In the state Hawaii, which has 4 electoral votes, there is a 40% chance of the Republican Candidate winning and a 60% chance of the Democrat candidate winning". What I'm ultimately trying to do is calculate the chance of each candidate winning the election. How this would be done in a perfect world is
Iterate through all 2^51 combinations of states
For each combination c, its combined electoral votes are greater than or equal to 270, add it to a collection C of collecions of states
For the Republican candidate, sum up the probabilities of winning each combination of states in C; call that value r. That's his/her chance of winning. The Democrat's chance is 1 - r.
But since I can't go through all 2^51, what I'm doing is choosing some N smaller than 51 and doing
Find a random 2^N combinations of states whose combined electoral votes sum to greater than or equal to 270; call this combination C.
For the Republican candidate, sum up the probabilities of winning each combination of states in C; call that value r. Multiply r by 2^(51-N). That's approximately his/her chance of winning. The Democrat's chance is 1 - r.
Anyhow, this doesn't seem to be working and I'm wondering whether my logic is wrong (I haven't taken statistics since college 3 years ago) or if I'm running into rounding errors. I'm getting a near 100% of the Republican winning (i.e. America being made great again) when I make the chance even in every state, which is wrong because it should calculate to about 50/50.
Code dump: https://jsfiddle.net/pqhnwek9/
The probability of a republican victory is
probRepVict = 0
for(combination in combinations) {
if(combination is republican victory) {
probRepVict += proability of combination
}
}
As you observe it is not feasible to calculate the entire sum. Hence, you choose some subset C to try to estimate this probability.
N = number of combination // 2^51
n = size of C
probRepVictEstimate = 0
for(combination in C) {
if(combination is republican victory) {
probRepVictEstimate += proability of combination
}
}
probRepVictEstimate *= N/n
In the last statement we assume that the probability of a victory scales linearly with the size of the subset.
I believe the logic goes wrong at several places in the script:
(1) When generating the random number you might not get a sufficiently many bits of randomness. For instance if there were 54 states you would be outside of the safe integer range. Some implementations might give you even less fewer bits of randomness (it did break for me in Node, which only give 32 bits). Thus I suggest adding a function
function getRandom() {
// Generate 32 random bits
var s = Math.floor(Math.random()*Math.pow(2, 32)).toString(2)
return new Array(32 - s.length + 1).join("0") + s
}
Replacing
const rand = Math.floor(Math.random() * Math.pow(2,states.length));
with const rand = getRandom() + getRandom();, and replace getCombo with
const getCombo = (i) => {
let combo = [];
for(var j = 0; j < states.length; ++j)
if(i[j] == "0")
combo.push(states[j]);
return combo;
}
(2) You need to count both wins and losses for the republican party to be able to estimate the probability. Thus you cannot add the complement of a combo (by the way, ~ is a bitwise operations, hence convert the operand to a 32-bit integer, so your code does not work as intended). Hence your code should be simplified to:
...
if(!winningCombos.hasOwnProperty(rand)) {
const stateCombo = getCombo(rand);
if(hasSufficientVotes(stateCombo))
{
winningCombos[rand] = stateCombo;
++wins;
}
++count;
}
...
(3) You should scale repubChanceSum by N/n, where N = Math.pow(2, 51) and n = limit. Note that limit should be considerably greater than winningCombos.length.
With these modifications the code correctly predicts a ~50% probability. See this modified fiddle.
Let's hope we get a more optimistic outlook for the future with more realistic probabilities.

Can I accurately calculate an average from a medium size set of 2 d.p. numbers using JavaScript?

I need to find the average of a set of values and after doing some reading am not sure if JavaScript is able to produce an accurate result or not.
Each value has a precision of 2 d.p. and there could be up to 10000 of them between -100000.00 and 100000.00. The result also needs to be to 2 d.p.
From what I can see it is usually the figures around the 16th decimal place that are inaccurate which means that I would have to average an extremely large set before affecting my result. Is the best way of doing it to simply sum all of the values, divide by the total number and then use a toFixed(2)?
You could take advantage of your 2dp prevision, and multiply all your numbers by 100 first, then do the mathematics using integers. EG, a float error occurs in this simple average (I am just using 1dp for this example):
(0.1 + 0.2) / 2
0.15000000000000002
But this works:
(0.1*10 + 0.2*10) / (2*10)
0.15
Some good reading here:
http://floating-point-gui.de/basic/
and here:
How to deal with floating point number precision in JavaScript?
and a really precise fix to do it using decimals is to use this:
https://github.com/dtrebbien/BigDecimal.js
Example for 2 dp:
var numbers = [0.10, 0.20, 0.30]
var simple_average = numbers.reduce(function(a, b) {
return a + b
}) / numbers.length
console.log(simple_average)
var smart_average = numbers.map(function(a) {
return a * 100
}).reduce(function(a, b) {
return a + b
}) / (numbers.length * 100)
console.log(smart_average)
This demo can be run here -- http://repl.it/e1B/1

adding string numbers (math) with local storage

I am trying to add numbers as strings using basic math. I first set the local storage to "0" then add "1" to it each time. I feel I am on the right path, but when I run this my result is not 0 + 1 = 1 rather I get "01" in my local storage. I want to be able to add 1 to the existing local storage each time so 0 + 1 I get 1. Next time around 1 + 1 I get 2, and 2 + 1 I get 3 and so on.
// sets "points" to 0 when user first loads page.
if (localStorage.getItem("points") === null){
localStorage.setItem("points", "0");
}
// get points
var totalPoints = localStorage.getItem("points");
// add 1 points to exisiting total
var addPoint = totalPoints +"1";
// set new total
localStorage.setItem("points", addPoint);
You can convert a string to a number in several ways (not an exhaustive list):
var n = s * 1; // s is the string
var n = s - 0;
var n = parseFloat(s);
var n = Number(s);
var n = ~~s; // force to 32-bit integer
var n = parseInt(s, 10); // also integer, precise up to 53 bits
Convert your strings to numbers when you fetch them from local storage, do the math, and then put the results back.
edit — the thing to keep in mind is that + is more "interesting" than the other arithmetic operators because it has meaning for string-valued operands. In fact, JavaScript tends to prefer string interpretation of the + operator, so if there's a string on one side and a number on the other, the operation is string concatenation and not arithmetic addition.

Categories

Resources