Assume we have an integer 16.
Is there a function, that returns random array of numbers, which compose its sum?
For example 7 1 2 4 1 1 or 1 5 2 3 6
I wonder if some elegant method of doing this in JavaScript exists.
No there's not existing function, but e.g.:
var n = 16;
var a = [];
while (n > 0) {
var s = Math.round(Math.random()*n);
a.push(s);
n -= s;
}
a contains the array.
you can consider this method too
function getRandomInt(max) {
return Math.floor(Math.random() * max + 1);
}
const total = 100;
const max = 20;
const nbrounds = 9;
function fillWithRandom(max, total, len) {
let arr = new Array();
let sum = 0;
newmax = max;
do {
newtotal = total - sum;
//max depending on length
console.log(arr.length,len);
if (arr.length+1 == len) {
arr.push(newtotal);
} else {
maxbylen = parseInt(newtotal / (len - arr.length));
// console.log('maxbylen', maxbylen, arr.length);
if (max > maxbylen) {
rndmax = max;
} else {
rndmax = maxbylen;
}
if (newtotal > max) {
rnd = getRandomInt(rndmax);
} else {
rnd = getRandomInt(newtotal);
}
arr.push(rnd);
}
sum = arr.reduce((acc, val) => acc + val, 0);
// console.log('sum', sum, 'newtotal', newtotal, 'rnd', rnd, arr);
} while (sum < total);
// console.log(arr);
//random order
return arr.map((value) => ({value, sort: Math.random()})).sort((a, b) => a.sort - b.sort).map(({ value }) => value);
}
;
console.log(fillWithRandom(max, total, nbrounds));
Related
I am working through a two-sum problem where I pass in an unsorted array, and a target, k, and I return the the highest sum of any two numbers that are less than k. If there's no possible sum less than k, then return -1.
I think I am on the right path by sorting the array and then using a 2-pointer technique but I am stuck now. If my sum of numbers is greater than the target, then I decrement the end pointer...that seems definitive. The else though, I am not sure if I am doing correctly.
var twoSumLessThanK = function(nums, k) {
// [1,8,23,23,33,34,54,75] 60
nums.sort((a, b) => a - b)
let start = 0;
let end = 0;
let max = -1;
while (start < end) {
if (nums[start] + nums[end] >= k) {
end--
} else if (nums[start] + nums[end] < k) {
max = Math.max(max, nums[start] + nums[end])
start++
}
}
return max;
};
console.log(twoSumLessThanK([1,8,23,23,33,34,54,75], 60));
You could check the sum of two values and decrement the right index if greater or equal than k or store the sum, if greater than the max value and increment the left index.
1 8 23 23 33 34 54 75 sum max < 60
> < 76
> < 55 -> max
> < 62
> < 42
> < 57 -> max
> < 57
> < 67
const
twoSumLessThanK = function(nums, k) {
nums.sort((a, b) => a - b);
let left = 0,
right = nums.length -1,
max = -Number.MAX_VALUE;
while (left < right) {
let sum = nums[left] + nums[right];
if (sum >= k) {
right--;
continue;
}
if (max < sum) max = sum;
left++;
}
return max;
};
console.log(twoSumLessThanK([1, 8, 23, 23, 33, 34, 54, 75], 60));
An alternative could be a nested for loop, this way you do't have to handle start and end manually
const twoSumLessThanK = function(nums, k)
{
let max = -1;
const len = nums.length - 1
for (let start = 0; start < len; start++)
{
for (let end = len; end > start; end--)
{
if (nums[start] + nums[end] < k) max = Math.max(max, nums[start] + nums[end])
}
}
return max;
};
console.log(twoSumLessThanK([1,8,23,23,33,34,54,75], 60)); // Logs 57
A different option would be the following:
const twoSumLessThanK = function(nums, k)
{
nums.sort((a, b) => a - b)
let max = -1;
let greatest = null
while (nums.length > 1 && max < k - 1)
{
greatest = nums.pop()
for (let i = nums.length - 1; i >= 0; i--)
{
if (greatest + nums[i] < k)
{
max = Math.max(max, greatest + nums[i])
break
}
}
}
return max;
};
Or you can just fix your version if you want:
const twoSumLessThanK = function(nums, k)
{
// [1,8,23,23,33,34,54,75] 60
nums.sort((a, b) => a - b)
const len = nums.length - 1
let start = 0;
let max = -1;
let end = len;
while (start < len)
{
if (nums[start] + nums[end] < k)
{
max = Math.max(max, nums[start] + nums[end])
start++
end = len;
}
else end--
}
return max;
};
it wasn't so hard to code...?
const twoSumLessThanK = (nums, k) =>
{
let max = -1
, arr = nums.reduce((a,c)=> // decrase order values < k
{
if (c < k)
{
let p = a.findIndex(x=>x < c)
if (p<0) p = a.length
a.splice( p,0,c)
}
return a
}
,[])
;
if (arr.length<2) return max
for(i=arr.length;--i;)
for(j=i;j--;)
{
let sum = arr[i] + arr[j]
if (sum >= k && i === j+1) return max
if (sum < k && sum > max ) max = sum
}
return max
}
console.log(twoSumLessThanK([33,1,8,23,23,34,54,75], 60))
I'm trying to calculate the quantity of the ingredients by the serve count. But, the counter i have implemented is not supporting fraction values such as 3/4 teaspoon, 2 1/2 teaspoon. Here is the sample code:
var gcd = function(a, b) {
if (b < 0.0000001) return a;
return gcd(b, Math.floor(a % b));
};
const decimalToFraction = ( ) => {
var fraction = result;
var len = fraction.toString().length - 2;
var denominator = Math.pow(10, len);
var numerator = fraction * denominator;
var divisor = gcd(numerator, denominator);
numerator /= divisor;
denominator /= divisor;
console.log(Math.floor(numerator) + '/' + Math.floor(denominator));
}
const stringToDecimal = (parseQuantity) => {
let split = parseQuantity.split('/');
let result = parseInt(split[0], 10) / parseInt(split[1], 10);
return result;
}
const serveQuantity = (count) =>{
let listOfIngredients=[];
if(count<1 && serve >= 1){
setServe(serve => serve-1);
listOfIngredients =ingredients.map((item,index)=>{
if(quantity[index] < 1){
item.quantity._text = item.quantity._text - parseInt(quantity[index]);
}else{
item.quantity._text = parseInt(item.quantity._text) - parseInt(quantity[index]);
}
return item;
})
}
if(count===1 && serve >= 1){
setServe(serve => serve+1);
listOfIngredients = ingredients.map((item, index)=>{
if(quantity[index] < 1){
item.quantity._text = item.quantity._text + parseInt(quantity[index]);
}else{
item.quantity._text = parseInt(item.quantity._text) + parseInt(quantity[index]);
}
return item;
})
}
setIngredients(listOfIngredients);
}
when the user clicks on the serve +/- button, i'm calling serveQuantity(2) function.
I'm generating a number based on a fixed character set.
function generator()
{
var text = "";
var char_list = "LEDGJR", number_list = "0123456789";
for(var i=0; i < 2; i++ )
{
text += char_list.charAt(Math.floor(Math.random() * char_list.length));
}
for(var j=0; j < 2; j++ )
{
text += number_list.charAt(Math.floor(Math.random() *
number_list.length));
}
return text;
}
Result :
RE39, JE12 etc...
Once all the permutation related to the above sequence is done, then the generator should generate string as RE391, JE125 means adding one more number to the complete number.
How can I get the permutation count of sequence?
For simplicity consider the case where:
chars = "AB"
nums = "123";
and we want to generate a 4-digit sequence of two chars and two numbers.
We define these variables
rows = [chars, chars, nums, nums]
rowSizes = rows.map(row => row.length) // [2, 2, 3, 3]
It’s easy to see the set size of all possible permuations equals:
spaceSize = rowSizes.reduce((m, n) => m * n, 1) // 2*2*3*3 = 36
And we define two set of utility functions, usage of which I'll explain in detail later.
decodeIndex() which gives us uniqueness
function euclideanDivision(a, b) {
const remainder = a % b;
const quotient = (a - remainder) / b;
return [quotient, remainder]
}
function decodeIndex(index, rowSizes) {
const rowIndexes = []
let dividend = index
for (let i = 0; i < rowSizes.length; i++) {
const [quotient, remainder] = euclideanDivision(dividend, rowSizes[i])
rowIndexes[i] = remainder
dividend = quotient
}
return rowIndexes
}
getNextIndex() which gives us pseudo-randomness
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
}
Uniqueness
Like mentioned above, spaceSize is the number of all possible permutations, thus each index in range(0, spaceSize) uniquely maps to one permutation. decodeIndex helps with this mapping, you can get the corresponding permutation to an index by:
function getSequenceAtIndex(index) {
const tuple = decodeIndex(index, rowSizes)
return rows.map((row, i) => row[tuple[i]]).join('')
}
Pseudo-Randomness
(Credit to this question. I just port that code into JS.)
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
Full Solution
Now let's put all the pieces together. Run the snippet to see result.
I've also includes the a counter before each log. For your case with char_list="LEDGJR", number_list="0123456789", the spaceSize for 4-digit sequence should be 6*6*10*10 = 3600
You'll observe the log bump to 5-digit sequence at 3601 😉
function euclideanDivision(a, b) {
const remainder = a % b;
const quotient = (a - remainder) / b;
return [quotient, remainder];
}
function decodeIndex(index, rowSizes) {
const rowIndexes = [];
let divident = index;
for (let i = 0; i < rowSizes.length; i++) {
const [quotient, remainder] = euclideanDivision(divident, rowSizes[i]);
rowIndexes[i] = remainder;
divident = quotient;
}
return rowIndexes;
}
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;
}
function generatorFactory(rows) {
const rowSizes = rows.map((row) => row.length);
function getSequenceAtIndex(index) {
const tuple = decodeIndex(index, rowSizes);
return rows.map((row, i) => row[tuple[i]]).join("");
}
const spaceSize = rowSizes.reduce((m, n) => m * n, 1);
const [firstIndex, N, Q] = getIndexGeneratorParams(spaceSize);
let currentIndex = firstIndex;
let exhausted = false;
function generator() {
if (exhausted) return null;
const sequence = getSequenceAtIndex(currentIndex);
currentIndex = getNextIndex(currentIndex, N, Q);
if (currentIndex === firstIndex) exhausted = true;
return sequence;
}
return generator;
}
function getRows(chars, nums, rowsOfChars, rowsOfNums) {
const rows = [];
while (rowsOfChars--) {
rows.push(chars);
}
while (rowsOfNums--) {
rows.push(nums);
}
return rows;
}
function autoRenewGeneratorFactory(chars, nums, initRowsOfChars, initRowsOfNums) {
let realGenerator;
let currentRowOfNums = initRowsOfNums;
function createRealGenerator() {
const rows = getRows(chars, nums, initRowsOfChars, currentRowOfNums);
const generator = generatorFactory(rows);
currentRowOfNums++;
return generator;
}
realGenerator = createRealGenerator();
function proxyGenerator() {
const sequence = realGenerator();
if (sequence === null) {
realGenerator = createRealGenerator();
return realGenerator();
} else {
return sequence;
}
}
return proxyGenerator;
}
function main() {
const char_list = "LEDGJR"
const number_list = "0123456789";
const generator = autoRenewGeneratorFactory(char_list, number_list, 2, 2);
let couter = 0
setInterval(() => {
console.log(++couter, generator())
}, 10);
}
main();
I am trying to create a function that generates a random number from a given interval, but I want to be able to generate only 3 identical consecutive numbers. For example, if a have the interval [0,4) I want:
Accepted: 1, 2, 2, 2, 1, 0
Not accepted: 1, 2, 2, 2, 2, 3, 0
I've found on multiple threads functions that generates a different number than the previous one, but I don't know how to change it to accomplish what I need. Any thoughts?
You must completely reset your counter, when you generate a different number. Not just decrease it.
function setRandomInterval(min, max, allowedRepeats) {
var last, // keeping the last random value
repeatCount = 0, // count of repeated value
getR = function () { return Math.floor(Math.random() * (max - min)) + min; };
if (min >= max) {
throw 'Selected interval [' + min + ', ' + max + ') does not work for random numbers.';
}
return function () {
var r = getR();
if (r != last) {
repeatCount = 0; //no repeat yet
} else if (repeatCount < allowedRepeats) { //new number is equal to last one, but it's still ok
repeatCount++; //just increase the number of repeats
} else { //new number is equal to last, and allowed number of repeats is reached
while (r == last) { //must create a different number
r = getR();
}
repeatCount = 0; //reset the repeatCount
}
return last = r; //save r as last number and return it
};
}
var getRandom = setRandomInterval(0, 4, 2); //call with 2 allowed repeats to allow a sequence of three equal numbers
Try this
function randomBetween(min, max, limit = 3) {
if (min > max) {
[max, min] = [min, max];
}
function getBetween(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
let last;
let count = 0;
return function generate() {
const result = getBetween(min, max);
count = (result === last) ? count + 1 : 0;
if (count > limit) {
return generate();
}
last = result;
return result;
};
}
Here's a snippet that prevent from the same number to randomize more than 3 times. If the count of the number is greater than the limit the randomNumber is called again (and so on until it won't get the same value.
You can see (when running it) that the largest sequence is 3.
const limit = 3;
let last = null,
count = 0;
function getRndInteger(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
function randomNumber(min, max) {
let num = getRndInteger(min, max);
if (last !== num) {
last = num;
count = 0;
}
count++;
if (count > limit) {
num = randomNumber(min, max);
}
return num;
}
for (let i = 0; i < 20; i++)
console.log(randomNumber(0, 2));
Here is an approach that's uses a nice feature of modern JS. Generators..
Using generators you can create composable code. Instead of creating 1 function that does this one specific thing, you can compose a function than stream together multiple generators. This is great for making re-usable code.
Below I've created 3 function,.
randomValues => this just generates random number between a range.
repeatLimit => this generator just prevents x amount of repeated values
iterCount => this generator stop after count iterations
All you do then is compose all these generators together, but the bonus is that these generators can be used in other places. eg. [...repeatLimit(3, [1,1,1,1, 0,0,0,0])] would return 1,1,1,0,0,0.
function* randValues(min, max) {
const range = max - min;
while (true)
yield min + Math.trunc(Math.random() * range);
}
function* repeatLimit(limit, gen) {
let rcount = 0, rvalue;
for (let g of gen) {
if (rvalue === g) rcount += 1; else rcount = 1;
rvalue = g;
if (rcount <= limit) yield g;
}
}
function* iterCount(count, gen) {
for (let g of gen) {
yield g;
count --;
if (count <= 0) break;
}
}
const gen =
iterCount(22, repeatLimit(3, randValues(0, 2)));
for (const r of gen) {
console.log(r);
}
The expected result of:
(1.175).toFixed(2) = 1.18 and
(5.175).toFixed(2) = 5.18
But in JS showing:
(1.175).toFixed(2) = 1.18 but
*(5.175).toFixed(2) = 5.17*
How to rectify the problem?
It's not a bug. It's related to the fact numbers aren't stored in decimal but in IEEE754 (so 5.175 isn't exactly stored).
If you want to round in a specific direction (up) and you consistently have numbers of this precision, you might use this trick :
(5.175 + 0.00001).toFixed(2)
You could always try using round, instead of toFixed.
Math.round(5.175*100)/100
You could even try putting it in some prototype method if you want.
Created a jsBin that implements a simple prototype on Number.
Number.prototype.toFixed = function(decimals) {
return Math.round(this * Math.pow(10, decimals)) / (Math.pow(10, decimals));
};
It's because the numbers are stored as IEEE754.
I would recommend you to use the Math class (round, floor or ceil methods, depending on your needing).
I've just created a class MathHelper that can easily solve your problem:
var MathHelper = (function () {
this.round = function (number, numberOfDecimals) {
var aux = Math.pow(10, numberOfDecimals);
return Math.round(number * aux) / aux;
};
this.floor = function (number, numberOfDecimals) {
var aux = Math.pow(10, numberOfDecimals);
return Math.floor(number * aux) / aux;
};
this.ceil = function (number, numberOfDecimals) {
var aux = Math.pow(10, numberOfDecimals);
return Math.ceil(number * aux) / aux;
};
return {
round: round,
floor: floor,
ceil: ceil
}
})();
Usage:
MathHelper.round(5.175, 2)
Demo: http://jsfiddle.net/v2Dj7/
Actually I think this is a bug in the implementation of Number.prototype.toFixed. The algorithm given in ECMA-262 20.1.3.3 10-a says to round up as a tie-breaker. As others have mentioned, there probably isn't a tie to break due to floating point imprecisions in the implementation. But that doesn't make it right :)
At least this behavior is consistent across FF, Chrome, Opera, Safari. (Haven't tried others.)
FWIW, you can actually implement your own version of toFixed per the spec in JS and that behaves as you would expect. See http://jsfiddle.net/joenudell/7qahrb6d/.
Kippie
your solution has problems
one of them
39133.005.toFixed(2) => 39133
var Calc = function () {
var self = this;
this.float2Array = function(num) {
var floatString = num.toString(),
exp = floatString.indexOf(".") - (floatString.length - 1),
mant = floatString.replace(".", "").split("").map(function (i) { return parseInt(i); });
return { exp: exp, mant: mant };
};
this.round2 = function (num, dec, sep) {
var decimal = !!dec ? dec : 2,
separator = !!sep ? sep : '',
r = parseFloat(num),
exp10 = Math.pow(10, decimal);
r = Math.round(r * exp10) / exp10;
var rr = Number(r).toFixed(decimal).toString().split('.');
var b = rr[0].replace(/(\d{1,3}(?=(\d{3})+(?:\.\d|\b)))/g, "\$1" + separator);
r = (rr[1] ? b + '.' + rr[1] : b);
return r;
};
this.toFixed10 = function (f, num) {
var prepareInt = self.float2Array(f),
naturalInt = prepareInt.mant,
places = Math.abs(prepareInt.exp),
result = prepareInt.mant.slice(),
resultFixedLenth;
// if number non fractional or has zero fractional part
if (f.isInt()) {
return f.toFixed(num);
}
// if the number of decimal places equals to required rounding
if (places === num) {
return Number(f).toString();
}
//if number has trailing zero (when casting to string type float 1.0050 => "1.005" => 005 <0050)
if (places < num) {
return Number(f).round2(num);
}
for (var e = naturalInt.length - (places > num ? (places - num) : 0), s = 0; e >= s; e--) {
if (naturalInt.hasOwnProperty(e)) {
if (naturalInt[e] > 4 && naturalInt[e - 1] < 9) {
result[e] = 0;
result[e - 1] = naturalInt[e - 1] + 1;
break;
} else if (naturalInt[e] > 4 && naturalInt[e - 1] === 9) {
result[e] = 0;
result[e - 1] = 0;
result[e - 2] = naturalInt[e - 2] < 9 ? naturalInt[e - 2] + 1 : 0;
} else if (e === 0 && naturalInt[e] === 9 && naturalInt[e + 1] === 9) {
result[e] = 0;
result.unshift(1);
} else {
break;
}
}
}
if (places - num > 0) {
resultFixedLenth = result.slice(0, -(places - num));
} else {
for (var i = 0, l = num - places; i < l; i++) {
result.push(0);
}
resultFixedLenth = result;
}
return (parseInt(resultFixedLenth.join("")) / Math.pow(10, num)).round2(num);
};
this.polyfill = function() {
if (!Array.prototype.map) {
Array.prototype.map = function (callback, thisArg) {
var T, A, k;
if (this == null) { throw new TypeError(' this is null or not defined'); }
var O = Object(this), len = O.length >>> 0;
if (typeof callback !== 'function') { throw new TypeError(callback + ' is not a function'); }
if (arguments.length > 1) { T = thisArg; }
A = new Array(len);
k = 0;
while (k < len) {
var kValue, mappedValue;
if (k in O) {
kValue = O[k];
mappedValue = callback.call(T, kValue, k, O);
A[k] = mappedValue;
}
k++;
}
return A;
};
}
};
this.init = function () {
self.polyfill();
Number.prototype.toFixed10 = function (decimal) {
return calc.toFixed10(this, decimal);
}
Number.prototype.round2 = function (decimal) {
return calc.round2(this, decimal);
}
Number.prototype.isInt = function () {
return (Math.round(this) == this);
}
}
}, calc = new Calc(); calc.init();
this works good)
obj = {
round(val) {
const delta = 0.00001
let num = val
if (num - Math.floor(num) === 0.5) {
num += delta
}
return Math.round(num + delta)
},
fixed(val, count = 0) {
const power = Math.pow(10, count)
let res = this.round(val * power) / power
let arr = `${res}`.split('.')
let addZero = ''
for (let i = 0; i < count; i++) {
addZero += '0'
}
if (count > 0) {
arr[1] = ((arr[1] || '') + addZero).substr(0, count)
}
return arr.join('.')
}
}
obj.fixed(5.175, 2)
// "5.18"