I have a list of integer sequence:
[10,15,30,45,60,75,90......n*15]
Let's say you have a value i.e. 33
What calculation i should do to find the closest value of 33 into the above sequence?
(JavaScript)
Can we find it without loop?
Thanks.
As others have already pointed out, if you're working with multiples of 15 (assuming the sequence starting with 10 was a mistake), then you can simply do the following:
var interval = 15;
var value = 33;
var closest = Math.round(value / interval) * interval;
console.log(closest);
You didn't specify any language, so here is some pseudocode. Also I assume that the sequence is actually 15*n and there has to be 0 instead of 10 as the first element. Assume the sequence is form 0 to 15*N and the test value is test. IMHO, the simplest algorithm is following:
if(test <= 0)
return 0
else if (test >= 15*N)
return 15*N
else {
lower = Math.floor(test/15)
upper = lower + 1
lowerDif = test - 15*lower
upperDif = 15*upper - test
if (lowerDif < upperDif)
return 15*lower
else
return 15*upper
}
The idea is that you need to check if test is inside [0; 15*N] range. If no - return the boundary, else check two values at indices Math.floor(test/15) and Math.floor(test/15) + 1. It is true that
Math.floor(test/15) <= test < Math.floor(test/15) + 1
So whichever is closer is the answer.
Related
I'm building an app and in one of my functions I need to generate random & unique 4 digit codes. Obviously there is a finite range from 0000 to 9999 but each day the entire list will be wiped and each day I will not need more than the available amount of codes which means it's possible to have unique codes for each day. Realistically I will probably only need a few hundred codes a day.
The way I've coded it for now is the simple brute force way which would be to generate a random 4 digit number, check if the number exists in an array and if it does, generate another number while if it doesn't, return the generated number.
Since it's 4 digits, the runtime isn't anything too crazy and I'm mostly generating a few hundred codes a day so there won't be some scenario where I've generated 9999 codes and I keep randomly generating numbers to find the last remaining one.
It would also be fine to have letters in there as well instead of just numbers if it would make the problem easier.
Other than my brute force method, what would be a more efficient way of doing this?
Thank you!
Since you have a constrained number of values that will easily fit in memory, the simplest way I know of is to create a list of the possible values and select one randomly, then remove it from the list so it can't be selected again. This will never have a collision with a previously used number:
function initValues(numValues) {
const values = new Array(numValues);
// fill the array with each value
for (let i = 0; i < values.length; i++) {
values[i] = i;
}
return values;
}
function getValue(array) {
if (!array.length) {
throw new Error("array is empty, no more random values");
}
const i = Math.floor(Math.random() * array.length);
const returnVal = array[i];
array.splice(i, 1);
return returnVal;
}
// sample code to use it
const rands = initValues(10000);
console.log(getValue(rands));
console.log(getValue(rands));
console.log(getValue(rands));
console.log(getValue(rands));
This works by doing the following:
Generate an array of all possible values.
When you need a value, select one from the array with a random index.
After selecting the value, remove it from the array.
Return the selected value.
Items are never repeated because they are removed from the array when used.
There are no collisions with used values because you're always just selecting a random value from the remaining unused values.
This relies on the fact that an array of integers is pretty well optimized in Javascript so doing a .splice() on a 10,000 element array is still pretty fast (as it can probably just be memmove instructions).
FYI, this could be made more memory efficient by using a typed array since your numbers can be represented in 16-bit values (instead of the default 64 bits for doubles). But, you'd have to implement your own version of .splice() and keep track of the length yourself since typed arrays don't have these capabilities built in.
For even larger problems like this where memory usage becomes a problem, I've used a BitArray to keep track of previous usage of values.
Here's a class implementation of the same functionality:
class Randoms {
constructor(numValues) {
this.values = new Array(numValues);
for (let i = 0; i < this.values.length; i++) {
this.values[i] = i;
}
}
getRandomValue() {
if (!this.values.length) {
throw new Error("no more random values");
}
const i = Math.floor(Math.random() * this.values.length);
const returnVal = this.values[i];
this.values.splice(i, 1);
return returnVal;
}
}
const rands = new Randoms(10000);
console.log(rands.getRandomValue());
console.log(rands.getRandomValue());
console.log(rands.getRandomValue());
console.log(rands.getRandomValue());
Knuth's multiplicative method looks to work pretty well: it'll map numbers 0 to 9999 to a random-looking other number 0 to 9999, with no overlap:
const hash = i => i*2654435761 % (10000);
const s = new Set();
for (let i = 0; i < 10000; i++) {
const n = hash(i);
if (s.has(n)) { console.log(i, n); break; }
s.add(n);
}
To implement it, simply keep track of an index that gets incremented each time a new one is generated:
const hash = i => i*2654435761 % (10000);
let i = 1;
console.log(
hash(i++),
hash(i++),
hash(i++),
hash(i++),
hash(i++),
);
These results aren't actually random, but they probably do the job well enough for most purposes.
Disclaimer:
This is copy-paste from my answer to another question here. The code was in turn ported from yet another question here.
Utilities:
function isPrime(n) {
if (n <= 1) return false;
if (n <= 3) return true;
if (n % 2 == 0 || n % 3 == 0) return false;
for (let i = 5; i * i <= n; i = i + 6) {
if (n % i == 0 || n % (i + 2) == 0) return false;
}
return true;
}
function findNextPrime(n) {
if (n <= 1) return 2;
let prime = n;
while (true) {
prime++;
if (isPrime(prime)) return prime;
}
}
function getIndexGeneratorParams(spaceSize) {
const N = spaceSize;
const Q = findNextPrime(Math.floor(2 * N / (1 + Math.sqrt(5))))
const firstIndex = Math.floor(Math.random() * spaceSize);
return [firstIndex, N, Q]
}
function getNextIndex(prevIndex, N, Q) {
return (prevIndex + Q) % N
}
Usage
// Each day you bootstrap to get a tuple of these parameters and persist them throughout the day.
const [firstIndex, N, Q] = getIndexGeneratorParams(10000)
// need to keep track of previous index generated.
// it’s a seed to generate next one.
let prevIndex = firstIndex
// calling this function gives you the unique code
function getHashCode() {
prevIndex = getNextIndex(prevIndex, N, Q)
return prevIndex.toString().padStart(4, "0")
}
console.log(getHashCode());
Explanation
For simplicity let’s say you want generate non-repeat numbers from 0 to 35 in random order. We get pseudo-randomness by polling a "full cycle iterator"†. The idea is simple:
have the indexes 0..35 layout in a circle, denote upperbound as N=36
decide a step size, denoted as Q (Q=23 in this case) given by this formula‡
Q = findNextPrime(Math.floor(2 * N / (1 + Math.sqrt(5))))
randomly decide a starting point, e.g. number 5
start generating seemingly random nextIndex from prevIndex, by
nextIndex = (prevIndex + Q) % N
So if we put 5 in we get (5 + 23) % 36 == 28. Put 28 in we get (28 + 23) % 36 == 15.
This process will go through every number in circle (jump back and forth among points on the circle), it will pick each number only once, without repeating. When we get back to our starting point 5, we know we've reach the end.
†: I'm not sure about this term, just quoting from this answer
‡: This formula only gives a nice step size that will make things look more "random", the only requirement for Q is it must be coprime to N
This problem is so small I think a simple solution is best. Build an ordered array of the 10k possible values & permute it at the start of each day. Give the k'th value to the k'th request that day.
It avoids the possible problem with your solution of having multiple collisions.
Given any number between 0 and 1, such as 0.84729347293923, is there a simple way to make it into 84729347293923 without string or regex manipulation? I can think of using a loop, which probably is no worse than using a string because it is O(n) with n being the number of digits. But is there a better way?
function getRandom() {
let r = Math.random();
while (Math.floor(r) !== r) r *= 10;
return r;
}
for (let i = 0; i < 10; i++)
console.log(getRandom());
Integers mod 1 = 0, non integers mod 1 != 0.
while ((r*=10) % 1);
Ok, just want to refactor my code (i realized that was bad so this is what i discovered to correctly get the value as you requested).
NOTE: As the question says that "given any number between 0 and 1", this solution only works for values between 0 and 1:
window.onload = ()=>{
function getLen(num){
let currentNumb = num;
let integratedArray = [];
let realLen = 0;
/*While the number is not an integer, we will multiply the copy of the original
*value by ten, and when the loop detects that the number is already an integer
*the while simply breaks, in this process we are storing each transformations
*of the number in an array called integratedArray*/
while(!(Number.isInteger(currentNumb))){
currentNumb *= 10;
integratedArray.push(currentNumb);
}
/*We iterate over the array and compare each value of the array with an operation
*in which the resultant value should be exactly the same as the actual item of the
*array, in the case that both are equal we assign the var realLen to i, and
*in case that the values were not the same, we simply breaks the loop, if the
*values are not the same, this indicates that we found the "trash numbers", so
*we simply skip them.*/
for(let i = 0; i < integratedArray.length; i++){
if(Math.floor(integratedArray[i]) === Math.floor(num * Math.pow(10, i + 1))){
realLen = i;
}else{
break;
}
}
return realLen;
}
//Get the float value of a number between 0 and 1 as an integer.
function getShiftedNumber(num){
//First we need the length to get the float part of the number as an integer
const len = getLen(num);
/*Once we have the length of the number we simply multiply the number by
*(10) ^ numberLength, this eliminates the comma (,), or point (.), and
*automatically transforms the number to an integer in this case a large integer*/
return num * (Math.pow(10, len));
}
console.log(getShiftedNumber(0.84729347293923));
}
So the explanation is the next:
Because we want to convert this number without using any string, regex or any another thing, first we need to get the length of the number, this is a bit hard to do without using string conversions... so i did the function getLen for this purpose.
In the function getLen, we have 3 variables:
currentNumb: This var is a copy of the original value (the original number), this value help us to found the length of the number and we can do some transforms to this value whitout changing the original reference of the number.
We need to multiply this value any times is needed to transform the number to an integer and then multiplyng this value by ten to ten.
with the help of a while (this method makes the number a false integer).
NOTE: I saw "False integer" because when i was making the tests i realized that in the number is being adding more digits than normal... (Very very strange), so this stupid but important thing makes neccesary the filter of these "trash numbers", so later we proccess them.
integratedArray: This array stores the values of the result of the first while operations, so the last number stored in this array is an integer, but this number is one of the "fake integers", so with this array we need to iterate later to compare what of those stored values are different to the original value multiplied by (10 * i + 1), so here is the hint:
In this case the first 12 values of this array are exactly the same with the operation of Math.floor(num * Math.pow(10, i + 1))), but in the 13th value of the array these values are not the same so... yes!, there are those "trash numbers" that we were searching for.
realLen: This is the variable where we will store the real length of the number converting the float part of this number in an integer.
Some binary search approach:
Its useless if avarage length < 8;
It contains floating point issues.
But hey it is O(log n) with tons of wasted side computations - i guess if one counts them its event worse than just plain multiplication.
I prefer #chiliNUT answer. One line stamp.
function floatToIntBinarySearch(number){
const max_safe_int_length = 16;
const powers = [
1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
10000000000,
100000000000,
1000000000000,
10000000000000,
100000000000000,
1000000000000000,
10000000000000000
]
let currentLength = 16
let step = 16
let _number = number * powers[currentLength]
while(_number % 1 != 0 || (_number % 10 | 0) == 0){
step /= 2
if( (_number % 10 | 0) == 0 && !(_number % 1 != 0)){
currentLength = currentLength - step;
} else {
currentLength = step + currentLength;
}
if(currentLength < 1 || currentLength > max_safe_int_length * 2) throw Error("length is weird: " + currentLength)
_number = number * powers[currentLength]
console.log(currentLength, _number)
if(Number.isNaN(_number)) throw Error("isNaN: " + ((number + "").length - 2) + " maybe greater than 16?")
}
return number * powers[currentLength]
}
let randomPower = 10 ** (Math.random() * 10 | 0)
let test = (Math.random() * randomPower | 0) / randomPower
console.log(test)
console.log(floatToIntBinarySearch(test))
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Generating random numbers in Javascript in a specific range?
How can i get a random value between, for example, from -99 to 99, excluding 0?
var num = Math.floor(Math.random()*99) + 1; // this will get a number between 1 and 99;
num *= Math.round(Math.random()) ? 1 : -1; // this will add minus sign in 50% of cases
Altogether
var ranNum = Math.ceil(Math.random() * 99) * (Math.round(Math.random()) ? 1 : -1)
This returns what you want
function getNonZeroRandomNumber(){
var random = Math.floor(Math.random()*199) - 99;
if(random==0) return getNonZeroRandomNumber();
return random;
}
Here's a functional fiddle
EDIT
To contribute for future readers with a little debate happened in the comments which the user #MarkDickinson made a indeed relevant contribution to my first code posted, I've decided to make another fiddle with a fast comparison between using Math.floor() and Math.round() functions to return the value the op wanted.
First Scenario: Using var random = Math.round(Math.random()*198) - 99; (My first suggestion)
function getNonZeroRandomNumberWithMathRound(){
var random = Math.round(Math.random()*198) - 99;
if(random==0) return getNonZeroRandomNumber();
return random;
}
Second scenario: Using var random=Math.floor(Math.random()*199) - 99; (Mark suggestion)
function getNonZeroRandomNumberWithMathFloor(){
var random = Math.floor(Math.random()*199) - 99;
if(random==0) return getNonZeroRandomNumber();
return random;
}
Methodology
Since it's a short debate I've chosen fiddle.net to do the comparison.
The test consists of running the above functions 100.000 times and then retrieving how much times the extreme numbers 99 and -99 would appear against a other number, let's say 33 and -33.
The test will then give a simple output consisting of the percentage of appearances from 99 and -99 and the percentage of appearances of 33 and -33.
It'll be used the Webkit implementation from Safari 6.0.2 to the give the output from this answer but anyone can test with your favourite browser late on fiddle.net
Result from first scenario:
Percentage of normal ocurrences:0.97%
Percentage of extreme ocurrences:0.52%
Percentage of extreme ocurrences relative to normal ocurrences:53.4% // Half the chances indeed
Result from second scenario:
Percentage of normal ocurrences:1.052%
Percentage of extreme ocurrences:0.974%
Percentage of extreme ocurrences relative to normal ocurrences:92% //Closer of a fair result with a minimal standard deviation
The result can be seen here: http://jsfiddle.net/brunovieira/LrXqh/
Here's a generalized solution that will let you set the boundaries, and opt in/out of including the 0.
var pos = 99,
neg = 99,
includeZero = false,
result;
do result = Math.ceil(Math.random() * (pos + neg)) - neg;
while (includeZero === false && result === 0);
The pos and neg values are inclusive.
This way there's no requirement that the positive and negative ranges be balanced.
Or if you're worried about the rerun due to a single excluded value, you can just make the initial range less by one, and add 1 to any result greater than or equal to 0.
var pos = 5,
neg = 5,
result;
result = Math.floor(Math.random() * (pos + neg)) - neg;
result = result < 0 ? result : result + 1;
That last line could be shorter if you prefer:
result += (result >= 0)
Lets say I have a list of numbers in the following form(Ignore the | they are there for formating help).
00|00|xx
00|xx|00
xx|00|00
etc.
Rules: XX can be any number between 1 and 50. No XX values can be identical.
Now I select a random set of numbers(no duplicates) from a list qualifying the above format, and randomly add and subtract them. For example
000011 - 002400 - 230000 = -232389
How can I determine the original numbers and if they were added or subtracted solely from -232389? I'm stumped.
Thanks!
EDIT:
I was looking for a function so I ended up having to make one. Its just a proof of concept function so variables names are ugly http://jsfiddle.net/jPW8A/.
There are bugs in the following implementation, and it fails to work in a dozen of scenarios. Check the selected answer below.
function reverse_add_subtract(num){
var nums = [];
while(num != 0){
var str = num.toString(),
L = Math.abs(num).toString().length,
MA = str.match(/^(-?[0-9]?[0-9])([0-9][0-9])([0-9][0-9])*$/);
if(MA){
var num1 = MA[1],
num2 = MA[2];
}else{
var num1 = num,
num2 = 0;
}
if(L%2)L++;
if( num2 > 50){
if(num < 0) num1--;
else num1++;
}
nums.push(num1);
var add = parseInt(num1 + Array(--L).join(0),10);
num = (num-add);
}
return nums;
}
reverse_add_subtract(-122436);
First note that each xx group is constrained from [1, 50). This implies that each associated pair in the number that is in the range [50, 99) is really 100 - xx and this means that it "borrowed from" the group to the left. (It also means that there is only one set of normalized numbers and one solution, if any.)
So given the input 23|23|89 (the initial xx spots from -232389), normalize it -- that is, starting from the right, if the value is >= 50, get 100 - value and carry the 100 rightward (must balance). Example: (23 * 100) + 89 = 2300 * 89 = 2400 - 11 = 2389. And example that shows that it doesn't matter if it's negative as the only things that change is the signs: (-23 * 100) - 89 = -2300 - 89 = -2400 + 11 = -2389
(Notes: Remember, 1 is added to the 23 group to make it 24: the sign of the groups is not actually considered in this step, the math is just to show an example that it's okay to do! It may be possible to use this step to determine the sign and avoid extra math below, but this solution just tries to find the candidate numbers at this step. If there are any repeats of the number groups after this step then there is no solution; otherwise a solution exists.)
The candidate numbers after the normalization are then 23|24|11 (let's say this is aa|bb|cc, for below). All the xx values are now known and it is just a matter of finding the combination such that e * (aa * 10000) + f * (bb * 100) + g * (cc * 1) = -232389. The values aa, bb, cc are known from above and e, f, and g will be either 1 or -1, respectively.
Solution Warning: A method of finding the addition or subtraction given the determined numbers (determined above) is provided below the horizontal separator. Take a break and reflect on the above sections before deciding if the extra "hints" are required.
This can then be solved by utilizing the fact that all the xx groups are not dependent after the normalization. (At each step, try to make the input number for the next step approach zero.)
Example:
-232389 + (23 * 10000) = -2389 (e is -1 because that undoes the + we just did)
-2389 + (24 * 100) = 11 (likewise, f is -1)
11 - (11 * 1) = 0 (0 = win! g is 1 and solution is (-1 * 23 * 10000) + (-1 * 24 * 100) + (1 * 11 * 1) = -232389)
Happy homeworking.
First, your math is wrong. Your leading zeros are converting the first two numbers to octal. If that is the intent, the rest of this post doesn't exactly apply but may be able to be adapted.
11-2400-230000 = -232389
Now the last number is easy, it's always the first two digits, 23 in this case. Remove that:
-232389 + 230000 = -2389
Your 2nd number is the next 100 below this, -2400 in this case. And your final number is simply:
-2389 + 2400 = 11
Aww! Someone posted an answer saying "brute force it" that I was about to respond to with:
function find(num){for(var i=1;i<50;i++){for(var o1=0;o1<2;o1++){for(var j=1;j<50;j++){for(var o2=0;o2<2;o2++){for(var k=1;k<50;k++){var eq;if(eval(eq=(i+(o1?'+':'-')+j+'00'+(o2?'+':'-')+k+'0000'))==num){ return eq; }}}}}}}
they deleted it... :(
It was going to go in the comment, but here's a cleaner format:
function find(num){
for(var i=1;i<50;i++){
for(var o1=0;o1<2;o1++){
for(var j=1;j<50;j++){
for(var o2=0;o2<2;o2++){
for(var k=1;k<50;k++){
var eq;
if(eval(eq=(i+(o1?'+':'-')+j+'00'+(o2?'+':'-')+k+'0000'))==num){ return eq; }
}
}
}
}
}
}
For example, getting "5" in "256". The closest I've gotten is Math.floor(256/10)), but that'll still return the numbers in front. Is there any simple way to get what I want or would I have to make a big function for it? Also, for clarity: "n digit" would be defined. Example, getDigit(2,256) would return 5 (second digit)
Math.floor((256 / 10) % 10)
or more generally:
Math.floor(N / (Math.pow(10, n)) % 10)
where N is the number to be extracted, and n is the position of the digit. Note that this counts from 0 starting from the right (i.e., the least significant digit = 0), and doesn't account for invalid values of n.
how about
(12345 + "")[3]
or
(12345 + "").charAt(3)
to count from the other end
[length of string - digit you want] so if you want the 2 it's:
5 - 4 = 1
(12345 + "")[1] = "2"
function getNumber (var num, var pos){
var sNum = num + "";
if(pos > sNum.length || pos <= 0){return "";}
return sNum[sNum.length - pos];
}
First, you need to cast the number to a string, then you can access the character as normal:
var num = 256;
var char = num.toString()[1]; // get the 2nd (0-based index) character from the stringified version of num
Edit: Note also that, if you want to access it without setting the number as a variable first, you need a double dot .. to access the function:
var char = 256..toString()[1];
The first dot tells the interpreter "this is a number"; the second accesses the function.
Convert to string and substring(2,2)?
This should do it:
function getDigit ( position, number ) {
number = number + ""; // convert number to string
return number.substr ( position + 1, 1 ); // I'm adding 1 to position, since 0 is the position of the first character and so on
}
Try this, last line is key:
var number = 12345;
var n = 2;
var nDigit = parseInt((number + '').substr(1,1));
If you want to try to do everything mathematically:
var number = 256;
var digitNum = 2;
var digit = ((int)(number/(Math.pow(10,digitNum-1))%10;
This code counts the digit from the right starting with 1, not 0. If you wish to change it to start at 0, delete the -1 portion in the call.
If you wish to count from the left, it gets more complicated and similar to other solutions:
var number = 256;
var digitNum = 2;
var digit = ((int)(number/(Math.pow(10,number.tostring().length-digitNum))%10;
edit:
Also, this assumes you want base 10 for your number system, but both of those will work with other bases. All you need to do is change instances of 10 in the final line of code to the number representing the base for the number system you'd like to use. (ie. hexadecimal =16, binary = 2)
// You do not say if you allow decimal fractions or negative numbers-
// the strings of those need adjusting.
Number.prototype.nthDigit= function(n){
var s= String(this).replace(/\D+/g,'');
if(s.length<=n) return null;
return Number(s.charAt(n))
}
use variable "count" to control loop
var count = 1; //starting 1
for(i=0; i<100; i++){
console.log(count);
if(i%10 == 0) count++;
}
output will fill
1
2
3
4
5
6
7
8
9