find minimum numbers of currency for required amount - javascript

I made a little program to find minimum number of notes(currency) for the required amount. for example, let's say I enter an amount 1121 and I have an array of these values below:
notes = [1, 2, 5, 10, 50, 100, 200, 500] so my final result will be:
500 * 2(notes) = 1000
100 * 1 = 100
20 * 1 = 20
1 * 1 = 1
then the total would be 1121. any help with understanding would be much appreciated. I know it only takes one for loop but I'm confused in some parts.
here's what I did: https://stackblitz.com/edit/angular-rufwzk?file=src%2Fapp%2Fapp.component.ts

Here is the corrected code of what you want. I hope i get it right.
requiredNotes(amount) {
let noteArry = this.notes,
quotient,
remainder,
temp,
noteCount = 0,
eachNote,
remainingAmount = 0;
for (let i = noteArry.length - 1; i >= 0; i--) {
if (amount >= noteArry[i]) {
quotient = Math.floor(amount / noteArry[i]);
remainder = amount % noteArry[i];
amount = amount - (noteArry[i] * quotient);
if (amount == 0) {
console.log("note:-", noteArry[i], ",number of note", quotient);
break;
} else if (amount != 0) {
console.log("note:-", noteArry[i], ",number of note", quotient);
}
} else {
continue;
}
}
}

I simplified a little your code (using JS Map):
...
notesMap = new Map();
...
requiredNotes(amount) {
for (let i = this.notes.length - 1; i >= 0 && amount; i--) {
const qty = Math.floor(amount / this.notes[i]);
qty && this.notesMap.set(this.notes[i], qty);
amount = amount % this.notes[i];
}
const entries = Array.from(this.notesMap.entries());
this.requireNotes = entries.map(([curr, qty]) => `${curr} * ${qty} = ${curr * qty}`);
}
STACKBLITZ

for (let i = noteArry.length - 1; i >= 0; i--) {
if (amount >= noteArry[i]) {
quotient = Math.floor(amount / noteArry[i]);
remainder = amount % noteArry[i];
remainingAmount = noteArry[i] * quotient;
amount=amount-remainingAmount;
console.log('number of notes =', noteArry[i], 'x', quotient,' notes');
}
}
This does the trick simply logs out the number of notes for the mentioned amount.

Related

Split number into 4 random numbers

I want to split 10 into an array of 4 random numbers, but neither can be 0 or higher than 4. For example [1,2,3,4], [1,4,4,1] or [4,2,3,1].
I think it's an easy question, but for some reason I can't think of how to do this. If someone has some instruction that would be very helpful!
Edit:
This is the code I have now, but I generates also a total number under 10:
let formation = [];
let total = 0;
for (let i = 0; i < 4; i ++) {
if (total < 9) {
formation[i] = Math.floor(Math.random() * 4) + 1;
} else {
formation[i] = 1;
}
}
You could create all possible combinations and pick a random array.
function get4() {
function iter(temp) {
return function (v) {
var t = temp.concat(v);
if (t.length === 4) {
if (t.reduce(add) === 10) {
result.push(t);
}
return;
}
values.forEach(iter(t));
};
}
const
add = (a, b) => a + b,
values = [1, 2, 3, 4],
result = [];
values.forEach(iter([]));
return result;
}
console.log(get4().map(a => a.join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
An algorithm for getting random values without a list of all possible combinations
It works by using a factor for the random value and an offset, based on the actual sum, index, minimum sum which is needed for the next index, and the maximum sum.
The offset is usually the minimum sum, or the greater value of the difference of sum and maximum sum. For getting the factor, three values are taken for the minimum for multiplying the random value.
The table illustrates all possible values of the sum and the needed iterations, based on a given value and the iteration for getting all values.
At the beginning the sum is the value for distribution in small parts. The result is the second block with a rest sum of 14 ... 10, because it is possible to take a value of 1 ... 5. The third round follows the same rules. At the end, the leftover sum is taken as offset for the value.
An example with 1, ..., 5 values and 5 elements with a sum of 15 and all possibilities:
min: 1
max: 5
length: 5
sum: 15
smin = (length - index - 1) * min
smax = (length - index - 1) * max
offset = Math.max(sum - smax, min)
random = 1 + Math.min(sum - offset, max - offset, sum - smin - min)
index sum sum min sum max random offset
------- ------- ------- ------- ------- -------
_ 0 15 4 20 5 1
1 14 3 15 5 1
1 13 3 15 5 1
1 12 3 15 5 1
1 11 3 15 5 1
_ 1 10 3 15 5 1
2 13 2 10 3 3
2 12 2 10 4 2
2 11 2 10 5 1
2 10 2 10 5 1
2 9 2 10 5 1
2 8 2 10 5 1
2 7 2 10 5 1
2 6 2 10 4 1
_ 2 5 2 10 3 1
3 10 1 5 1 5
3 9 1 5 2 4
3 8 1 5 3 3
3 7 1 5 4 2
3 6 1 5 5 1
3 5 1 5 4 1
3 4 1 5 3 1
3 3 1 5 2 1
_ 3 2 1 5 1 1
4 5 0 0 1 5
4 4 0 0 1 4
4 3 0 0 1 3
4 2 0 0 1 2
4 1 0 0 1 1
The example code takes the target 1, ..., 4 with a length of 4 parts and a sum of 10.
function getRandom(min, max, length, sum) {
return Array.from(
{ length },
(_, i) => {
var smin = (length - i - 1) * min,
smax = (length - i - 1) * max,
offset = Math.max(sum - smax, min),
random = 1 + Math.min(sum - offset, max - offset, sum - smin - min),
value = Math.floor(Math.random() * random + offset);
sum -= value;
return value;
}
);
}
console.log(Array.from({ length: 10 }, _ => getRandom(1, 4, 4, 10).join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
The simplest solution is brute force.
Make a while loop to nest your calculations in
In the loop, create an empty array and fill it with random values until length is reached
Check if the sum of the array is your desired value, and if it is then break the loop
The above should run until you have a result.
Two things worth considering though.
Your can easily test if a solution is at all possible by calculating, that length-of-array times minimum-value isn't more than the sum and length-of-array times maximum-value isn't less than the sum.
A loop based on random conditions could potentially run forever, so a maximum amount of iterations might be desirable.
Both of these points are considered in the snippet below:
function randomNumber(max, min) {
while (true) {
var r = Math.round(Math.random() * max);
if (r >= min) {
return r;
}
}
}
function splitXintoYComponentsBetweenMaxAndMin(numberToSplit, numberOfSplits, maxValue, minValue, onUpdate) {
if (minValue === void 0) {
minValue = 1;
}
//Test that a result can exist
if (maxValue * numberOfSplits < numberToSplit || minValue * numberOfSplits > numberToSplit) {
return new Promise(function(resolve, reject) {
resolve(false);
});
}
//Create returner array
var arr = [];
var accumulator = 0;
while (arr.length < numberOfSplits) {
var val = randomNumber(Math.floor(numberToSplit / numberOfSplits), minValue);
accumulator += val;
arr.push(val);
}
return new Promise(function(resolve, reject) {
function runTest() {
var d = Date.now();
var localMaxValue = Math.min(maxValue, Math.ceil((numberToSplit - accumulator) / 4));
//Combination loop
while (accumulator < numberToSplit && Date.now() - d < 17) {
var index = Math.round(Math.random() * (arr.length - 1));
if (arr[index] >= maxValue) {
continue;
}
var r = randomNumber(localMaxValue, minValue);
while (arr[index] + r > maxValue || accumulator + r > numberToSplit) {
if (Date.now() - d >= 17) {
break;
}
r = randomNumber(localMaxValue, minValue);
}
if (arr[index] + r > maxValue || accumulator + r > numberToSplit) {
continue;
}
arr[index] += r;
accumulator += r;
}
if (accumulator < numberToSplit) {
if (onUpdate !== void 0) {
onUpdate(arr);
}
requestAnimationFrame(runTest);
} else {
resolve(arr);
}
}
runTest();
});
}
//TEST
var table = document.body.appendChild(document.createElement('table'));
table.innerHTML = "<thead><tr><th>Number to split</th><th>Number of splits</th><th>Max value</th><th>Min value</th><th>Run</th></tr></thead>" +
"<tbody><tr><th><input id=\"number-to-split\" value=\"10\" type=\"number\" min=\"1\"/></th><th><input id=\"number-of-splits\" value=\"4\" type=\"number\" min=\"1\"/></th><th><input id=\"max-value\" type=\"number\" min=\"1\" value=\"4\"/></th><th><input id=\"min-value\" type=\"number\" min=\"1\" value=\"1\"/></th><th><input id=\"run\" type=\"button\" value=\"Run\"/></th></tr></tbody>";
var output = document.body.appendChild(document.createElement('pre'));
output.style.overflowX = "scroll";
document.getElementById("run").onclick = function() {
splitXintoYComponentsBetweenMaxAndMin(parseInt(document.getElementById("number-to-split").value, 10), parseInt(document.getElementById("number-of-splits").value, 10), parseInt(document.getElementById("max-value").value, 10), parseInt(document.getElementById("min-value").value, 10))
.then(function(data) {
if (data !== false) {
output.textContent += data.join("\t") + '\n';
} else {
output.textContent += 'Invalid data\n';
}
});
};
EDIT 1 - Big calculations
Using requestAnimationFrame and Promises the code can now execute asynchronously, which allows for longer calculation time without bothering the user.
I also made the random function scale with the remaining range, greatly reducing the amount of calculations needed for big numbers.
A litte late to the show, but I found this a fun task to think about so here you go. My approach does not need to create all partitions, it also does not rely on pure luck of finding a random match, it is compact and it should be unbiased.
It works efficiently even when large values are used, as long as max is not too limiting.
const len = 4;
const total = 10;
const max = 4;
let arr = new Array(len);
let sum = 0;
do {
// get some random numbers
for (let i = 0; i < len; i++) {
arr[i] = Math.random();
}
// get the total of the random numbers
sum = arr.reduce((acc, val) => acc + val, 0);
// compute the scale to use on the numbers
const scale = (total - len) / sum;
// scale the array
arr = arr.map(val => Math.min(max, Math.round(val * scale) + 1));
// re-compute the sum
sum = arr.reduce((acc, val) => acc + val, 0);
// loop if the sum is not exactly the expected total due to scale rounding effects
} while (sum - total);
console.log(arr);
Basically you need the partitions (See https://en.wikipedia.org/wiki/Partition_(number_theory)) of 10 and apply your conditions on the resulting set.
// Partition generator taken from
// https://gist.github.com/k-hamada/8aa85ac9b334fb89ac4f
function* partitions(n) {
if (n <= 0) throw new Error('positive integer only');
yield [n];
var x = new Array(n);
x[0] = n;
for (var i = 1; i < n; i++) x[i] = 1;
var m = 0, h = 0, r, t;
while (x[0] != 1) {
if (x[h] == 2) {
m += 1;
x[h] = 1;
h -= 1;
} else {
r = x[h] - 1;
x[h] = r;
t = m - h + 1;
while (t >= r) {
h += 1;
x[h] = r;
t -= r;
}
m = h + (t !== 0 ? 1 : 0);
if (t > 1) {
h += 1;
x[h] = t;
}
}
yield x.slice(0, m + 1);
}
}
results = [];
// Get all possible partitions for your number
for (var partition of partitions(10)) {
// Apply your conditions (must be 4 numbers, none of them greater than 4)
if(partition.length != 4 || partition.some((x) => x > 4)) continue;
results.push(partition);
}
console.log(results);
Given that:
In a collection of n positive numbers that sum up to S, at least one of them will be less than S divided by n (S/n)
and that you want a result set of exactly 4 numbers,
you could use the following algorithm:
Get a random number from range [1, floor(S/n)], in this case floor(10/4) = 2, so get a random number in the range of [1,2]. Lets mark it as x1.
Get a random number from range [1, floor((S - x1)/(n - 1))]. Lets mark it as x2.
Get a random number from range [1, floor((S - x1 - x2)/(n - 2))].
Continue until you get x(n-1).
Get the last number by doing S - x1 - x2 .... - x(n-1).
Finally, extend the above algorithm with a condition to limit the upper limit of the random numbers.
In n steps, you can get a collection.
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function getRandomCollection(min, max, length, sum) {
var collection = [];
var leftSum = sum - (min - 1);
for(var i = 0; i < length - 1; i++) {
var number = getRandomInt(min, Math.min(Math.ceil(leftSum/(length - i)), max));
leftSum -= number;
collection.push(number);
}
leftSum += min - 1;
while(leftSum > max) {
var randomIndex = Math.floor(Math.random() * collection.length);
if(collection[randomIndex] < max) {
collection[randomIndex]++;
leftSum--;
}
}
collection.push(leftSum);
return collection;
}
console.log(getRandomCollection(1, 4, 4, 10).join(' + ') + ' = 10');
console.log(getRandomCollection(3, 20, 10, 100).join(' + ') + ' = 100');
Reference
My answer using the same algorithm for another question
Quick and simple but biased and nondeterministically terminating
function partition(sum, len, min, max) {
const a = Array(len).fill(min)
while (a.reduce((acc,val)=>acc+val) < sum) {
const i = Math.random()*len|0
if (a[i] < max) a[i]++
}
return a
}
console.log(Array(10).fill().map(_=>partition(10, 4, 1, 4).join(' ')))
.as-console-wrapper { max-height: 100% !important; top: 0; }
The while loop can loop forever with an infinitesimal probability. To prevent this, you can keep another array of "valid indexes" and delete keys of it when the value reaches max.
this calculates a random number from 1 to 4
wrap it on a function to your needs to generate the arrays
Math.floor(Math.random() * 4) + 1
var randomNumber = Math.floor(Math.random() * 4) + 1 ;
console.log(randomNumber);
It was too easy.
var values = null;
while(true) {
var currentSum = 0;
var expectedSum = 10;
values = [];
while(expectedSum !== currentSum) {
//var value = Math.floor(Math.random() * 9) + 1;
var value = Math.floor(Math.random() * 4) + 1;
if(value + currentSum > expectedSum) {
continue;
}
currentSum += value;
values.push(value);
}
if(values.length === 4) {
break;
} else {
console.log('false iteration')
}
}
console.log(values);

Generate a random boolean 70% True, 30% false

I have the following function that generates a random boolean.
choose_direction: function () {
var random_boolean = Math.random() >= 0.5;
if (random_boolean) {
trade.call()
prev_trade.goingUp = true
console.log('Trade: CALL!!!!!!!!!!!!!!!!!!!!!!')
} else {
trade.put()
prev_trade.goingUp = false
console.log('Trade: PUT!!!!!!!!!!!!!!!!!!!!!!!!')
}
}
However, I need the distribution to be unfair. More specifically, I want the output to be 70% of the time true and 30% of the time false.
Instead of >= 0.5 you just need < 0.7:
var random_boolean = Math.random() < 0.7;
// 70% this will be true, 30% false
As #plasmacel commented, Math.random() returns a value between 0 and 1 (including 0, but not 1: [0, 1)), so therefore we do < 0.7.
Let's consider one thing. Math.random() gives you a pseudo-random number.
Try this function:
function randomTest(triesCount) {
var sum = 0;
for(var i = 0; i < triesCount; i++){
sum += Math.random();
}
return sum / triesCount;
}
if you try it with different triesCount parameters the results will be:
randomTest(100) - 0.5189474703446081
randomTest(100) - 0.5189474703446081
randomTest(1000) - 0.4973368682657417
randomTest(10000) - 0.5001058467610172
randomTest(100000) - 0.4987280984186288
randomTest(1000000) - 0.4999987387801045
randomTest(10000000) - 0.49998292815655454
randomTest(100000000) - 0.500079160302315
So as you can se the results go to 0.5 which means this is not a random number generated here.
Although the answer to your question will be
var random_boolean = Math.random() >= 0.3;
as Ionică Bizău said.
But keep in mind that it is pseudo-random number!
Here is a version I made with typescript. Can easily remove the variable types to turn this into javascript.
//Usage
let randomBoolean = randomPercentForTrue(70)
export function randomPercentForTrue(percentage: number): boolean {
return randomNumber(1, 100) <= percentage
}
export function randomNumber(minValue: number, maxValue: number): number {
return Math.floor(Math.random() * (maxValue - minValue + 1)) + minValue
}
These methods are heavily tested with various use cases. Here are test cases with mocha:
it('randomPercentForTrue with 95% accuracy', function() {
let results0: boolean[] = []
let results1: boolean[] = []
let results2: boolean[] = []
let results3: boolean[] = []
let results4: boolean[] = []
let loopAmount = 10000
for(let i = 0; i < loopAmount; i++) {
results0.push(randomPercentForTrue(0))
results1.push(randomPercentForTrue(25))
results2.push(randomPercentForTrue(50))
results3.push(randomPercentForTrue(75))
results4.push(randomPercentForTrue(100))
}
let resultTotals = Array(5).fill(0)
for(let i = 0; i < loopAmount; i++) {
resultTotals[0] += results0[i]
resultTotals[1] += results1[i]
resultTotals[2] += results2[i]
resultTotals[3] += results3[i]
resultTotals[4] += results4[i]
}
expect(resultTotals[0]).to.be.closeTo(0, 0)
expect(resultTotals[1]).to.be.closeTo(loopAmount * 25 / 100, loopAmount * 25 / 100 / 20)
expect(resultTotals[2]).to.be.closeTo(loopAmount * 50 / 100, loopAmount * 50 / 100 / 20)
expect(resultTotals[3]).to.be.closeTo(loopAmount * 75 / 100, loopAmount * 75 / 100 / 20)
expect(resultTotals[4]).to.be.closeTo(loopAmount, 0)
})

how to calculate the ways of a big number split by some small numbers in js

For example: There is a total number 1000, and how many ways to equal 1000 using 100/50/20/10/5/1. I have found a idea about this. But obviously, that's not good. So does anyone have some other good ideas to cover it?
total = prompt('you can input number 1-1000, but 1000 will take a LOONG time');
count = 0;
t100 = total;
function money() {
for (var i100 = Math.floor(t100 / 100); i100 >= 0; i100--) {
var t50 = t100 - i100 * 100;
for (var i50 = Math.floor(t50 / 50); i50 >= 0; i50--) {
var t20 = t50 - i50 * 50;
for (var i20 = Math.floor(t20 / 20); i20 >= 0; i20--) {
var t10 = t20 - i20 * 20;
for (var i10 = Math.floor(t10 / 10); i10 >= 0; i10--) {
var t5 = t10 - i10 * 10;
for (var i5 = Math.floor(t5 / 5); i5 >= 0; i5--) {
var t1 = t5 - i5 * 5;
count++;
console.log(i100 + ' 100' + i50 + ' 50' + i20 + ' 20' + i10 + ' 10' + i5 + ' 5' + t1 + ' 1');
}
}
}
}
}
alert('The total number ' + total + ' is ' + count);
}
money()
No idea if it's correct but a little idea that popped to my head:
You know that with the bills/coins given, you can always fill up to the amount you need (value). If at the same time, you assume that you will always chose the highest possible bill / coin for the remainder I think you could do this:
If you use 100 bills you have floor(value / 100) possibilities (You can use 1 x 100, 2 x 100... )
If you don't use 100 but 50 you have floor(value / 50) possibilities
If you don't use 50 but 20 you have floor(value /20) possibilities
and so on. Add those up and you have a total of possibilities.
Let me know if I'm missing something.
I don't know about coding in js but you could try an algorithm like this one, which should be much more performant timely speaking. I'll try to make it as clear as possible :
availableCoins = [100, 50, 20, 10, 5] // all available coins exept 1
numberCoins = availableCoins.Length
mapResults = new Map[(int,int),int]
money(int total, int indexCoin){
if ( indexCoin == numberCoins ) {
return 1 // only coin 1 is available, there is only one solution
}
elseif ( mapResults.containsKey((total,indexCoin)) ) {
return mapResults((total,indexCoin)) // if the result has already been computed
}
else {
int count = 0
int currentCoin = availableCoin[indexCoin]
int upperbound = floor(total / currentCoin) // compute it before to avoid useless computation
for (int i = 0; i <= upperbound; i++) {
count += money(total - i * currentCoin, indexCoin + 1) // we assume only i of the current coin will be use
}
mapResults((total,indexCoin)) = count
return count
}
}
money(1000, 0)
Let me know if I have missed something or if it is not clear.
I let you adapt it to js.
I have got a expression from my colleague, and here it is:
//n is the total number
Change(n,m) {
if(n===0) return 0;
if(n<0 || m === 0) return 0;
var dimes = [1, 5, 10, 20, 50, 100];
return (change(n, m-1)+change(n-dimes[m-1], m));
}

How to divide number into integer pieces that are each a multiple of n?

Had a hard time coming up with a concise title for this. I'm sure there are terms for what I want to accomplish and there is no doubt a common algorithm to accomplish what I'm after - I just don't know about them yet.
I need to break up a number into n pieces that are each a multiple of 50. The number is itself a multiple of 50. Here is an example:
Divide 5,000 by 3 and end up with three numbers that are each multiples of 50:
1,650
1,700
1,650
I also would like to have the numbers distributed so that they flip back and forth, here is an example with more numbers to illustrate this:
Divide 5,000 by 7 and end up with 7 numbers that are each multiples of 50:
700
750
700
750
700
700
700
Note that in the above example I'm not worried that the extra 50 is not centered in the series, that is I don't need to have something like this:
700
700
750 <--- note the '50s' are centered
700
750 <--- note the '50s' are centered
700
700
Hopefully I've asked this clearly enough that you understand what I want to accomplish.
Update: Here is the function I'll be using.
var number = 5000;
var n = 7;
var multiple = 50;
var values = getIntDividedIntoMultiple(number, n, multiple)
function getIntDividedIntoMultiple(dividend, divisor, multiple)
{
var values = [];
while (dividend> 0 && divisor > 0)
{
var a = Math.round(dividend/ divisor / multiple) * multiple;
dividend -= a;
divisor--;
values.push(a);
}
return values;
}
var number = 5000;
var n = 7;
var values = [];
while (number > 0 && n > 0) {
var a = Math.floor(number / n / 50) * 50;
number -= a;
n--;
values.push(a);
} // 700 700 700 700 700 750 750
Edit
You can alternate Math.floor and Math.ceil to obtain the desired result:
while (number > 0 && n > 0) {
if (a%2 == 0)
a = Math.floor(number / n / 50) * 50;
else
a = Math.ceil(number / n / 50) * 50;
number -= a;
n--;
values.push(a);
} // 700 750 700 750 700 700 700
// i - an integer multiple of k
// k - an integer
// n - a valid array length
// returns an array of length n containing integer multiples of k
// such that the elements sum to i and the array is sorted,
// contains the minimum number of unique elements necessary to
// satisfy the first condition, the elements chosen are the
// closest together that satisfy the first condition.
function f(i, k, n) {
var minNumber = (((i / k) / n) | 0) * k;
var maxNumber = minNumber + k;
var numMax = (i - (minNumber * n)) / k;
var nums = [];
for (var i = 0; i < n - numMax; ++i) {
nums[i] = minNumber;
}
for (var i = n - numMax; i < n; ++i) {
nums[i] = maxNumber;
}
return nums;
}
So your second example would be
f(5000, 50, 7)
which yields
[700,700,700,700,700,750,750]
Let a be your starting number, k - number of parts you want to divide to.
Suppose, that b = a/n.
Now you want to divide b into k close integer parts.
Take k numbers, each equal to b/k (integer division).
Add 1 to first b%k numbers.
Multiply each number by n.
Example:
a = 5000, n = 50, k = 7.
b = 100
Starting series {14, 14, 14, 14, 14, 14, 14}
Add 1 to first 2 integers {15, 15, 14, 14, 14, 14, 14}.
Multiply by 50 {750, 750, 700, 700, 700, 700, 700}.
Your problem is the same as dividing a number X into N integer pieces that are all within 1 of each other (just multiply everything by 50 after you've found the result). Doing this is easy - set all N numbers to Floor(X/N), then add 1 to X mod N of them.
I see your problem as basically trying to divide a sum of money into near-equal bundles of bills of a certain denomination.
For example, dividing 10,000 dollars into 7 near-equal bundles of 50-dollar bills.
function getBundles(sum, denomination, count, shuffle)
{
var p = Math.floor(sum / denomination);
var q = Math.floor(p / count);
var r = p - q * count;
console.log(r + " of " + ((q + 1) * denomination)
+ " and " + (count - r) + " of " + (q * denomination));
var b = new Array(count);
for (var i = 0; i < count; i++) {
b[i] = (r > 0 && (!shuffle || Math.random() < .5 || count - i == r)
? (--r, q + 1) : q)
* denomination;
}
return b;
}
// Divide 10,000 dollars into 7 near-equal bundles of 50-dollar bills
var bundles = getBundles(10000, 50, 7, true);
console.log("bundles: " + bundles);
Output:
4 of 1450 and 3 of 1400
bundles: 1400,1450,1450,1400,1450,1400,1450
If the last argument shuffle is true, it distributes the extra amount randomly between the bundles.
Here's my take:
public static void main(String[] args) {
System.out.println(toList(divide(50, 5000, 3)));
System.out.println(toList(divide(50, 5000, 7)));
System.out.println(toList(divide(33, 6600, 7)));
}
private static ArrayList<Integer> toList(int[] args) {
ArrayList<Integer> list = new ArrayList<Integer>(args.length);
for (int i : args)
list.add(i);
return list;
}
public static int[] divide(int N, int multiplyOfN, int partsCount) {
if (N <= 0 || multiplyOfN <= N || multiplyOfN % N != 0)
throw new IllegalArgumentException("Invalid args");
int factor = multiplyOfN / N;
if (partsCount > factor)
throw new IllegalArgumentException("Invalid args");
int parts[] = new int[partsCount];
int remainingAdjustments = factor % partsCount;
int base = ((multiplyOfN / partsCount) / N) * N;
for (int i = 0; i < partsCount; i ++) {
parts[i] = (i % 2 == 1 && remainingAdjustments-- > 0) ? base + N : base;
}
return parts;
}
My algorithm provides even distribution of remainder across parts:
function splitValue(value, parts, multiplicity)
{
var result = [];
var currentSum = 0;
for (var i = 0; i < parts; i++)
{
result[i] = Math.round(value * (i + 1) / parts / multiplicity) * multiplicity - currentSum;
currentSum += result[i];
}
return result;
}
For value = 5000, parts = 7, multiplicity = 50 it returns
[ 700, 750, 700, 700, 700, 750, 700 ]

Math.ceil to nearest five at position 1

Okay....
I have a lot of uncontrolled numbers i want to round:
51255 -> 55000
25 -> 25
9214 -> 9500
13135 -> 15000
25123 -> 30000
I have tried modifying the numbers as string and counting length....
But is there a simple way using some Math function maybe?
Here's my late answer. Uses no Math methods.
function toN5( x ) {
var i = 5;
while( x >= 100 ) {x/=10; i*=10;}
return ((~~(x/5))+(x%5?1:0)) * i;
}
DEMO: http://jsbin.com/ujamoj/edit#javascript,live
[51255, 24, 25, 26, 9214, 13135, 25123, 1, 9, 0].map( toN5 );
// [55000, 25, 25, 30, 9500, 15000, 30000, 5, 10, 0]
Or this is perhaps a bit cleaner:
function toN5( x ) {
var i = 1;
while( x >= 100 ) {x/=10; i*=10;}
return (x + (5-((x%5)||5))) * i;
}
DEMO: http://jsbin.com/idowan/edit#javascript,live
To break it down:
function toN5( x ) {
// v---we're going to reduce x to the tens place, and for each place
// v reduction, we'll multiply i * 10 to restore x later.
var i = 1;
// as long as x >= 100, divide x by 10, and multiply i by 10.
while( x >= 100 ) {x/=10; i*=10;}
// Now round up to the next 5 by adding to x the difference between 5 and
// the remainder of x/5 (or if the remainder was 0, we substitute 5
// for the remainder, so it is (x + (5 - 5)), which of course equals x).
// So then since we are now in either the tens or ones place, and we've
// rounded to the next 5 (or stayed the same), we multiply by i to restore
// x to its original place.
return (x + (5-((x%5)||5))) * i;
}
Or to avoid logical operators, and just use arithmetic operators, we could do:
return (x + ((5-(x%5))%5)) * i;
And to spread it out a bit:
function toN5( x ) {
var i = 1;
while( x >= 100 ) {
x/=10;
i*=10;
}
var remainder = x % 5;
var distance_to_5 = (5 - remainder) % 5;
return (x + distance_to_5) * i;
}
var numbers = [51255, 25, 9214, 13135, 25123, 3, 6];
function weird_round(a) {
var len = a.toString().length;
var div = len == 1 ? 1 : Math.pow(10, len - 2);
return Math.ceil(a / 5 / div) * div * 5;
}
alert(numbers.map(weird_round));
Also updated for numbers below 10. Won't work properly for negative numbers either, just mention if you need this.
DEMO
I'm not sure why, but I thought it would be fun with regular expressions:
var result = +(number.toString().replace(/([1-9])([0-9])(.+)/, function() {
return Math.ceil(+(arguments[1] + '.' + arguments[2])) * 10 - (+arguments[2] < 5?5:0) + arguments[3].replace(/./g, '0');
}));
Working Demo
with(Math) {
var exp = floor(log(number)/log(10)) - 1;
exp = max(exp,0);
var n = number/pow(10,exp);
var n2 = ceil(n/5) * 5;
var result = n2 * pow(10,exp);
}
http://jsfiddle.net/NvvGf/4/
Caveat: only works for the natural numbers.
function round(number) {
var numberStr = number + "",
max,
i;
if (numberStr[1] > '4') {
numberStr[0] = parseInt(numberStr[0]) + 1;
numberStr[1] = '0';
} else {
numberStr[1] = '5';
}
for (i = 2; max = numberStr.length; i < max; i += 1) {
numberStr += '0';
}
return parseInt(numberStr);
}
Strange coincidence, I wrote something really similar not so long ago!
function iSuckAtNames(n) {
var n = n.toString(), len = n.length, res;
//Check the second number. if it's less than a 5, round down,
//If it's more/equal, round up
//Either way, we'll need to use this:
var res = parseFloat(n[0]) * Math.pow(10, len - 1); //e.g. 5 * 10^4 = 50000
if (n[1] <= 5) {
//we need to add a 5 right before the end!
res += 5 * Math.pow(10, len - 2);
}
else {
//We need another number of that size
res += Math.pow(10, len - 1);
}
return res;
}

Categories

Resources