如何将固定数量添加到一个高度数组,以便新的最小值均匀分布?

时间:2017-09-06 09:16:05

标签: algorithm

给定一系列高度H和一个量A,在A之间分配H以最大化最小数组值的最佳方法是什么。

例1:

H = { 1, 3, 2, 2, 4 }

A = 4

output = { 3, 3, 3, 3, 4 }

例2:

H = { 1, 3, 2, 2, 4 }

A = 3

output = { 2.66, 3, 2.66, 2.66, 4 }

3 个答案:

答案 0 :(得分:2)

O(N^2)中的简单算法:

sort(T) // In ascending order; O(N log(N))

while amount > 0:
    i := first index such that T[i] < T[i+1]   # O(log(N)) at worst if you look for it from scratch each time, or (better) in average O(1) if you keep track of the previous such i and just look at the next values like  @jdehesa does
    amount_to_add := min(T[i+1] - T[i], amount / i)  # Considering that T[N] = infinity  makes it useless to do another loop if amount is big, it's all included here
    for j <= i:
        T[j] += amount_to_add 
    amount -= i * amount_to_add

在最糟糕的情况下,你会看到我曾经的每个位置,然后做一个长度为i的循环,因此O(N^2)

您只需存储您在第一个循环中必须执行的更改,然后在第二个循环中执行更新,即可实现O(Nlog(N))

sort(T) # In ascending order; O(N log(N))

lowest_value := T[0]
amount_to_add := zeros(N)  # Array containing zeros initially
while amount > 0:
    i := first index such that lowest_value < T[i+1]   # O(log(N)) at worst. In practice probably O(1) if you keep track of the previous such i
    amount_to_add[i] := min(T[i+1] - lowest_value, amount / i) # Considering that T[N] = infinity  makes it useless to do another loop if amount is big, it's all included here
    lowest_value +=  amount_to_add[i]
    amount -= i * amount_to_add[i]
amount_to_add_incremental = 0
for j=N-1 to 0:
    amount_to_add_incremental += amount_to_add[j]
    T[j] += amount_to_add += amount_to_add_incremental 

也许有更好的东西可以有效地计算O(N)中的最终值,然后更新数组中的所有元素,在这种情况下你可以获得O(N),但你不会做比这更好。

例如,如果您认为amount很大:例如amount >= N*max(T) - sum(T),只需要O(N)次检查,那么您可以直接将T中的所有值设置为{{ 1}},需要max(T) + (amount - N*max(T) + sum(T))/N时间。 O(N)较小的情况更成问题。

答案 1 :(得分:2)

这是一个Python实现(对于NumPy或者类似的它可能更简洁但是无论如何):

def top_up(array, amount):
    if not array:
        return []
    # Sort and keep the indices
    array_sort, sort_idx = zip(*sorted(zip(array, range(len(array)))))
    array_sort = list(array_sort)
    # Take the smallest value
    v = array_sort[0]
    # Increase values from smaller to bigger
    for i in range(1, len(array_sort)):
        if amount <= 0:
            break
        if array_sort[i] == v:
            continue
        # When a different value is found increase previous to match
        increase = min((array_sort[i] - v) * i, amount)
        for j in range(i):
            array_sort[j] += increase / i
        amount -= increase
        v = array_sort[i]
    # Distribute remainder if any
    if amount > 0:
        for i in range(len(array_sort)):
            array_sort[i] += amount / len(array_sort)
    # Back to original order
    result = [0] * len(array_sort)
    for i, v in zip(sort_idx, array_sort):
        result[i] = v
    return result

测试:

top_up([1., 5., 7., 4., 3.], 6)
>>> [4.666666666666667, 5.0, 7.0, 4.666666666666667, 4.666666666666667]

更新:

采取&#34;增量变更存储&#34;这是一个小小的改进。来自gdelab's answer的想法:

def top_up(array, amount):
    if not array:
        return []
    # Sort and keep the indices
    array_sort, sort_idx = zip(*sorted(zip(array, range(len(array)))))
    array_sort = list(array_sort)
    # Take the smallest value
    v = array_sort[0]
    # Compute incremental changes
    changes = [0] * len(array_sort)
    for i in range(1, len(array_sort)):
        if amount <= 0:
            break
        if array_sort[i] == v:
            continue
        # When a different value is found increase previous to match
        increase = min((array_sort[i] - v) * i, amount)
        changes[i - 1] = increase / i
        amount -= increase
        v = array_sort[i]
    # Distribute remainder if any
    changes[-1] = amount / len(array_sort)
    # Perform the actual changes
    change = 0
    for i in reversed(range(len(changes))):
        change += changes[i]
        array_sort[i] += change
    # Back to original order
    result = [0] * len(array_sort)
    for i, v in zip(sort_idx, array_sort):
        result[i] = v
    return result

答案 2 :(得分:0)

这是一个具有O(n log n)复杂度的伪代码算法:

# array : an array of land heights
# amount : an amount of water to fill up the lowland

tmparray = array.SortHighToLow # O(n log n) here, rest is O(n)
mean = array.MeanValue
count = array.Count;

for (height in tmparray)
begin
  newmean = mean + (amount / count)
  if (height <= newmean) break
  count--
  # recalculate mean for the remaining heights
  mean = (mean * (count + 1) - height) / count
end

for (height in array)
begin
  if (height < newmean) height = newmean # shall modify array
end

请注意,一旦获得newmean,临时数组就会被丢弃,这足以使原始数组均匀。