【问题标题】:Split array to approximately equal chunks将数组拆分为大致相等的块
【发布时间】:2015-02-19 09:09:41
【问题描述】:

当每个块的总和大致相等时,如何将数组拆分为两个块?

>>> foo([10, 1, 1, 1])
[[10], [1, 1, 1]]
>>> foo([2, 5, 9, 5, 1, 1])
[[2, 5], [9, 5, 1, 1]]
>>> foo([9, 5, 5, 8, 2, 2, 18, 8, 3, 9, 4])
[[9, 5, 5, 8, 2, 2], [18, 8, 3, 9, 4]]
>>> foo([17, 15, 2, 18, 7, 20, 3, 20, 12, 7])
[[17, 15, 2, 18, 7], [20, 3, 20, 12, 7]]
>>> foo([19, 8, 9, 1, 14, 1, 16, 4, 15, 5])
[[19, 8, 9, 1], [14, 1, 16, 4, 15, 5]]

【问题讨论】:

  • 为什么最后一个没有分成 2 个长度为 5 的列表,或者你的前两个列表没有平均分配?
  • 这个比较模糊,一个简单的方法就是在高于平均值的时候进行分割。
  • @Vladislav 您的 arrays 中的元素是否都大于 0?如果他们不是你的问题是不合适的,或者至少在我看来是这样。例如,对于[1, -1, 1, -1, ..., 1, -1],每个偶数长度的块都是一个可能的解决方案。
  • @gboffi 这个问题并不是说必须有一个独特的解决方案..

标签: python python-2.7


【解决方案1】:

您可以在列表中循环创建切片,然后使用正确的键选择带有 min 函数的正确对:

>>> def find_min(l):
...     return min(((l[:i],l[i:]) for i in range(len(l))),key=lambda x:abs((sum(x[0])-sum(x[1]))))

演示:

>>> l=[10, 1, 1, 1]
>>> find_min(l)
([10], [1, 1, 1])
>>> l=[9, 5, 5, 8, 2, 2, 18, 8, 3, 9, 4]
>>> find_min(l)
([9, 5, 5, 8, 2, 2], [18, 8, 3, 9, 4])
>>> l=[19, 8, 9, 1, 14, 1, 16, 4, 15, 5]
>>> find_min(l)
([19, 8, 9, 1, 14], [1, 16, 4, 15, 5])

【讨论】:

  • 我想听听投反对票的原因,直到如果我的回答有问题我会纠正它,你也可以知道这个原因的社区
  • 似乎有人不喜欢这个问题及其答案,因为他自己无法解决问题!!!!并对所有答案和问题投反对票:D
  • @IgorHatarist 是的! ;) 我认为这是一个很好的问题,我也投了赞成票!
  • @thefourtheye 我不明白你的意思!
  • foo([19, 8, 9, 1, 14, 1, 16, 4, 15, 5]) 与预期输出不匹配。
【解决方案2】:

类似的东西:

def foo(lst):
    total_sum = sum(lst)
    i = 1
    while sum(lst[:i]) < total_sum / 2:  # iterate over the list slices until we hit the "middle" 
        if sum(lst[:i+1]) >= total_sum / 2:  # also make sure that we won't go further
            break

        i += 1

    return [lst[:i], lst[i:]]

测试:

[[10], [1, 1, 1]]                         # 10 + 3
[[2, 5], [9, 5, 1, 1]]                    # 7 + 16
[[9, 5, 5, 8, 2, 2], [18, 8, 3, 9, 4]]    # 31 + 42
[[17, 15, 2, 18, 7], [20, 3, 20, 12, 7]]  # 59 + 62
[[19, 8, 9, 1], [14, 1, 16, 4, 15, 5]]    # 37 + 55

【讨论】:

    【解决方案3】:

    假设在列表的累积总和尽可能接近整个列表总和的一半的点对列表进行分区时得到最优拆分:

    import numpy as np
    
    x = [19, 8, 9, 1, 14, 1, 16, 4, 15, 5]
    csum = np.cumsum(x)
    ix = np.argmin(abs(csum-csum[-1]/2)) + 1
    result = [x[:ix], x[ix:]]
    

    结果:

    [[19, 8, 9, 1, 14], [1, 16, 4, 15, 5]]
    

    【讨论】:

    【解决方案4】:
    from itertools import combinations
    from collections import Counter
    
    
    def most_equal_pairs(seq, n=None):
        seq_mapping = dict(enumerate(seq))
    
        if len(seq_mapping) < 2:
            raise ValueError()
        if len(seq_mapping) == 2:
            first, second = seq_mapping.values()
            yield [first], [second], abs(first - second)
            return
    
        ids = set(seq_mapping)
    
        def get_chunk_by_ids(ids):
            return [seq_mapping[i] for i in ids]
    
        def get_chunk_sum_by_ids(ids):
            return sum(get_chunk_by_ids(ids))
    
        pairs = Counter()
    
        for comb_len in range(1, len(ids) - 1):
            for first_comb in combinations(ids, comb_len):
                second_comb = tuple(ids - set(first_comb))
                first_sum = get_chunk_sum_by_ids(first_comb)
                second_sum = get_chunk_sum_by_ids(second_comb)
                diff = abs(first_sum - second_sum)
                pairs[(first_comb, second_comb)] = -diff
    
        for (first_comb_ids, second_comb_ids), diff in pairs.most_common(n):
            first_comb = get_chunk_by_ids(first_comb_ids)
            second_comb = get_chunk_by_ids(second_comb_ids)
            yield first_comb, second_comb, abs(diff)
    
    
    def test(seq):
        pairs = list(most_equal_pairs(seq))
        diff_seq = []
    
        for first, second, diff in pairs:
            assert abs(sum(first) - sum(second)) == abs(diff)
            diff_seq.append(diff)
    
        assert tuple(sorted(diff_seq)) == tuple(diff_seq)
        best_pair = pairs[0]
        first, second, diff = best_pair
        return first, second, sum(first), sum(second), diff
    

    结果

    >>> test([10, 1, 1, 1])
    ([10], [1, 1, 1], 10, 3, 7)
    
    >>> test([2, 5, 9, 5, 1, 1])
    ([2, 9, 1], [5, 5, 1], 12, 11, 1)
    
    >>> test([9, 5, 5, 8, 2, 2, 18, 8, 3, 9, 4])
    ([5, 8, 2, 2, 8, 3, 9], [9, 5, 4, 18], 37, 36, 1)
    
    >>> test([17, 15, 2, 18, 7, 20, 3, 20, 12, 7])
    ([18, 3, 20, 12, 7], [17, 15, 2, 7, 20], 60, 61, 1)
    
    >>> test([19, 8, 9, 1, 14, 1, 16, 4, 15, 5])
    ([19, 9, 14, 4], [8, 1, 1, 16, 15, 5], 46, 46, 0)
    

    【讨论】:

      【解决方案5】:

      这是我的解决方案:

      def sum(*args):
          total = 0
          if len(args) > 0:
              for i in args:
                  for element in i:
                      total += element
          return total
      
      def foo(Input):
          size = len(Input)
          checkLeftCross = 0
          previousLeft = 0
          previousRight = 0
          currentLeft = 0
          currentRight = 0
          targetIndex = 0
          for i in range(size):
              currentLeft = sum(Input[0:i])
              currentRight = sum(Input[i:size])
              if currentLeft >= currentRight:
                  targetIndex = i
                  break
              else:
                  previousLeft = currentLeft
                  previousRight = currentRight
      
          diffPrev = previousRight - previousLeft
          diffCurr = currentLeft - currentRight
      
          if diffPrev > diffCurr:
              return Input[0:targetIndex], Input[targetIndex:size]
          else:
              return Input[0:targetIndex-1], Input[targetIndex-1:size]
      
      def main():
          print foo([2, 5, 9, 5, 1, 1])
          print foo([10,1,1,1])
          print foo([9, 5, 5, 8, 2, 2, 18, 8, 3, 9, 4])
          print foo([17, 15, 2, 18, 7, 20, 3, 20, 12, 7])
          print foo([19, 8, 9, 1, 14, 1, 16, 4, 15, 5])
      
      if __name__ == "__main__":
          main()
      

      解释:

      1. 我使用了函数sum 来返回列表中所有元素的总和。
      2. 函数 foo 在检查是否被拆分后返回 2 个列表 当前拆分比之前的拆分更好或更差,基于 2 个连续总和之间的差。

      输出:

      ([2, 5], [9, 5, 1, 1])
      ([10], [1, 1, 1])
      ([9, 5, 5, 8, 2, 2], [18, 8, 3, 9, 4])
      ([17, 15, 2, 18, 7], [20, 3, 20, 12, 7])
      ([19, 8, 9, 1, 14], [1, 16, 4, 15, 5])
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多