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

时间:2019-01-24 18:19:21

标签: python

我正在尝试找到一种在python中将数量分配到给定的插槽中的方法。

例如:

将7个橙子分配到4个板上将返回:

[2, 2, 2, 1]

在4个盘子中的10个橘子是:

[3, 3, 2, 2]

5 个答案:

答案 0 :(得分:40)

从概念上讲,您要做的是计算7 // 4 = 17 % 4 = 3。这意味着所有盘子都得到1个完整的橙色。其余的3个告诉您,其中三个板块会得到一个额外的橙色。

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

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

以您的示例为例:

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

为完整起见,您可能需要检查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]

答案 1 :(得分:15)

您想查看Bresenham's algorithm的画线(即,尽可能“笔直”地将X像素分布在Y范围内;将其应用于分布问题很简单)。

这是我发现的实现here

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

答案 2 :(得分:3)

另请参阅more_itertools.distribute,第三方工具及其source code

代码

在这里,我们将m项物品逐个分发到n个垃圾箱中,并对每个垃圾箱进行计数。

import more_itertools as mit


def sum_distrib(m, n):
    """Return an iterable of m items distributed across n spaces."""
    return [sum(x) for x in mit.distribute(n, [1]*m)]

演示

sum_distrib(10, 4)
# [3, 3, 2, 2]

sum_distrib(7, 4)
# [2, 2, 2, 1]

sum_distrib(23, 17)
# [2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

示例

这个想法类似于在玩家之间分配一副纸牌。这是Slapjack

的初始游戏
import random
import itertools as it


players = 8
suits = list("♠♡♢♣")
ranks = list(range(2, 11)) + list("JQKA")
deck = list(it.product(ranks, suits))
random.shuffle(deck)

hands = [list(hand) for hand in mit.distribute(players, deck)]
hands
# [[('A', '♣'), (9, '♠'), ('K', '♣'), (7, '♢'), ('A', '♠'), (5, '♠'), (2, '♠')],
#  [(6, '♣'), ('Q', '♠'), (5, '♢'), (5, '♡'), (3, '♡'), (8, '♡'), (7, '♣')],
#  [(7, '♡'), (9, '♢'), (2, '♢'), (9, '♡'), (7, '♠'), ('K', '♠')],
#   ...]

“在所有[8]个玩家之间尽可能平均地分配卡片”:

[len(hand) for hand in hands]
# [7, 7, 7, 7, 6, 6, 6, 6]

答案 3 :(得分:2)

疯狂物理学家的答案是完美的。但是,如果您想在盘子上分配橘子制服(例如,在7个橘子和4个盘子示例中,2 3 2 32 2 3 3),这是一个简单的想法。

简单案例

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

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

[4, 4, 4, 4, 4, 4, 4]

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

[4, 5, 4, 5, 4, 5, 4]

情况并非如此

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

第1步:您仍然像疯子物理学家那样开始了一个欧几里得除法式34 = 4*7 + 6。在每个盘子里放4个橙子,剩下的6个。

[4, 4, 4, 4, 4, 4, 4]

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

[5, 5, 5, 5, 5, 5, 4+apple]

但是,如果要保持均匀性,则必须将该苹果放在其他地方!为什么不尝试在第一种情况下分发像橘子这样的苹果? 7个盘子,1个苹果:7 = 1*7 + 0leap是7,从leap/2开始,即3:

[5, 5, 5, 4+apple, 5, 5, 5]

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

[5, 5, 5, 4, 5, 5, 5]

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

代码

请多说点代码:

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

一些测试:

>>> 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]

答案 4 :(得分:0)

不确定如何运行。但它返回的结果完全相同

a = 23
b = 17
s = pd.Series(pd.cut(mylist, b), index=mylist)
s.groupby(s).size().values