【问题标题】:Split list in almost equal parts and return bounds将列表拆分为几乎相等的部分并返回边界
【发布时间】:2018-10-04 05:50:22
【问题描述】:

我想拆分一个列表并返回边界,因此在扩展中不应存在单个元素。

例如

split(list(range(1,101)),2) 
# should return 
[[1,50],[51,100]]

split(list(range(1,101)),3)
# should return
[[1,33],[34,66],[67,100]]

split(list(range(1,6)),3)
# should return
[[1,2],[3,5]] # Ideally last element should merge with last if last one has no pair.

到目前为止我尝试过

def split(l, n):
    x = list(range(1, l+1))
    return [x[i:i+n] for i in range(0, len(x), int(len(x)/n))]

print(split(20, 2))

返回[[1, 2], [11, 12]] 而不是[[1, 10], [11, 20]]

【问题讨论】:

  • 这是因为,第一次 i 的值是 0 。因此,它将采用 x[0:2],因此该位置的值将分别为 1 和 2,对于下一个循环,它将是 11 和 12。我希望这将有助于您重新考虑逻辑部分。
  • 我真的觉得你的预期结果对于其中一些是不正确的,如果不仅仅是不一致的话。
  • 但是在你的问题中你分成了 3 个。
  • 不应该split(list(range(1,6)),3) 给出 3 个拆分,而不是 2 个拆分?
  • @pylang range(1,6) == [1,2,3,4,5] - 如果分成 3,你会得到 [ [1,2], [3,4]. [5,None] - 结果中不应该有“无范围”的 - 而是扩展之前的范围 - 因此[[1,2],[3,5]]

标签: python python-3.x list split


【解决方案1】:

这应该会有所帮助,它没有使用完整的列表范围,而只是使用您感兴趣的“边框”,一些创意zip()ping 并在创建范围后修复错误:

def split(l, n):
    stride =(l//n)
    r = list(range(1, l+stride, stride))  # overbuild needed range

    # adjust zipped value by 1 if needed
    k = [ [a , b-1 ] for a,b in zip(r,r[1:]) ]

    # fix special cases 
    for _ in range(n+1): # guesstimate of fixes needed

        if k[-1][0] == k[-1][1]: # last pair identical 
            k.pop()    # remove it and fix new last index to be l  instead
            k[-1][1] = l        
        elif k[-1][1] != l:
            k[-1][1] = l

    return k

print(split(100, 2))
print(split(100, 4))
print(split(103, 3))
print(split(6, 3))

输出:

[[1, 50], [51, 100]]                          # split(100, 2)
[[1, 25], [26, 50], [51, 75], [76, 100]]      # split(100, 4)
[[1, 34], [35, 68], [69, 103]]                # split(103, 3)
[[1, 2], [3, 4], [5, 6]]                      # split(6, 3)

我用您的示例对其进行了粗略的测试,检查逻辑是否适合您的所有特殊情况。

尤其是# guesstimate of fixes needed 可能太多了...我有一种预感,最多 2 个就足够了

【讨论】:

  • @chrisz 感谢您的时间安排 :) - 仍然觉得它有点作弊
  • @chrisz - 错字:split(6,3) 导致 [1,3],[4,6] - 现已修复并简化了压缩
【解决方案2】:

这是一个非常快速的解决方案,需要注意的是最长的子列表将位于最后而不是第一个(但是组仍将尽可能均匀):

def fast_chunks(start, stop, chunks):
  l = math.ceil(((stop-start)/chunks)-1)
  fin = []
  for j in range(chunks-1):
    fin.append([start, start+l])
    start += l + 1
  fin.append([start, stop])
  return fin

在行动:

In [41]: fast_chunks(1, 100, 2)
Out[41]: [[1, 50], [51, 100]]

In [42]: fast_chunks(1, 100, 3)
Out[42]: [[1, 33], [34, 66], [67, 100]]

In [43]: fast_chunks(1, 6, 2)
Out[43]: [[1, 3], [4, 6]]

使用numpynp.array_split 的简单解决方案:

def _split(start, stop, n):
    return [[i[0], i[-1]] for i in np.array_split(np.arange(start, stop+1), n)]

在行动:

In [54]: _split(1, 20, 2)
Out[54]: [[1, 10], [11, 20]]

In [55]: _split(1, 100, 2)
Out[55]: [[1, 50], [51, 100]]

In [56]: _split(1, 6, 2)
Out[56]: [[1, 3], [4, 6]]

在计时方面,fast_chunks 的表现要好于其他人:

In [45]: %timeit chrisz_fast_chunks(1, 1000000, 100)
24.5 µs ± 496 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [27]: %timeit artner_split(1000000, 100)
58 µs ± 1.02 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [22]: %timeit gahan_split(1000000, 100)
77 µs ± 280 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [23]: %timeit chrisz_split(1, 1000000, 100)
1.77 ms ± 39.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [24]: %timeit pylang_split(1000000, 100)
72 ms ± 445 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

【讨论】:

    【解决方案3】:
    def csplit(m,n):
        div_ = m//n
        step = div_ if div_ > 1 else 2  # determine step for range function (at least 2 'alternate steps')
        lis = []
        for i in range(1, m+1, step):
            if (m-(i+max(1, div_-1))) > 1:
                # append list only if remains at least two elements remains 
                lis.append([i,i+max(1, div_-1)])
            else:
                if not m == i:
                    # in case if m and i not equal and not more then one element left then construct list which include that element
                    lis.append([i, m])
                break  # break the loop from iterating any further
        return lis
    
    if __name__ == "__main__":
        print(csplit(100, 2))
        print(csplit(100, 3))
        print(csplit(5, 3))
    

    输出:

    [[1, 50], [51, 100]]
    [[1, 33], [34, 66], [67, 100]]
    [[1, 2], [3, 5]]
    

    同一个班轮:

    def csplit(m,n):
        return [[i,i+max(1, m//n-1)] if (m-(i+max(1, m//n-1))) > 1 else [i, m] for i in range(1, m+1, max(m//n, 2)) if not i==m]
    

    【讨论】:

    • @chrisz 我的变量未包含在第二个函数中(已更新)但没有语法错误..
    • 我将您的答案与 Chrisz 的答案合并。我确实喜欢速度,这就是为什么我一开始就接受了 Chrisz 的回答。感谢您的帮助,如果我判断错误,请见谅
    • @PanosKal;您也可以在此线程中发布您自己的答案。我想看看其他方式。
    【解决方案4】:

    我认为您的预期结果不一致,但这里有一些简单的方法可能会对您有所帮助。

    使用第三方库more_itertools(通过> pip install more_itertools安装):

    代码

    import more_itertools as mit
    
    
    def split(val, n):
        """Return a list of equally divided intervals."""
        a = [list(c)[0] for c in mit.divide(n, range(1, val+1))]
        b = [list(c)[-1] for c in mit.divide(n, range(1, val+1))]
        return list(zip(a, b))
    

    演示

    split(100, 2)
    # [(1, 50), (51, 100)]
    
    split(99, 3)
    # [(1, 33), (34, 66), (67, 99)]
    
    split(100, 3)
    # [(1, 34), (35, 67), (68, 100)]
    
    split(20, 2)
    # [(1, 10), (11, 20)]
    

    【讨论】:

      猜你喜欢
      • 2011-10-15
      • 2011-03-22
      • 2012-12-13
      • 2011-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多