如何对数值向量取整使总和恒为指定值(此处为100)
问题背景
给定R语言中的数值向量:
input <- c(5.170669324, 8.69978618, 8.386448608, 9.765707038, 9.725934478, 6.37837945, 1.770207313, 4.64951078, 7.69234579, 8.060673651, 9.656558295, 9.933008843, 7.141666039, 2.969104213)
使用常规取整函数round(input, 0)得到结果:
[1] 5 9 8 10 10 6 2 5 8 8 10 10 7 3
常规取整后总和为101,但需求是取整后总和恰好为100。
你的思路分析
你提出的思路是:计算原始值与取整值的差值,优先调整最接近x.5的数值的取整方向——比如本次总和超了1,就把最接近0.5且小数部分大于0.5的数(也就是4.64...)从5改成4;如果总和少了则反向调整。
这个思路是合理的:x.5是常规取整的分界点,调整这类数值的取整方向,对结果的“公平性”影响最小——相比调整小数部分极近0或1的数,不会让单个数值的取整结果过度偏离原始值,符合“最小偏差”的直觉。
更通用的系统化方法
针对任意总和偏差的场景,有两种更标准化的方法:
方法1:基于小数部分的排序调整
核心逻辑是根据小数部分的大小,批量决定需要修正的数值:
- 先对所有数执行向下取整(
floor(input)),计算此时的总和与目标总和的差值diff = 目标总和 - floor_sum。 - 如果
diff > 0:需要将diff个小数部分最大的数,从floor(x)调整为floor(x)+1(优先让更接近上一个整数的数完成向上取整)。 - 如果
diff < 0:需要将abs(diff)个小数部分最小的数,从常规取整的floor(x)+1调整为floor(x)(优先修正那些最接近下一个整数的向上取整结果)。
对应你的场景:常规取整总和比目标多1,本质就是要从所有被向上取整的数里,选出小数部分最接近0.5的那个(也就是对取整结果“最不坚定”的数),把它调回向下取整——和你的思路完全契合,只是这种方法能批量处理更大的总和偏差。
方法2:最小偏差的线性规划取整
如果追求所有取整结果与原始值的偏差平方和最小,可以用线性规划实现:
设取整后结果为y_i,每个y_i为整数,满足sum(y_i) = 目标值,同时最小化sum((y_i - input_i)^2)。
这种方法会优先保证偏差大的数尽可能贴近原始值,让偏差小的数承担总和调整的任务。在R中可以用lpSolve包实现,不过对于小规模向量,方法1已经足够高效且易理解。
总结
你的思路本质是方法1的特例,逻辑合理且符合直觉。如果需要处理更复杂的场景(比如总和偏差较大),方法1的排序调整法更通用,能快速完成批量修正。
内容的提问来源于stack exchange,提问作者deschen

