【问题标题】:Dividing array into chunks of almost equal sum将数组划分为几乎相等的块
【发布时间】:2020-08-13 07:26:02
【问题描述】:

分块的代码由这段代码sn-p提供:

def chunks(lst, n):     #n here is 4
    """Yield successive n-sized chunks from lst."""
    for i in range(0, len(lst), n):
        yield lst[i:i + n]

作为输入,我有一个包含以下整数的列表:

[11, 45, 74, 24, 27, 55, 37, 97, 15, 36, 54, 7, 41, 77, 28, 36, 22, 214, 110, 40, 41, 14, 6, 35, 6, 7, 62, 2, 34, 1, 30, 5, 4, 8, 9, 7, 5, 7, 0, 0, 3, 0, 0, 1, 2]

我想用它生成 4 个块。作为输出,我得到以下信息:

[[11, 45, 74, 24, 27, 55, 37, 97, 15, 36, 54, 7], 
[41, 77, 28, 36, 22, 214, 110, 40, 41, 14, 6, 35], 
[6, 7, 62, 2, 34, 1, 30, 5, 4, 8, 9, 7], 
[5, 7, 0, 0, 3, 0, 0, 1, 2]]

我的问题是输出中的第二个列表的权重高于其他列表;数字的分布不太公平。
谁能告诉我如何通过包含整数来公平分配块中的数字?

我亲手做了一个例子:

输入:[11,20,2,4,8,13,16,0,1,0,3,6]

输出:[[20,1,0,0],[16,6],[13,8],[11,4,3,2]]

【问题讨论】:

  • 你有衡量你希望分布有多“均匀”吗?一种解决方案是对输入列表进行排序,然后从每个块的 n 个元素中选择 1 个
  • 这里是一个整数较少的例子:[11,20,2,4,8,13,16,0,1,0,3,6] 想要输出:[[20,1], [16,6],[13,8],[11,4,3,2]] 这是我手工制作的,我不知道如何编码
  • 您可以编辑您的问题以包含最小可重现示例吗?
  • 嘿@menbar 我没有理解你的问题。你的例子一开始似乎有点错误...... [16,3,2 应该是一个块,[11,6,4] 应该是另一个正确的?
  • 好的。所以你已经知道多进程的事情了吗?如果是,那么我建议您可以从问题中删除多进程标签以消除混淆。

标签: python arrays algorithm


【解决方案1】:

我们可以先尝试将一个数组分成两部分,使它们的总和几乎相等。
然后,一旦我们有了这 2 个集合,我们可以进一步对它们中的每一个应用相同的过程,以获得 2*2 = 4 个相等的集合。

将一个数组分成两部分近似相等的算法如下:

  • 初始化 2 个空集来保存我们的答案。
  • 以相反的顺序对数组进行排序。
  • 在保持 2 个集合的总和的同时,迭代数组中的所有元素并将它们附加到总和较小的集合中。
  • 请注意,这只是一个近似算法。如果我们想找到完全最优的答案,那么我们可以将此问题建模为subset sum problem,并找出我们是否可以将数组分成两部分,其中一组的总和为sum/2sum/2 - 1sum/2 - 2 ... 0 (按顺序尝试每个)。与我们的近似解决方案相比,这要慢得多。
def divide_almost_equally_into_2(arr):
    set1 = []
    set2 = []
    sum1 = sum2 = arr_idx = 0
    while arr_idx < len(arr):
        if sum1 < sum2:
            set1.append(arr[arr_idx])
            sum1 += arr[arr_idx]
        else:
            set2.append(arr[arr_idx])
            sum2 += arr[arr_idx]
        arr_idx += 1
    return set1, set2


def divide_almost_equally_into_4(arr):
    arr.sort(reverse=True)
    set1, set2 = divide_almost_equally_into_2(arr)
    set11, set12 = divide_almost_equally_into_2(set1)
    set21, set22 = divide_almost_equally_into_2(set2)
    return [set11, set12, set21, set22]


def main():
    arr = [11,20,2,4,8,13,16,0,1,0,3,6]
    set1, set2, set3, set4 = divide_almost_equally_into_4(arr)
    print(f"{arr}   {sum(arr)}\n")
    print(f"{set1}   {sum(set1)}")
    print(f"{set2}   {sum(set2)}")
    print(f"{set3}   {sum(set3)}")
    print(f"{set4}   {sum(set4)}")


main()

输出:

[20, 16, 13, 11, 8, 6, 4, 3, 2, 1, 0, 0]   84

[13, 8]   21
[16, 3, 2]   21
[11, 6, 4]   21
[20, 1, 0, 0]   21

编辑:

要将相同的算法推广到“n”个分割,我们可以使用堆:

  • 创建一个大小为“n”的最小堆,其中每个元素都是 (current_sum_of_set_i, i) 形式的元组。
  • 因此,最初堆将包含元素(0, 0), (0, 1) ... (0, n-1)
  • 现在遍历反向排序的数组,并将每个元素分配给堆顶部的集合。
  • 使用添加到其中的元素的新总和更新集合的堆元素。
import heapq


def divide_almost_equally(arr, num_chunks):
    arr = sorted(arr, reverse=True)
    heap = [(0, idx) for idx in range(num_chunks)]
    heapq.heapify(heap)
    sets = {}
    for i in range(num_chunks):
        sets[i] = []
    arr_idx = 0
    while arr_idx < len(arr):
        set_sum, set_idx = heapq.heappop(heap)
        sets[set_idx].append(arr[arr_idx])
        set_sum += arr[arr_idx]
        heapq.heappush(heap, (set_sum, set_idx))
        arr_idx += 1
    return sets.values()


def main():
    arr = [11,20,2,4,8,13,16,0,1,0,3,6]
    set1, set2, set3, set4 = divide_almost_equally(arr, 4)
    print(f"{sorted(arr, reverse=True)}   {sum(arr)}\n")
    print(f"{set1}   {sum(set1)}")
    print(f"{set2}   {sum(set2)}")
    print(f"{set3}   {sum(set3)}")
    print(f"{set4}   {sum(set4)}")


main()

输出:

[20, 16, 13, 11, 8, 6, 4, 3, 2, 1, 0, 0]   84

[20, 1]   21
[16, 4, 0, 0]   20
[13, 6, 3]   22
[11, 8, 2]   21

【讨论】:

  • 谢谢!有没有办法,我可以说,我想要多少块?在示例中,我说我需要 4 个块。怎么样,当我需要 6 时?有没有一种简单的方法来形成你的代码?非常感谢!
  • 用更通用的解决方案对其进行了更新。一边想一边玩得很开心:)
  • 你能帮我用这种输入生成块吗? [[11,"a"], [20,"a"], [2,"a"], [4,"a"], [8,"a"], [13,"a"], [ 16,"a"], [0,"a"], [1,"a"],[0,"a"], [3,"a"],[6,"a"] ] 块应该用整数生成但列表中的字符串必须跟随整数?非常感谢!
  • 有什么方法可以在不改变顺序的情况下得到结果(这里你使用了排序),非常感谢
【解决方案2】:

这是一个解决方案,其中每个块的总和小于或等于阈值。该解决方案的灵感来自 Anmol Singh Jaggi 提出的算法。

在下面的函数中,递归地划分输入集,直到每个块的总和小于或等于阈值

chunk的数量是线性增加的,但你可以根据自己的喜好改变,比如num_chunks = num_chunks*2

def divide_almost_equally(arr, num_chunks,Threshold):
    divideMore = 0
    arr = sorted(arr, reverse=True)
    set_sum = np.zeros(num_chunks)
    sets = {}
    for i in range(num_chunks):
        sets[i] = []
    arr_idx = 0
    while arr_idx < len(arr):
        ChunkIndx = np.argmin(set_sum)
        sets[ChunkIndx].append(arr[arr_idx])
        set_sum[ChunkIndx] = set_sum[ChunkIndx] + arr[arr_idx]
        arr_idx = arr_idx + 1
    for total in set_sum:
        if(total>Threshold):
            divideMore = 1
            break
    if(divideMore == 1):
      num_chunks = num_chunks + 1
      return divide_almost_equally(arr, num_chunks,Threshold)
    else:
        return sets, num_chunks

if __name__ == "__main__":
   input = np.arange(0,100)
   Set, num_chunks = divide_almost_equally(np.arange(0,100), 1,100) # start with num_chunks = 1num_chunks = 1
   for chunk_indx in range(num_chunks): 
      print(Set[chunk_indx],'-- >',np.sum(Set[chunk_indx]))




 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-22
    • 1970-01-01
    • 2012-09-04
    • 1970-01-01
    • 2020-06-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多