首页 > 解决方案 > 将加权值计算为整数以进行布局

问题描述

我有一个布局,其中:

如果允许小数列跨度,这将很简单(将每个权重乘以列数并除以总权重),但将其分解为整数变得更加困难。这就是我想出的:

function weightedIntegerValues(weights, total) {
    const totalWeight = totalValues(weights);
    const weightedValues = weights.map(weight => Math.round(total * weight / totalWeight));

    for (let totalValue = totalValues(weightedValues); totalValue > total; totalValue = totalValues(weightedValues))
        subtractOneFromHighest(weightedValues);

    return weightedValues;
}

为简洁起见,我省略了以下功能:

该函数的工作原理如下:

  1. 如上所述计算加权值,但随着每个值的进行四舍五入
  2. 不断地从最大值中减去 1,weightedValues直到总和weightedValues小于或等于total(考虑任何四舍五入的 0.5 对)

这个函数有两个主要问题:

  1. 它的效率非常低(两者totalValuessubtractOneFromHighest必须循环遍历函数主循环内的数组)
  2. 它错误地倾向于减少它找到的第一个“最高值”。

为了说明第 (2) 点,请考虑以下内容:

weightedIntegerValues([1, 2, 4, 3], 5); // [1, 1, 1, 2]

加权函数找到 的四舍五入值[1, 1, 2, 2],确定它大于所需的总和 5,并从它找到的第一个最大值(在索引 3 处)减去 1,但实际上我们希望从索引 4 中减去 1,即 1.5在四舍五入之前,给我们[1, 1, 2, 1].

我的问题如下:

  1. 这可以做得比 N 2更好吗?能降到N就好了。
  2. 是否有一些简单且更数学的方法来支持四舍五入到 0.5 的数字,而不是支持更左或更右的值?
  3. 是否有一些整洁的 CSS 可以为我处理这个用例?Flex-box 非常接近,但还没有完全符合我的要求(这可能是另一个问题)。

标签: math

解决方案


这是一种基于答案模糊地实现此目的的方法:

function weightedIntegerValues(weights, total) {
    let flooredValues = [];
    const weightSum = weights.reduce((sum, item) => {
        return sum + valueFunc(item);
    }, 0);

    let flooredSum = 0

    weights.forEach((weight, i) => {
        const weighted = total * weight / weightSum
        const floored = Math.floor(weighted);

        flooredValues.push({
            floored: floored,
            diff: weighted - floored,
            index: i
        });

        flooredSum = flooredSum + flooredValues[i].floored;
    });

    flooredValues = flooredValues.sort((a, b) => b.diff - a.diff);

    const difference = total - flooredSum;

    for (let i = 0; i < difference; i++) {
        flooredValues[i].floored += 1;
    }

    return flooredValues.sort((a, b) => a.index - b.index).map(v => v.floored);
}

推荐阅读