增加或减少数组的数量,使它们的总和始终保持 100

Increase or decrease numbers of an array so the sum of them always remains 100

假设有一个 x 数数组,它们的和是 100。

每个数字增加或减少线性 step。每当一个数字增加时,其余的数字必须减少 均匀地 ,以便数字的总和不超过 100。同样,每当一个数字减少时,其余的数字必须增加 均匀地 以便数字的总和不会低于 100。如果某个值导致总和超过或低于 100,则必须禁止该操作并向用户发送消息。

假设数组 A: [20,20,20,20,20]

A[2] += 4
//the array A has to become somehow automatically: [19,19,24,19,19]

这个有3个问题。首先,如果一个数字超过了一个值,那将使其余数字低于 0,我不希望这样。示例:

A: [-5,100,-5,-5,-5]

还有一个跟步骤有关。我不知道应该增加或减少多少(可能基于数组的长度)。

现在我有step = 1 / A.length

因此,如果一个数字随着 step 增加,则其余数字 必须减少step / A.length - 1(减去一个因为我没有计算用户更改的数量)

反之亦然

基本上我试图根据用户价值(向下或向上)进行百分比增加或减少。 你能给我建议我必须遵循的逻辑,或者一些 JavaScript 代码吗?

编辑:

我已经在 angular 中实现了一些东西。

起初所有值都相等,总和为 100:

如果我使用按钮 ( > ) 增加第一个数字,其余数字将减少,依此类推..

我没有发布代码,因为它有错误并且非常紧密,我只想要代码中的逻辑或范例,以便我可以在我的应用程序中实现它。数字中的复选框旨在锁定该值,因此它不会增加或减少。

您可以检查是否有递减值并将所有更改添加到总和以递增值。

const
    change = (array, index, direction) => {
        if (direction === 1) {
            const step = 1 / array.length;
            for (let i = 0; i < array.length; i++) {
                if (i === index || array[i] === 0) continue;
                const s = Math.min(step, array[i]);
                array[i] -= s;
                array[index] += s;
            }
        } else {
            const step = Math.min(1 / array.length, array[index] / (array.length - 1));
            for (let i = 0; i < array.length; i++) {
                if (i === index) continue;
                array[i] += step;
                array[index] -= step;
            }
        }
        return array;
    },
    values = [96.8, 0.2, 1, 1, 1],
    display = values => values.forEach((v, i) => document.getElementById('value' + i).innerHTML = v.toFixed(2)),
    addEvent = (type, index) => event => {
        change(values, index, type === 'up' ? 1 : -1);
        display(values);
    };
    

[...document.getElementsByTagName('button')].forEach(element => {
    const [type, index] = element.id.match(/\d+|\D+/g);
    element.addEventListener('click', addEvent(type, +index));
});

display(values);
.as-console-wrapper { max-height: 100% !important; top: 0; }
td { text-align: center; width: 20%; }
<table>
    <tr>
        <td><button id="up0">^</button></td>
        <td><button id="up1">^</button></td>
        <td><button id="up2">^</button></td>
        <td><button id="up3">^</button></td>
        <td><button id="up4">^</button></td>
    </tr>
    <tr>
        <td id="value0"></td>
        <td id="value1"></td>
        <td id="value2"></td>
        <td id="value3"></td>
        <td id="value4"></td>
    </tr>
    <tr>
        <td><button id="down0">v</button></td>
        <td><button id="down1">v</button></td>
        <td><button id="down2">v</button></td>
        <td><button id="down3">v</button></td>
        <td><button id="down4">v</button></td>
    </tr>
</table>

下面的详细评论示例

// Utility function
const log = data => console.log(JSON.stringify(data));

const arr1 = [20, 20, 20, 20, 20];
const arr2 = [-5, 115, -5, -5];

/**@function
 *@name incDec
 *@description Increase/decrease a number by the given index of a given 
 * array by a given number and inversely decrease/increase the rest of 
 * the numbers evenly until the sum of all numbers within the array is 
 * the same as it was originally.
 *@param {number} index - Index number of the number being changed.
 *@param {number} by - Number to decrease/increase first parameter by.
 *@param {array<number>} array - The array of numbers
 *@returns {array<number>} A new array of modified numbers
 */
const incDec = (index, by, array) => {
  /* 
  Chrome doesn't divide negative numbers correctly.
  Simple divison in of itself needs to be simplified.
  So a ❉ will denote this as the reason.
  */
  let mod = Math.abs(by);
  /*
  Return a new array with the targeted number modified by >by<.
  */
  const newArr = array.map((num, idx) => idx == index ? num + by : num);

  const div = array.length - 1; //❉
  /*
  Divide the number it changed by, by the number of remaining numbers 
  in the array.
  */
  let quo = mod / div;
  quo = by < 0 ? -Math.abs(quo) : quo; //❉
  // Return a new array that has the modified numbers.
  return newArr.map((num, idx) => idx == index ? num : num - quo);
};

log(incDec(3, 8, arr1));
log(incDec(2, -8, arr2));
.as-console-row::after {
  width: 0;
  font-size: 0;
}

.as-console-row-code {
  width: 100%;
  word-break: break-word;
}