将数字向量舍入为整数,同时保留其总和

问题描述 投票:0回答:4

如何将浮点数舍入为整数,同时保留其总和?有以下用伪代码编写的答案,它将向量舍入为整数值,以便元素的总和保持不变且舍入误差最小化。我想在 R 中有效地实现这一点(即向量化,如果可能)。

例如,对这些数字进行四舍五入会产生不同的总数:

set.seed(1)
(v <- 10 * runif(4))
# [1] 2.655087 3.721239 5.728534 9.082078
(v <- c(v, 25 - sum(v)))
# [1] 2.655087 3.721239 5.728534 9.082078 3.813063
sum(v)
# [1] 25
sum(round(v))
# [1] 26

answer复制伪代码以供参考

// Temp array with same length as fn.
tempArr = Array(fn.length)

// Calculate the expected sum.
arraySum = sum(fn)

lowerSum = 0
-- Populate temp array.
for i = 1 to fn.lengthf
    tempArr[i] = { result: floor(fn[i]),              // Lower bound
                   difference: fn[i] - floor(fn[i]),  // Roundoff error
                   index: i }                         // Original index

    // Calculate the lower sum
    lowerSum = lowerSum + tempArr[i] + lowerBound
end for

// Sort the temp array on the roundoff error
sort(tempArr, "difference")

// Now arraySum - lowerSum gives us the difference between sums of these
// arrays. tempArr is ordered in such a way that the numbers closest to the
// next one are at the top.
difference = arraySum - lowerSum

// Add 1 to those most likely to round up to the next number so that
// the difference is nullified.
for i = (tempArr.length - difference + 1) to tempArr.length
    tempArr.result = tempArr.result + 1
end for

// Optionally sort the array based on the original index.
array(sort, "index")
r rounding
4个回答
26
投票

以更简单的形式,我会说这个算法是:

  1. 从四舍五入开始
  2. 将小数部分最大的数字向上舍入,直到达到所需的总和。

这可以在 R 中通过以下方式以矢量化方式实现:

  1. floor
  2. 向下取整
  3. 按小数部分对数字进行排序(使用
    order
  4. 使用
    tail
    获取具有 k 个最大小数部分的元素的索引,其中 k 是我们需要增加总和以达到目标值的量
  5. 将每个索引的输出值增加 1

代码中:

smart.round <- function(x) {
  y <- floor(x)
  indices <- tail(order(x-y), round(sum(x)) - sum(y))
  y[indices] <- y[indices] + 1
  y
}
v
# [1] 2.655087 3.721239 5.728534 9.082078 3.813063
sum(v)
# [1] 25
smart.round(v)
# [1] 2 4 6 9 4
sum(smart.round(v))
# [1] 25

11
投票

感谢这个有用的功能!只是为了添加答案,如果四舍五入到指定的小数位数,可以修改该函数:

smart.round <- function(x, digits = 0) {
  up <- 10 ^ digits
  x <- x * up
  y <- floor(x)
  indices <- tail(order(x-y), round(sum(x)) - sum(y))
  y[indices] <- y[indices] + 1
  y / up
}

4
投票

与 @josliber 的 smartRound 相比,运行总计和基于差异的方法要快得多:

diffRound <- function(x) { 
  diff(c(0, round(cumsum(x)))) 
}

这里是 100 万条记录的结果比较(请参阅此处的详细信息:运行舍入):

res <- microbenchmark(
  "diff(dww)" = x$diff.rounded <- diffRound(x$numbers) ,
  "smart(josliber)"= x$smart.rounded <- smartRound(x$numbers),
  times = 100
)

Unit: milliseconds
expr            min       lq        mean       median     uq       max       neval
diff(dww)       38.79636  59.70858  100.6581   95.4304    128.226  240.3088   100
smart(josliber) 466.06067 719.22723 966.6007   1106.2781  1177.523 1439.9360  100

0
投票

是否可以对此进行修改以保留总和,同时最小值至少为一。 IE。零总是四舍五入为一,但总和会被保留?

© www.soinside.com 2019 - 2024. All rights reserved.