【问题标题】:Calculating weighted values as integers for layout将加权值计算为整数以进行布局
【发布时间】:2019-02-28 22:39:12
【问题描述】:

我有一个布局,其中:

  • 页面被分成尽可能多的固定宽度的
  • 行适合这些,每行跨越多个
  • 每个都有一个权重,用于确定它们的列跨度(即:如果一行有 6 个和 2 个权重为 1 和 2 的 的列跨度分别为 2 和 4)。

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

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;
}

为简洁起见,我省略了以下函数:

  • totalValues - 获取数组中所有值的总和
  • subtractOneFromHighest - 找到数组中的最大值并从中减去 1(就地修改数组)

函数是这样工作的:

  1. 按上述方式计算加权值,但会随着每个值的进行四舍五入
  2. 不断地从weightedValues 中的最大值减去1,直到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,但实际上我们希望从中减去 1索引 4,舍入前为 1.5,给我们[1, 1, 2, 1]

我的问题如下:

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

【问题讨论】:

  • 作为后续:我知道这不考虑四舍五入为 0 的值(这对我的布局不利),我只是要处理一点效率低下并在此基础上编写一些丑陋的代码来处理它。
  • @NicoSchertler 我的数组没有排序,每次函数针对相同的输入运行时结果应该是一样的
  • 那里的答案似乎并不取决于排序。而且它们看起来也很确定。
  • @NicoSchertler 这很公平 - 我今天晚些时候可以测试它们并回发。谢谢。

标签: math


【解决方案1】:

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

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);
}

【讨论】:

    猜你喜欢
    • 2021-11-14
    • 1970-01-01
    • 2021-03-31
    • 2012-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-30
    相关资源
    最近更新 更多