像Humble Bundle一样计算JS中的比率

时间:2017-12-10 08:09:23

标签: javascript math

您是否知道humblebundle.com上的滑块选择您想要的钱?那么当你调整任何一个比例时,它会自动调整其余部分。

所以说你支付20美元,不管你想把你的小费从2美元调整到5美元到5美元,其他东西的比例应该会自动降低到匹配但是我不知道我和# 39;我在做什么。

这与我数学上的接近:

var settip = 50;
var tip = 5;
var devs = 75;
var donation = 20;

tip = settip;
var newAvail = 100 - tip;
var rCalc = 100 - (devs + donation);

devs = ((devs + rCalc) * newAvail) * .01;
donation = ((donation + rCalc) * newAvail) * .01;

console.log("New Ratio Calculation: " + rCalc);
console.log("New available space: " + newAvail);
console.log(tip);
console.log(devs);
console.log(donation);

控制台日志就是这样,我可以尝试将它放在我脑海中,哪里出了问题。这些数字也是整数:50而不是.5,因为Javascript不准确,我不想每次都做修复代码,我宁愿弄清楚如何让代码先工作,然后考虑优化。

因此,如果有人可以指导我某种方法或我在这里出错的地方,那么那就太棒了。感谢。

提示是捆绑制造商的提示。 Devs是开发者的小费。 捐款是捐款箱的小费。

每个数字都是比率。 Settip是新的比率,我应该能够改变任何一个值并让它自动改变所有其他值,但我甚至无法弄清楚如何做第一部分所以我无法开始尝试使其真正起作用的第二部分。

2 个答案:

答案 0 :(得分:0)

要重申我的想法:三个变量tipdevsdonation应始终合计为100。更新一个变量时,应更新另外两个变量以进行补偿。自动更新应保持相同的比率(例如,如果donationdevs加倍,并且提示已更新,则更新的donation值仍应为{{1}的两倍1}}值)。

如果我说得对,那么这对你有用:

devs

答案 1 :(得分:0)

我认为如果你想覆盖不同的边缘情况,这个问题并不像看起来那么容易。在这里,我假设您分配资金,因此您需要以下属性:

  1. 每个金额必须是整数美分
  2. 所有金额的总和必须等于总和
  3. 在JS中处理它的最简单方法是使用整数进行所有计算(例如以美分而不是美元计算),并在UI上以更人性化的方式格式化它们。即使有了这种简化,它也需要一些非平凡的代码:

    function updateRates(rates, newValue, index) {
        var i, len = rates.length;
        var sum = 0;
        for (i = 0; i < len; i++)
            sum += rates[i];
        var oldValue = rates[index];
        var newRest = sum - newValue;
        var curRest = sum - rates[index];
        rates[index] = newValue;
        var remainders = new Array(len);
        var fraction, value, subsum = 0;
        for (i = 0; i < len; i++) {
            if (i === index) continue;
    
            // special case, all other sliders were at 0 - split value equally
            if (curRest === 0) {
                fraction = 1.0 / (len - 1)
            }
            else {
                fraction = rates[i] / curRest
            }
            value = newRest * fraction;
            rates[i] = Math.floor(value); // always round down and then distribute rest according to the Largest remainder method
            subsum += rates[i];
            remainders[i] = {
                index: i,
                value: value - rates[i]
            };
        }
        // sort remainders and distribute rest (fractions) accordingly
        remainders.sort(function (a, b) {
            var av = a.value;
            var bv = b.value;
            if (av === bv)
                return 0;
            if (av < bv)
                return 1;
            else
                return -1;
        });
        for (i = 0; subsum < newRest; i++) {
            rates[remainders[i].index] += 1;
            subsum += 1;
        }
    
        return rates;
    }
    

    一些非平凡的测试:

    1. updateRates([85,10,5], 82, 0) => [82, 12, 6]
    2. updateRates([85,10,5], 83, 0) => [83, 11, 6]
    3. updateRates([85,10,5], 84, 0) => [84, 11, 5]
    4. updateRates([100,0,0], 95, 0) => [95, 2, 3]
    5. updateRates([4,3,3,1], 0, 0) =>  [0, 5, 5, 1]
    

    注意示例#5。如果使用了一些天真的舍入,则不会保留总和。实际上,您需要按比例+4分发3:3:1。这意味着您应该添加+12/7+12/7+4/7。由于12/7 = 1 5/7,根据标准数学规则,所有三个都应该向上舍入,结果为+2, +2, +1但我们只需要+4分即可分配。为了解决这个问题,largest remainder method用于在类别中分配小数分数。简单地说,我们的想法是,我们首先只分配整数美分(即总是向下舍入),计算实际剩余的分数,然后逐个分配。此方法的最大可能缺点是,以相等值开始的某些速率在更新后可能具有不同的值。另一方面,这是不可避免的,例如#4显示:你不能在两个类别之间平均分配5美分。