通过一组槽尽可能均匀地分配整数量

fma*_*gno 23 python

我试图找到一种优雅的方式来实现将数量分配到python中给定的一组槽中.

例如:

分布在4个平板上的7个橙子将返回:

[2, 2, 2, 1]
Run Code Online (Sandbox Code Playgroud)

4个板块上的10个橙子将是:

[3, 3, 2, 2]
Run Code Online (Sandbox Code Playgroud)

Mad*_*ist 40

从概念上讲,你想要做的是计算7 // 4 = 17 % 4 = 3.这意味着所有的盘子都变成了整个橙色.3的剩余部分告诉你三个板块得到额外的橙色.

divmod内置是同时获得这两个量的快捷方式:

def distribute(oranges, plates):
    base, extra = divmod(oranges, plates)
    return [base + (i < extra) for i in range(plates)]
Run Code Online (Sandbox Code Playgroud)

用你的例子:

>>> distribute(oranges=7, plates=4)
[2, 2, 2, 1]
Run Code Online (Sandbox Code Playgroud)

为了完整起见,您可能希望检查oranges是否为非负数且plates为正数.鉴于这些条件,这里有一些额外的测试用例:

>>> distribute(oranges=7, plates=1)
[7]

>>> distribute(oranges=0, plates=4)
[0, 0, 0, 0]

>>> distribute(oranges=20, plates=2)
[10, 10]

>>> distribute(oranges=19, plates=4)
[5, 5, 5, 4]

>>> distribute(oranges=10, plates=4)
[3, 3, 2, 2]
Run Code Online (Sandbox Code Playgroud)

  • @Lserni.这种方法可以保证最大和最小板之间的差异最大为1.您对均匀性有哪些额外的标准? (3认同)
  • 虽然我同意剩余部分的分配可以从“推到左边”得到改善,但OP没有做任何事情让我相信这就是他们想要的东西。 (2认同)

LSe*_*rni 15

你想看看Bresenham的绘制线算法(即在Y范围内分配X像素尽可能"直线";这对分布问题的应用是直截了当的).

这是我在这里找到的一个实现:

def get_line(start, end):
    """Bresenham's Line Algorithm
    Produces a list of tuples from start and end

    >>> points1 = get_line((0, 0), (3, 4))
    >>> points2 = get_line((3, 4), (0, 0))
    >>> assert(set(points1) == set(points2))
    >>> print points1
    [(0, 0), (1, 1), (1, 2), (2, 3), (3, 4)]
    >>> print points2
    [(3, 4), (2, 3), (1, 2), (1, 1), (0, 0)]
    """
    # Setup initial conditions
    x1, y1 = start
    x2, y2 = end
    dx = x2 - x1
    dy = y2 - y1

    # Determine how steep the line is
    is_steep = abs(dy) > abs(dx)

    # Rotate line
    if is_steep:
        x1, y1 = y1, x1
        x2, y2 = y2, x2

    # Swap start and end points if necessary and store swap state
    swapped = False
    if x1 > x2:
        x1, x2 = x2, x1
        y1, y2 = y2, y1
        swapped = True

    # Recalculate differentials
    dx = x2 - x1
    dy = y2 - y1

    # Calculate error
    error = int(dx / 2.0)
    ystep = 1 if y1 < y2 else -1

    # Iterate over bounding box generating points between start and end
    y = y1
    points = []
    for x in range(x1, x2 + 1):
        coord = (y, x) if is_steep else (x, y)
        points.append(coord)
        error -= abs(dy)
        if error < 0:
            y += ystep
            error += dx

    # Reverse the list if the coordinates were swapped
    if swapped:
        points.reverse()
    return points
Run Code Online (Sandbox Code Playgroud)

  • @gmagno.这不仅仅是关于优雅.该解决方案实际上以不同于我的方式分配箱.如果还有一个额外的要求是尽可能保持峰值分布均匀,我的根本不会削减它.但是,是的,这更麻烦,我猜OP想要初学者版. (3认同)

jfe*_*ard 5

疯狂物理学家的答案是完美的。但是,如果你要分发uniformley上板橘子(如2 3 2 3VS2 2 3 3在7个橙子和4个板为例),这里是一个简单的想法。

简易案例

以 31 个橙子和 7 个盘子为例。

第 1 步:您像疯狂物理学家一样开始使用欧几里德除法:31 = 4*7 + 3。每个盘子里放 4 个橙子,剩下的 3 个。

[4, 4, 4, 4, 4, 4, 4]
Run Code Online (Sandbox Code Playgroud)

第 2 步:现在,您的盘子比橙子多,这完全不同:您必须在橙子之间分配盘子。您还剩 7 个盘子和 3 个橙子:7 = 2*3 + 1。每个橙子将有 2 个盘子(您还剩下一个盘子,但没关系)。让我们把这种2leap。开始leap/2会很漂亮:

[4, 5, 4, 5, 4, 5, 4]
Run Code Online (Sandbox Code Playgroud)

没那么容易的案子

那是最简单的情况。34 个橙子和 7 个盘子会发生什么?

第 1 步:您仍然像 Mad Physicist 一样开始使用欧几里得除法:34 = 4*7 + 6。每个盘子里放 4 个橙子,剩下的 6 个。

[4, 4, 4, 4, 4, 4, 4]
Run Code Online (Sandbox Code Playgroud)

第 2 步:现在,您还剩下 7 个盘子和 6 个橙子:7 = 1*6 + 1。每个橙子有一个盘子。但是等等……我没有 7 个橙子!别怕,我借给你一个苹果:

[5, 5, 5, 5, 5, 5, 4+apple]
Run Code Online (Sandbox Code Playgroud)

但是如果你想要一些统一,你必须把那个苹果放在别处!为什么不尝试在第一种情况下像橙子一样分发苹果?7个盘子,1个苹果:7 = 1*7 + 0。的leap是如图7所示,在开始leap/2,即3:

[5, 5, 5, 4+apple, 5, 5, 5]
Run Code Online (Sandbox Code Playgroud)

第 3 步。你欠我一个苹果。请把我的苹果还给我:

[5, 5, 5, 4, 5, 5, 5]
Run Code Online (Sandbox Code Playgroud)

总结一下:如果你只剩下几个橙子,你就分布峰,否则你分布谷。(免责声明:我是这个“算法”的作者,我希望它是正确的,但如果我错了,请纠正我!

编码

话不多说,上代码:

def distribute(oranges, plates):
    base, extra = divmod(oranges, plates) # extra < plates
    if extra == 0:
        L = [base for _ in range(plates)]
    elif extra <= plates//2:
        leap = plates // extra
        L = [base + (i%leap == leap//2) for i in range(plates)]
    else: # plates/2 < extra < plates
        leap = plates // (plates-extra) # plates - extra is the number of apples I lent you
        L = [base + (1 - (i%leap == leap//2)) for i in range(plates)]
    return L
Run Code Online (Sandbox Code Playgroud)

一些测试:

>>> distribute(oranges=28, plates=7)
[4, 4, 4, 4, 4, 4, 4]
>>> distribute(oranges=29, plates=7)
[4, 4, 4, 5, 4, 4, 4]
>>> distribute(oranges=30, plates=7)
[4, 5, 4, 4, 5, 4, 4]
>>> distribute(oranges=31, plates=7)
[4, 5, 4, 5, 4, 5, 4]
>>> distribute(oranges=32, plates=7)
[5, 4, 5, 4, 5, 4, 5]
>>> distribute(oranges=33, plates=7)
[5, 4, 5, 5, 4, 5, 5]
>>> distribute(oranges=34, plates=7)
[5, 5, 5, 4, 5, 5, 5]
>>> distribute(oranges=35, plates=7)
[5, 5, 5, 5, 5, 5, 5]
Run Code Online (Sandbox Code Playgroud)