Algorithm 如何在保留浮点数和的同时将浮点数舍入为整数?

Algorithm 如何在保留浮点数和的同时将浮点数舍入为整数?,algorithm,language-agnostic,rounding,Algorithm,Language Agnostic,Rounding,假设我有一个浮点数数组,按排序(升序)顺序排列,其和已知为整数N。我想将这些数字“四舍五入”为整数,同时保持其总和不变。换句话说,我正在寻找一种算法,将浮点数数组(称之为fn)转换为整数数组(称之为中的),这样: 这两个数组的长度相同 整数数组的和为N 每个浮点数fn[i]与其在[i]中对应的整数之间的差值小于1(如果确实必须,则等于1) 假设浮点数是按排序的(fn[i]你能试试这样吗 in [i] = fn [i] - int (fn [i]); fn_res [i] = fn [i] - i

假设我有一个浮点数数组,按排序(升序)顺序排列,其和已知为整数
N
。我想将这些数字“四舍五入”为整数,同时保持其总和不变。换句话说,我正在寻找一种算法,将浮点数数组(称之为
fn
)转换为整数数组(称之为
中的
),这样:

  • 这两个数组的长度相同
  • 整数数组的和为
    N
  • 每个浮点数
    fn[i]
    与其在[i]
  • 中对应的整数
    之间的差值小于1(如果确实必须,则等于1)
    
  • 假设浮点数是按排序的(
    fn[i]你能试试这样吗

    in [i] = fn [i] - int (fn [i]);
    fn_res [i] = fn [i] - in [i];
    
    fn_res→ 是合成分数。
    (我认为这是基本的…),我们遗漏了什么吗?

    一个非常简单的方法是把所有的小数部分相加。根据问题的定义,这个数字必须是一个整数。从最大的数字开始均匀地分配这个整数。然后给第二大的数字一个……等等,直到你没有东西可以分配特朗普

    注意这是伪代码…在索引中可能会被关闭一个…时间很晚了,我很困

    float accumulator = 0;
    
    for (i = 0; i < num_elements; i++)  /* assumes 0 based array */
    {
       accumulator += (fn[i] - floor(fn[i])); 
       fn[i] =  (fn[i] - floor(fn[i]);
    }
    
    i = num_elements;
    
    while ((accumulator > 0) && (i>=0))
    {
        fn[i-1] += 1;   /* assumes 0 based array */
        accumulator -= 1;
        i--;
    }
    
    float累加器=0;
    对于(i=0;i0)和&(i>=0))
    {
    fn[i-1]+=1;/*假定基于0的数组*/
    累加器-=1;
    我--;
    }
    

    更新:根据对每个值执行的截断量,还有其他方法来分配累积值。这需要保留一个称为损失[i]=fn[i]-floor(fn[i])的单独列表。然后您可以重复fn[i]列表,并重复给最大损失项1(设置损失[i])这很复杂,但我想它可以工作。

    您可以尝试的一个选项是“级联舍入”

    对于这个算法,您可以跟踪两个运行的总数:一个是目前为止的浮点数,另一个是目前为止的整数。 若要获得下一个整数,请将下一个fp编号添加到跑步总数中,对跑步总数进行四舍五入,然后从四舍五入的跑步总数中减去整数跑步总数:-

    number  running total   integer integer running total
       1.3       1.3          1           1
       1.7       3.0          2           3
       1.9       4.9          2           5
       2.2       8.1          3           8
       2.8      10.9          3          11
       3.1      14.0          3          14
    

    好吧,4是难点。否则你可以做一些事情,比如“通常向下取整并累积剩余;当累加器>=1时向上取整”。(编辑:事实上,只要你交换了它们的位置,这可能仍然是可以的?)

    也许有一种方法可以用线性规划来实现(那是数学“编程”,而不是计算机编程——你需要一些数学来找到可行的解决方案,尽管你可能会跳过通常的“优化”部分)

    作为线性规划的一个例子,在例子[1.3,1.7,1.9,2.2,2.8,3.1]中,你可以有以下规则:

    1 <= i < 2
    1 <= j < 2
    1 <= k < 2
    2 <= l < 3
    3 <= m < 4
    i <= j <= k <= l <= m
    i + j + k + l + m = 13
    

    1基本上,你要做的是将四舍五入后的剩余部分分配给最有可能的候选人

  • 按照通常的方式对浮点数进行四舍五入,但要跟踪从四舍五入到
    fn
    in
    的增量和相关索引
  • 按增量对第二个数组排序
  • 求和(in)
    时,从最大的负增量开始向前计算,增加舍入值(确保您仍然满足规则3)
  • 或者,当
    sum(in)>N
    时,从最大的正增量向后计算,减小舍入值(确保您仍然满足规则3)
  • 例如:

    [0.02, 0.03, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.12, 0.13, 0.14] N=1 1. [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] sum=0 and [[-0.02, 0], [-0.03, 1], [-0.05, 2], [-0.06, 3], [-0.07, 4], [-0.08, 5], [-0.09, 6], [-0.1, 7], [-0.11, 8], [-0.12, 9], [-0.13, 10], [-0.14, 11]] 2. sorting will reverse the array 3. working from the largest negative remainder, you get [-0.14, 11]. Increment `in[11]` and you get [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] sum=1 Done. [0.02,0.03,0.05,0.06,0.07,0.08,0.09,0.1,0.11,0.12,0.13,0.14]N=1 1.[0,0,0,0,0,0,0,0,0,0,0,0]和=0 和[[-0.02,0]、-0.03,1]、-0.05,2]、-0.06,3]、-0.07,4]、-0.08,5], [-0.09, 6], [-0.1, 7], [-0.11, 8], [-0.12, 9], [-0.13, 10], [-0.14, 11]] 2.排序将反转数组 3.从最大负余数开始计算,得到[-0.14,11]。 在[11]中增加'Increment',得到[0,0,0,0,0,0,0,0,0,1]和=1
    完成。这里有一个算法应该可以完成任务。与其他算法的主要区别在于,这个算法总是以正确的顺序对数字进行舍入。最小化舍入误差。

    该语言是一些伪语言,可能来自JavaScript或Lua。应该解释这一点。请注意基于索引的语言(对于循环,从x到y更好。:p)


    使总差值小于1,并检查是否排序。 有人说,

    while(i=1){
    res--;
    in[i]=ceil(fn[i]);
    }
    其他的
    in[i]=楼层(fn[i]);
    if(in[i-1]>in[i])
    交换(在[i-1]中,在[i++]中);
    }
    

    (这是纸质代码,所以我没有检查有效性。)

    在我看来,问题在于没有指定排序算法。或者更类似于——不管它是否是稳定排序

    考虑以下浮点数数组:

    [0.20.20.20.2 0.2 0.2 0.2]

    总和为1。整数数组应为:

    [01]

    但是,如果排序算法不稳定,它可能会在数组中的其他位置对“1”进行排序…

    如何:

    a) start: array is [0.1, 0.2, 0.4, 0.5, 0.8], N=3, presuming it's sorted
    b) round them all the usual way: array is [0 0 0 1 1]
    c) get the sum of the new array and subtract it from N to get the remainder.
    d) while remainder>0, iterate through elements, going from the last one
       - check if the new value would break rule 3.
       - if not, add 1
    e) in case that remainder<0, iterate from first one to the last one
       - check if the new value would break rule 3.
       - if not, subtract 1
    
    a)开始:数组为[0.1,0.2,0.4,0.5,0.8],N=3,假设已排序
    b) 按常规方式将其四舍五入:数组为[0 0 1 1]
    c) 获取新数组的和,然后从N中减去它得到余数。
    d) 余数>0时,从最后一个元素开始迭代元素
    -检查新值是否会违反规则3。
    -如果不是,则添加1
    
    e) 如果余数低于@mikko rantanen代码的python和numpy实现。我花了一点时间才把这些放在一起,所以这可能对未来的谷歌用户有所帮助,尽管这个话题已经过时了

    import numpy as np
    from math import floor
    
    original_array = np.array([1.2, 1.5, 1.4, 1.3, 1.7, 1.9])
    
    # Calculate length of original array
    # Need to substract 1, as indecies start at 0, but product of dimensions
    # results in a count starting at 1
    array_len = original_array.size - 1 # Index starts at 0, but product at 1
    
    # Calculate expected sum of original values (must be integer)
    expected_sum = np.sum(original_array)
    
    # Collect values for temporary array population
    array_list = []
    lower_sum = 0
    for i, j in enumerate(np.nditer(original_array)):
        array_list.append([i, floor(j), j - floor(j)]) # Original index, lower bound, roundoff error
    # Calculate the lower sum of values
    lower_sum += floor(j)
    
    # Populate temporary array
    temp_array = np.array(array_list)
    
    # Sort temporary array based on roundoff error
    temp_array = temp_array[temp_array[:,2].argsort()]
    
    # Calculate difference between expected sum and the lower sum
    # This is the number of integers that need to be rounded up from the lower sum
    # The sort order (roundoff error) ensures that the value closest to be
    # rounded up is at the bottom of the array
    difference = int(expected_sum - lower_sum)
    
    # Add one to the number most likely to round up to eliminate the difference
    temp_array_len, _ = temp_array.shape
    for i in xrange(temp_array_len - difference, temp_array_len):
        temp_array[i,1] += 1
    
    # Re-sort the array based on original index
    temp_array = temp_array[temp_array[:,0].argsort()]
    
    # Return array to one-dimensional format of original array
    array_list = []
    for i in xrange(temp_array_len):
        array_list.append(int(temp_array[i,1]))
    new_array = np.array(array_list)
    

    计算楼层总和和人数总和
    a) start: array is [0.1, 0.2, 0.4, 0.5, 0.8], N=3, presuming it's sorted
    b) round them all the usual way: array is [0 0 0 1 1]
    c) get the sum of the new array and subtract it from N to get the remainder.
    d) while remainder>0, iterate through elements, going from the last one
       - check if the new value would break rule 3.
       - if not, add 1
    e) in case that remainder<0, iterate from first one to the last one
       - check if the new value would break rule 3.
       - if not, subtract 1
    
    import numpy as np
    from math import floor
    
    original_array = np.array([1.2, 1.5, 1.4, 1.3, 1.7, 1.9])
    
    # Calculate length of original array
    # Need to substract 1, as indecies start at 0, but product of dimensions
    # results in a count starting at 1
    array_len = original_array.size - 1 # Index starts at 0, but product at 1
    
    # Calculate expected sum of original values (must be integer)
    expected_sum = np.sum(original_array)
    
    # Collect values for temporary array population
    array_list = []
    lower_sum = 0
    for i, j in enumerate(np.nditer(original_array)):
        array_list.append([i, floor(j), j - floor(j)]) # Original index, lower bound, roundoff error
    # Calculate the lower sum of values
    lower_sum += floor(j)
    
    # Populate temporary array
    temp_array = np.array(array_list)
    
    # Sort temporary array based on roundoff error
    temp_array = temp_array[temp_array[:,2].argsort()]
    
    # Calculate difference between expected sum and the lower sum
    # This is the number of integers that need to be rounded up from the lower sum
    # The sort order (roundoff error) ensures that the value closest to be
    # rounded up is at the bottom of the array
    difference = int(expected_sum - lower_sum)
    
    # Add one to the number most likely to round up to eliminate the difference
    temp_array_len, _ = temp_array.shape
    for i in xrange(temp_array_len - difference, temp_array_len):
        temp_array[i,1] += 1
    
    # Re-sort the array based on original index
    temp_array = temp_array[temp_array[:,0].argsort()]
    
    # Return array to one-dimensional format of original array
    array_list = []
    for i in xrange(temp_array_len):
        array_list.append(int(temp_array[i,1]))
    new_array = np.array(array_list)
    
    public class Float_Ceil_or_Floor {
    
    public static int[] getNearlyArrayWithSameSum(double[] numbers) {
    
        NumWithDiff[] numWithDiffs = new NumWithDiff[numbers.length];
        double sum = 0.0;
        int floorSum = 0;
        for (int i = 0; i < numbers.length; i++) {
            int floor = (int)numbers[i];
            int ceil = floor;
            if (floor < numbers[i]) ceil++; // check if a number like 4.0 has same floor and ceiling
            floorSum += floor;
            sum += numbers[i];
            numWithDiffs[i] = new NumWithDiff(ceil,floor, ceil - numbers[i]);
        }
    
        // sort array by its diffWithCeil
        Arrays.sort(numWithDiffs, (a,b)->{
            if(a.diffWithCeil < b.diffWithCeil)  return -1;
            else return 1;
        });
    
        int roundSum = (int) Math.round(sum);
        int diff = roundSum - floorSum;
        int[] res = new int[numbers.length];
    
        for (int i = 0; i < numWithDiffs.length; i++) {
            if(diff > 0 && numWithDiffs[i].floor != numWithDiffs[i].ceil){
                res[i] = numWithDiffs[i].ceil;
                diff--;
            } else {
                res[i] = numWithDiffs[i].floor;
            }
        }
        return res;
    }
    public static void main(String[] args) {
        double[] arr = { 1.2, 3.7, 100, 4.8 };
        int[] res = getNearlyArrayWithSameSum(arr);
        for (int i : res) System.out.print(i + " ");
    
    }
    
    class NumWithDiff {
        int ceil;
        int floor;
        double diffWithCeil;
        public NumWithDiff(int c, int f, double d) {
            this.ceil = c;
            this.floor = f;
            this.diffWithCeil = d;
        }
    }
    
    import math
    import random
    integer_list = [int(x) + int(random.random() <= math.modf(x)[0]) for x in my_list]