我怎样才能将数字(钱)平均划分为 x 数字可以是一个或两个小数或没有它
例如1000
或100.2
或112.34
我希望能够将该数字平均分成所有的x部分,但是如果它不是奇数,则最后一个的额外数字。
例如
3856 / 3
1285.33
1285.33
1285.34
我怎样才能将数字(钱)平均划分为 x 数字可以是一个或两个小数或没有它
例如1000
或100.2
或112.34
我希望能够将该数字平均分成所有的x部分,但是如果它不是奇数,则最后一个的额外数字。
例如
3856 / 3
1285.33
1285.33
1285.34
审查其他答案
在重新检查此处提供的解决方案后,我注意到它们产生了一些奇怪的结果。
@GeorgeMaulerdivideCurrencyEqually
似乎可以使用原始问题中提供的输入。但是如果你稍微改变一下,它会产生一个非常奇怪的结果……
// it appears to work
divideCurrencyEvenly(10,3)
=> [ '3.33', '3.33', '3.34' ]
// wups ...
divideCurrencyEvenly(10,6)
=> [ '1.67', '1.67', '1.67', '1.67', '3.32' ]
// hmm this one works ...
divideCurrencyEvenly(18,5)
=> [ '3.60', '3.60', '3.60', '3.60', '3.60' ]
// wups ...
divideCurrencyEvenly(100,7)
=> [ '14.29', '14.29', '14.29', '14.29', '14.29', '28.55' ]
虽然@Barmar 的解决方案似乎表现得更好一些……好吧……
// it appears to work
divide(10,3)
=> [ '3.33', '3.33', 3.34 ]
// acceptable, i guess
divide(10,6)
=> [ '1.66', '1.66', '1.66', '1.66', '1.66', 1.700000000000001 ]
// hmm, not so good, but still acceptable
divide(18,5)
=> [ '3.60', '3.60', '3.60', '3.60', 3.5999999999999996 ]
// as acceptable as the last two, i guess
divide(100,7)
=> [ '14.28', '14.28', '14.28', '14.28', '14.28', '14.28', 14.320000000000007 ]
像这样3.5999999999999996
的数字大多是可以原谅的,因为这就是浮点算法在 JavaScript 中的工作方式。
但是,我发现最令人不安divide
的是它提供了一个混合类型的数组(字符串和浮点数)。而且由于输出中的数字没有四舍五入(精确到正确的精度),如果您要将结果添加到纸上,您将无法返回原始numerator
输入 - 也就是说,除非您对结果进行四舍五入.
我们仍在为平等而战……
我对上述两种解决方案也存在最后的不满。结果并不代表尽可能接近相等的分布。
如果您将 100 除以 7,精度为 2,@Barmar 的解决方案(修正了舍入问题)将给出……
[ '14.28', '14.28', '14.28', '14.28', '14.28', '14.28', 14.32 ]
如果这些是人并且数字代表金钱,那么1个人不应该多付4便士。相反,4个人应该多付1便士……
[ 14.29, 14.29, 14.29, 14.29, 14.28, 14.28, 14.28 ]
尽可能接近相等
回到原点
我对解决方案不满意,所以我自己制作了一个,distribute
.
它有 3 个参数:precision p
、divisor d
和numerator n
。
它缩放分子并找到其中的最大q
整数q * d <= n
。使用模块化划分,我们知道需要贡献多少“切片” q+1
。最后,每个q
orq+1
都按比例缩小并填充输出数组。
const fill = (n, x) =>
Array (n) .fill (x)
const concat = (xs, ys) =>
xs .concat (ys)
const quotrem = (n, d) =>
[ Math .floor (n / d)
, Math .floor (n % d)
]
const distribute = (p, d, n) =>
{ const e =
Math .pow (10, p)
const [ q, r ] =
quotrem (n * e, d)
return concat
( fill (r, (q + 1) / e)
, fill (d - r, q / e)
)
}
console .log
( distribute (2, 3, 10)
// [ 3.34, 3.33, 3.33 ]
, distribute (2, 6, 10)
// [ 1.67, 1.67, 1.67, 1.67, 1.66, 1.66 ]
, distribute (2, 5, 18)
// [ 3.6, 3.6, 3.6, 3.6, 3.6 ]
, distribute (2, 7, 100)
// [ 14.29, 14.29, 14.29, 14.29, 14.28, 14.28, 14.28 ]
)
您会看到我将精度设置为参数,p
这意味着您可以控制小数位数。还要注意Δ
结果中任何数字之间的最大差异是Δ <= 1/10^p
distribute (0, 7, 100)
=> [ 15, 15, 14, 14, 14, 14, 14 ] // Δ = 1
distribute (1, 7, 100)
=> [ 14.3, 14.3, 14.3, 14.3, 14.3, 14.3, 14.2 ] // Δ = 0.1
distribute (2, 7, 100)
=> [ 14.29, 14.29, 14.29, 14.29, 14.28, 14.28, 14.28 ] // Δ = 0.01
distribute (3, 7, 100)
=> [ 14.286, 14.286, 14.286, 14.286, 14.286, 14.285, 14.285 ] // Δ = 0.001
distribute (4, 7, 100)
=> [ 14.2858, 14.2857, 14.2857, 14.2857, 14.2857, 14.2857, 14.2857 ] // Δ = 0.0001
distribute
可以以有意义的方式部分应用。这是小人可以使用它在餐厅精确分摊账单的一种方法……
// splitTheBill will use precision of 2 which works nice for monies
const splitTheBill = (people, money) =>
distribute (2, people, money)
// partyOfThree splits the bill between 3 people
const partyOfThree = money =>
splitTheBill (3, money)
// how much does each person pay ?
partyOfThree (67.89)
=> [ 18.93, 18.93, 18.92 ]
这是一种将人们分组的有效方法——同时注意不要分裂一个人——这通常会导致死亡……
// p=0 will yield only whole numbers in the result
const makeTeams = (teams, people) =>
distribute (0, teams, people)
// make 4 teams from 18 people
// how many people on each team?
makeTeams (4, 18)
=> [ 5, 5, 4, 4 ]
function divide(numerator, divisor) {
var results = [];
var dividend = (Math.floor(numerator/divisor*100)/100).toFixed(2); // dividend with 2 decimal places
for (var i = 0; i < divisor-1; i++) {
results.push(dividend); // Put n-1 copies of dividend in results
}
results.push(numerator - (divisor-1)*dividend); // Add remainder to results
return results;
}
听起来像一个非常简单的循环/递归。
function divideEvenly(numerator, minPartSize) {
if(numerator / minPartSize< 2) {
return [numerator];
}
return [minPartSize].concat(divideEvenly(numerator-minPartSize, minPartSize));
}
console.log(divideEvenly(1000, 333));
要获得货币的两位小数,在调用此函数之前将两个数字乘以 100,然后将每个结果除以 100 并调用toFixed(2)
.
function divideCurrencyEvenly(numerator, divisor) {
var minPartSize = +(numerator / divisor).toFixed(2)
return divideEvenly(numerator*100, minPartSize*100).map(function(v) {
return (v/100).toFixed(2);
});
}
console.log(divideCurrencyEvenly(3856, 3));
//=>["1285.33", "1285.33", "1285.34"]
由于我们谈论的是金钱,只要总数正确加起来,通常几美分的差异并不重要。因此,如果您不介意一次付款可能比其他付款多几美分,这里有一个非常简单的分期付款解决方案。
function CalculateInstallments(total, installments) {
// Calculate Installment Payments
var pennies = total * 100;
var remainder = pennies % installments;
var otherPayments = (pennies - remainder) / installments;
var firstPayment = otherPayments + remainder;
for (var i = 0; i < installments; i++) {
if (i == 0) {
console.log("first payment = ", firstPayment / 100);
} else {
console.log("next payment = ", otherPayments / 100);
}
}
}
既然你说你想要最后的最高付款,你会想要修改 for 循环中的 if 语句: if (i==installments-1) { //last payment }
如果有人正在寻找随机分布的 vanilla JS 解决方案,这里是我的:
function fairDivision(resolution, numerator, denominator) {
// preserves numerator(n) integrity when dividing into denominator(d) bins
// remainders are randomly distributed into sequential bins
// resolution is number of significant digits after decimal
// (-'ve resolution for insignificant digits before decimal).
const n = numerator * Math.pow(10, resolution);
const remainder = n % denominator;
const base = (n - remainder) / denominator;
const offset = Math.floor(Math.random()*denominator); // index of first 'round-up' bin
let arr = []
let low= (offset + remainder) % denominator;
let a; let b; let c = (low < offset); //logical units
for (let i=0; i < denominator; i++) {
a = (i < low);
b = (i >= offset);
if ((a && b) || (a && c) || (b && c)) {
arr.push(base +1)
} else{
arr.push(base)
}
arr[i] = arr[i] / Math.pow(10, resolution);
}
return arr;
}
这是基于@Thank_you 的结论,只是为了根据相等划分没有小数的部分:
//n=how many groups, lot = total units
function makeTeams2(n,lot){
var div = lot/n;
var portion = Math.floor(div);
var remains = Math.round(n*((div) % 1));
var arr = [];
for(var i=0; i<n; i++) arr.push(portion);
for(var i=0; i<remains; i++) arr[i] = arr[i] + 1;
return arr;
}
// >> makeTeams2(7,100);
// >> [15,15,14,14,14,14,14]
这比@Thank_you 的“makeTeams(teams,people)”常量要快一点,但我接受性能改进,因为我是初学者,我只是以自己的方式做这件事并想分享。
请检查以下代码,
function Dividently(Amount, Quantity) {
var m = Amount* 100,
n = m % Quantity,
v = Math.floor(m / Quantity) / 100,
w = Math.floor(m / Quantity+ 1) / 100;
for (var i = 0, out = new Array(Quantity); i < Quantity; ++i) {
out[i] = i < n ? w : v;
}
return out;
}
var arr = Dividently(3856, 3);
Math.floor() - 将数字向下舍入到最接近的整数
@user633183 存在问题,distribute
但仅在分频器低于 3 时才会发生。
distribute(2, 2, 560.3)
// [ '280.15' , '280.14']
distribute(2, 1, 74.10)
// [ '74.09' ]
distribute(2, 1, 74.60)
// [ '74.59' ]
我将Guffa的答案改写为 javascript
const distribute = (precision, divider, numerator) => {
const arr = [];
while (divider > 0) {
let amount = Math.round((numerator / divider) * Math.pow(10, precision)) / Math.pow(10, precision);
arr.push(amount);
numerator -= amount;
divider--;
}
return arr.sort((a, b) => b-a);
};
这是结果
distribute(0, 7, 100)
=> [ 15, 15, 14, 14, 14, 14, 14 ]
distribute(1, 7, 100)
=> [ 14.3, 14.3, 14.3, 14.3, 14.3, 14.3, 14.2 ]
distribute(2, 7, 100)
=> [ 14.29, 14.29, 14.29, 14.29, 14.28, 14.28, 14.28 ]
distribute(3, 7, 100)
=> [ 14.286, 14.286, 14.286, 14.286, 14.286, 14.285, 14.285 ]
distribute(4, 7, 100)
=> [ 14.2858, 14.2857, 14.2857, 14.2857, 14.2857, 14.2857, 14.2857 ]
// and of course
distribute(2, 2, 560.3)
=> [ 280.15, 280.15 ]
distribute(2, 1, 74.10)
=> [ 74.1 ]
distribute(2, 1, 74.60)
=> [ 74.6 ]
<button onclick="myFunction(3856,3)">Try it</button>
function myFunction(number,divide) {
var money = number / divide;
money = Math.ceil(money * 100) / 100;
alert(money); //1285.34
}