分配一个整数数组比例补偿的舍入误差(Allocate an array of integers pr

2019-09-01 04:34发布

我具有非负值的阵列。 我想建立数值数组谁的总和为20,使它们成正比的第一阵列。

这将是一个简单的问题,但我想比例阵列总结正好20,补偿任何舍入误差。

例如,该阵列

input = [400, 400, 0, 0, 100, 50, 50]

将产生

output = [8, 8, 0, 0, 2, 1, 1]
sum(output) = 20

然而,大多数情况下,将会有大量的舍入误差,如

input = [3, 3, 3, 3, 3, 3, 18]

天真地产生

output = [1, 1, 1, 1, 1, 1, 10]
sum(output) = 16  (ouch)

有没有分摊输出数组,这样加起来,以每次20的好办法?

Answer 1:

有一个很简单的回答了这个问题:我已经做了很多次。 每个分配到新的数组后,可以减少你使用如下工作的值:

  1. 调用第一阵列A,和新的,比例阵列B(其中开始是空的)。
  2. 呼叫的元件T的总和
  3. 调用期望的总和S.
  4. 对于阵列(ⅰ)执行以下操作中的每个元素:
    一个。 B [I] =圆(A [I] / T * S)。 (四舍五入至最接近的整数,便士或任何是必需的)
    湾 T = - A [1]
    C。 S =的S - B [i]于

而已! 易溶于任何编程语言或电子表格来实现。

该解决方案是最佳的,所述得到的数组的元素将不会超过1远离他们的理想的,非圆形的值。 让我们与你的实例来说明:
T = 36,S = 20 B [1] =圆(A [1] / T * S)= 2(理想地,1.666 ....)
T = 33,S = 18,B [2] =圆(A [2] / T * S)= 2(理想地,1.666 ....)
T = 30,S = 16,B [3] =圆(A [3] / T * S)= 2(理想地,1.666 ....)
T = 27,S = 14,B [4] =圆(A [4] / T * S)= 2(理想地,1.666 ....)
T = 24,S = 12,B [5] =圆(A [5] / T * S)= 2(理想地,1.666 ....)
T = 21,S = 10,B [6] =圆(A [6] / T * S)= 1(理想地,1.666 ....)
T = 18,S = 9 B [7] =圆(A [7] / T * S)= 9(理想地,10)

请注意,每个值B中与之比较的理想值在括号中,不同的是从来没有超过1。

这也是有趣的是,重新安排数组中的元素可能导致所得数组中不同的相应的值。 我发现,按升序排列的元素是最好的,因为它会导致实际与理想之间的最小平均差异。



Answer 2:

你的问题是类似比例代表要分享当事人proportionnaly他们获得的选票中的N个席位(在你的案件20),你的情况[3,3,3,3,3,3,18]

有在不同国家用于处理四舍五入问题的几种方法。 我的代码下面使用哈根巴赫-Bischoff的配额在瑞士,使用的方法基本上由分配(N + 1)的整数除法,其具有最高的剩余各方之后剩余的席位:

def proportional(nseats,votes):
    """assign n seats proportionaly to votes using Hagenbach-Bischoff quota
    :param nseats: int number of seats to assign
    :param votes: iterable of int or float weighting each party
    :result: list of ints seats allocated to each party
    """
    quota=sum(votes)/(1.+nseats) #force float
    frac=[vote/quota for vote in votes]
    res=[int(f) for f in frac]
    n=nseats-sum(res) #number of seats remaining to allocate
    if n==0: return res #done
    if n<0: return [min(x,nseats) for x in res] # see siamii's comment
    #give the remaining seats to the n parties with the largest remainder
    remainders=[ai-bi for ai,bi in zip(frac,res)]
    limit=sorted(remainders,reverse=True)[n-1]
    #n parties with remainter larger than limit get an extra seat
    for i,r in enumerate(remainders):
        if r>=limit:
            res[i]+=1
            n-=1 # attempt to handle perfect equality
            if n==0: return res #done
    raise #should never happen

但是这种方法并不总是产生相同数目的席位与在你的情况下,完全平等方:

proportional(20,[3, 3, 3, 3, 3, 3, 18])
[2,2,2,2,1,1,10]


Answer 3:

您已设置3项不兼容的要求。 一个整数值阵列成正比[1,1,1]不能进行总结准确20.你必须选择突破“正好20总和”之一的,“成比例的输入”和“整数值”的要求。

如果你选择打破了整数值的要求,然后用浮点或有理数。 如果你选择打破确切数额的要求,那么你已经解决了这个问题。 选择打破比例是有点麻烦。 你可能会采取一种办法是要弄清楚如何遥远的总和,然后通过输出数组随机分配更正。 例如,如果你输入的是:

[1, 1, 1]

那么你可以先让它总结以及可能的,同时仍然比例:

[7, 7, 7]

并且由于20 - (7+7+7) = -1 ,选择一种元素随机递减:

[7, 6, 7]

如果错误是4 ,你会选择四个元素递增。



Answer 4:

不执行良好,但会提供正确的结果天真的解决方案...

写该给定的阵列具有八个整数(一个迭代candidate )和input阵列,输出是最远离正比于其他人(伪码)的元素的索引:

function next_index(candidate, input)
    // Calculate weights
    for i in 1 .. 8
        w[i] = candidate[i] / input[i]
    end for
    // find the smallest weight
    min = 0
    min_index = 0
    for i in 1 .. 8
        if w[i] < min then
            min = w[i]
            min_index = i
        end if
    end for

    return min_index
 end function

然后,只是这样做

result = [0, 0, 0, 0, 0, 0, 0, 0]
result[next_index(result, input)]++ for 1 .. 20

如果没有最佳的解决方案,它会扭曲对数组的开始。

使用上面的方法,你可以舍去(如你在你的例子一样),然后只用上面的方法来补充是要被抛弃了,由于舍入误差减少迭代次数:

result = <<approach using rounding down>>
while sum(result) < 20
    result[next_index(result, input)]++


Answer 5:

因此,上述问题的答案和意见是有益的......特别是从@Frederik下降和评论。

我想出了溶液利用这样的事实,对于一个输入数组V,总和(V_I * 20)是由总和(V)整除的优势。 因此,对于V中的每个值,我mulitply 20,以及将和数鸿沟。 我把商,积累剩余。 每当累加器比总和(V)时,我添加一个到值。 这样的话,我保证所有的余得到滚入结果。

是清晰? 下面是用Python实现:

def proportion(values, total):
    # set up by getting the sum of the values and starting
    # with an empty result list and accumulator
    sum_values = sum(values)
    new_values = []
    acc = 0

    for v in values:
        # for each value, find quotient and remainder
        q, r = divmod(v * total, sum_values)

        if acc + r < sum_values:
            # if the accumlator plus remainder is too small, just add and move on
            acc += r
        else:
            # we've accumulated enough to go over sum(values), so add 1 to result
            if acc > r:
                # add to previous
                new_values[-1] += 1
            else:
                # add to current
                q += 1
            acc -= sum_values - r

        # save the new value
        new_values.append(q)

    # accumulator is guaranteed to be zero at the end
    print new_values, sum_values, acc

    return new_values

(I补充说,如果累加器>其余部分,我递增先前值代替当前值的增强)



文章来源: Allocate an array of integers proportionally compensating for rounding errors