根据Python中的大致比例分配项目 [英] Allocate items according to an approximate ratio in Python

查看:107
本文介绍了根据Python中的大致比例分配项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

查看更新下...

我正在写一个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发生3次尽可能经常C和D 6 + 6 + 2 + 2 = 16,其对应于玩家在模拟的数量,这是很好的

我想有目标相等的玩家数量和分布池,以便有大约三倍 prop_high 目标,有 prop_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化的方式来处理这种算法,我pretty的肯定这是一种比较常见的设计模式。我只是不知道从哪里开始使用Google找到一个更优雅的方式来处理这种类型的结构(而不​​是我用现在的蛮力方法)

 输入字符串
进口数学
信= string.uppercase

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

prop_high =比[0] /浮点(SUM(比))/(浮点(num_goals)/ 2)
prop_low =比[1] /浮点(SUM(比))/(浮点(num_goals)/ 2)

如果num_goals%2 == 1:
    is_odd = TRUE
其他:
    is_odd =假

goals_high = []
goals_low = []
高= []
低= []

#分配的目标到池中。最终的结果是不正确的。
数= 0
因为我在范围内(num_goals):
    如果计数< num_goals / 2:#高比例
        high.append(math.ceil(prop_high * num_players))
        goals_high.append(字母[I])
    其他:#低比例
        low.append(math.ceil(prop_low * num_players))
        goals_low.append(字母[I])
    数+ = 1


#作出调整池分配到账户的舍入和奇数
ratio_high_total = LEN(高)/浮点(num_players)
overall_ratio =比[1] /浮点(SUM(比))
标记=(num_players / 2)+1
偏移量= num_goals  - 标记

如果num_players == num_goals:
    因为我高:
        高[INT(I)]  -  = 1
ELIF num_goals == 1:
    低[0] = num_players
ELIF ratio_high_total == overall_ratio和is_odd:
    高[-1]  -  = 1
ELIF ratio_high_total> = overall_ratio:#上可能的目标一半
    打印偏移

    因为我在范围(偏移):
        指数=  - (INT(ⅰ)+ 1)的
        高[指数]  -  = 1

目标= goals_high + goals_low
goals_quantities =高+低


打印玩家,num_players
打印的目标类型:num_goals
打印池总目标:和(goals_quantities)
打印高池,goals_high,高
打印低池,goals_low,低
打印目标,goals_quantities
打印高比例,prop_high,||比重低,prop_low
 

解决方案

而不是试图让分数吧,我只是分配的目标之一在适当比例的时间。这里的'allocate_goals'发生器分配一个目标的每一个的低比率的目标,然后向每个的高比目标(重复3次)。然后它重复。呼叫者利用itertools.islice在分配在所要求的数目关闭此无限发生器切口(玩家的数量)。

 导入收藏
进口itertools
进口字符串

高清allocate_goals(prop_low,prop_high):
    prop_high3 = prop_high * 3
    而真正的:
        在prop_low G:
            产量摹
        在prop_high3 G:
            产量摹

高清分配(目标,玩家):
    字母= string.ascii_uppercase [进球]
    high_count =目标// 2
    prop_high,prop_low =字母[:high_count],信[high_count:]
    G = allocate_goals(prop_low,prop_high)
    返回collections.Counter(itertools.islice(G,播放器))

在的xrange目标(2,9):
    打印目标,排序(分配(目标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:

 高清allocate_goals(prop_low,prop_high):
    all_goals = prop_low + prop_high * 3
    而真正的:
        产量random.choice(all_goals)
 

See update below...

I'm writing a Python simulation that assigns an arbitrary number of imaginary players one goal from an arbitrary pool of goals. The goals have two different levels or proportions of scarcity, prop_high and prop_low, at approximately a 3:1 ratio.

For example, if there are 16 players and 4 goals, or 8 players and 4 goals, the two pools of goals would look like this:

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

...with goals A and B occurring 3 times as often as C and D. 6+6+2+2 = 16, which corresponds to the number of players in the simulation, which is good.

I want to have a pool of goals equal to the number of players and distributed so that there are roughly three times as many prop_high goals as there are prop_low goals.

What's the best way to build an allocation algorithm according to a rough or approximate ratio—something that can handle rounding?

Update:

Assuming 8 players, here's how the distributions from 2 to 8 goals should hopefully look (prop_high players are starred):

    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

These numbers don't correspond to players. For example, with 5 goals and 8 players, goals A and B have a high proportion in the pool (3 and 2 respectively) while goals C, D, and E are more rare (1 each).

When there's an odd number of goals, the last of the prop_high gets one less than the others. As the number of goals approaches the number of players, each of the prop_high items gets one less until the end, when there is one of each goal in the pool.

What I've done below is assign quantities to the high and low ends of the pool and then make adjustments to the high end, subtracting values according to how close the number of goals is to the number of players. It works well with 8 players (the number of goals in the pool is always equal to 8), but that's all.

I'm absolutely sure there's a better, more Pythonic way to handle this sort of algorithm, and I'm pretty sure it's a relatively common design pattern. I just don't know where to start googling to find a more elegant way to handle this sort of structure (instead of the brute force method I'm using for now)

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

解决方案

Rather than try to get the fractions right, I'd just allocate the goals one at a time in the appropriate ratio. Here the 'allocate_goals' generator assigns a goal to each of the low-ratio goals, then to each of the high-ratio goals (repeating 3 times). Then it repeats. The caller, in allocate cuts off this infinite generator at the required number (the number of players) using 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())

It produces this answer:

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

The great thing about this approach (apart from, I think, that it's easy to understand) is that it's quick to turn it into a randomized version.

Just replace allocate_goals with this:

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

这篇关于根据Python中的大致比例分配项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆