小编典典

根据Python中的近似比例分配项目

algorithm

请参阅下面的更新…

我正在编写一个Python模拟程序,该程序从任意目标池中分配任意数量的假想玩家一个目标。目标的稀缺度有两个不同的水平或比例,prop_high并且prop_low大约为3:1。

例如,如果有16个玩家和4个进球,或者8个玩家和4个进球,则两个目标池如下所示:

{'A': 6, 'B': 6, 'C': 2, 'D': 2}
{'A': 3, 'B': 3, 'C': 1, 'D': 1}

…目标A和B的发生频率是C和D的3倍。6 + 6 + 2 + 2 = 16,与模拟中的玩家数量相对应,这很好。

我希望有一个与球员人数相等并分配的进球池,以便进球数大约prop_highprop_low进球数的三倍。

根据粗略或近似比率构建分配算法的最佳方法是什么—可以处理舍入的方法?

更新:

假设有8位玩家,则这是从2到8个进球的分配方式的希望(prop_high玩家已加注星标):

    A   B   C   D   E   F   G   H
2   6*  2                       
3   6*  1   1                   
4   3*  3*  1   1               
5   3*  2*  1   1   1           
6   2*  2*  1*  1   1   1       
7   2*  1*  1*  1   1   1   1   
8   1*  1*  1*  1*  1   1   1   1

这些数字与玩家不符。例如,在有5个进球和8个球员的情况下,进球A和B在泳池中的比例很高(分别为3和2),而进球C,D和E则比较少见(每个1)。

当目标数量奇数时,最后一个目标prop_high比其他目标少一个。随着进球数逼近玩家人数,每项prop_high都会少一个,直到最后,池中只有一个进球。

我下面要做的是将数量分配给池的高端和低端,然后对高端进行调整,根据目标数与玩家数的接近程度减去数值。它可以与8个玩家一起很好地工作(池中的进球数始终等于8),仅此而已。

我绝对确定有一种更好的,更Python化的方式来处理这种算法,而且我很确定这是一种相对常见的设计模式。我只是不知道从哪里开始搜寻以找到一种更优雅的方式来处理这种结构(而不​​是我现在使用的蛮力方法)

import string
import math
letters = string.uppercase

num_players = 8
num_goals = 5
ratio = (3, 1)

prop_high = ratio[0] / float(sum(ratio)) / (float(num_goals)/2)
prop_low = ratio[1] / float(sum(ratio)) / (float(num_goals)/2)

if num_goals % 2 == 1:
    is_odd = True
else:
    is_odd = False

goals_high = []
goals_low = []
high = []
low = []

# Allocate the goals to the pool. Final result will be incorrect.
count = 0
for i in range(num_goals):
    if count < num_goals/2:         # High proportion
        high.append(math.ceil(prop_high * num_players))
        goals_high.append(letters[i])
    else:                           # Low proportion
        low.append(math.ceil(prop_low * num_players))
        goals_low.append(letters[i])
    count += 1


# Make adjustments to the pool allocations to account for rounding and odd numbers
ratio_high_total = len(high)/float(num_players)
overall_ratio = ratio[1]/float(sum(ratio))
marker = (num_players / 2) + 1
offset = num_goals - marker

if num_players == num_goals:
    for i in high:
        high[int(i)] -= 1
elif num_goals == 1:
    low[0] = num_players
elif ratio_high_total == overall_ratio and is_odd:
    high[-1] -= 1
elif ratio_high_total >= overall_ratio:         # Upper half of possible goals
    print offset

    for i in range(offset):
        index = -(int(i) + 1)
        high[index] -= 1

goals = goals_high + goals_low
goals_quantities = high + low


print "Players:", num_players
print "Types of goals:", num_goals
print "Total goals in pool:", sum(goals_quantities)
print "High pool:", goals_high, high
print "Low pool:", goals_low, low
print goals, goals_quantities
print "High proportion:", prop_high, " || Low proportion:", prop_low

阅读 368

收藏
2020-07-28

共1个答案

小编典典

我不会尝试正确地计算分数,而只是一次以适当的比例分配目标。在这里,“
allocate_goals”生成器将目标分配给每个低比率目标,然后分配给每个较高比率目标(重复3次)。然后重复。调用方inalloc使用itertools.islice以所需的数量(玩家数量)切断此无限生成器。

import collections
import itertools
import string

def allocate_goals(prop_low, prop_high):
    prop_high3 = prop_high * 3
    while True:
        for g in prop_low:
            yield g
        for g in prop_high3:
            yield g

def allocate(goals, players):
    letters = string.ascii_uppercase[:goals]
    high_count = goals // 2
    prop_high, prop_low = letters[:high_count], letters[high_count:]
    g = allocate_goals(prop_low, prop_high)
    return collections.Counter(itertools.islice(g, players))

for goals in xrange(2, 9):
    print goals, sorted(allocate(goals, 8).items())

它产生以下答案:

2 [('A', 6), ('B', 2)]
3 [('A', 4), ('B', 2), ('C', 2)]
4 [('A', 3), ('B', 3), ('C', 1), ('D', 1)]
5 [('A', 3), ('B', 2), ('C', 1), ('D', 1), ('E', 1)]
6 [('A', 2), ('B', 2), ('C', 1), ('D', 1), ('E', 1), ('F', 1)]
7 [('A', 2), ('B', 1), ('C', 1), ('D', 1), ('E', 1), ('F', 1), ('G', 1)]
8 [('A', 1), ('B', 1), ('C', 1), ('D', 1), ('E', 1), ('F', 1), ('G', 1), ('H', 1)]

这种方法的优点(除了我认为,这很容易理解)是,它很快可以将其转换为随机版本。

只需用以下命令替换allocate_goals:

def allocate_goals(prop_low, prop_high):
    all_goals = prop_low + prop_high * 3
    while True:
        yield random.choice(all_goals)
2020-07-28