首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何通过编程将圈时间分组成团队,以尽量减少差异?

如何通过编程将圈时间分组成团队,以尽量减少差异?
EN

Stack Overflow用户
提问于 2017-06-30 11:03:19
回答 4查看 189关注 0票数 5

考虑到以下(任意)搭圈时间:

代码语言:javascript
复制
John: 47.20
Mark: 51.14
Shellie: 49.95
Scott: 48.80
Jack: 46.60
Cheryl: 52.70
Martin: 57.65
Karl: 55.45
Yong: 52.30
Lynetta: 59.90
Sueann: 49.24
Tempie: 47.88
Mack: 51.11
Kecia: 53.20
Jayson: 48.90
Sanjuanita: 45.90
Rosita: 54.43
Lyndia: 52.38
Deloris: 49.90
Sophie: 44.31
Fleta: 58.12
Tai: 61.23
Cassaundra: 49.38 
Oren: 48.39

我们正在进行一场卡丁车耐力比赛,而不是允许团队挑选,而是编写一个工具来处理最初的排位赛时间,然后吐出最接近的分组。

我最初的调查让我觉得这是一个团状图形类型的情况,但我从来没有玩过绘图算法,我觉得有点超出了我的深度。

什么是最快/最简单的方法,产生3人组与最接近的总体平均圈时间,以消除他们之间的总体优势/差异?

这是我可以使用networkx实现的东西吗?如果可以的话,我如何最好地定义上面的数据集的图表?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2017-06-30 18:09:46

当你遇到这样的问题时,一种方法就是利用随机性。

当其他人说他们认为X或Y应该工作时,我知道我的算法至少会收敛到一个局部最大值。如果您可以证明任何状态空间都可以通过成对交换(对于旅行销售人员问题是正确的属性)从其他任何状态空间到达,则该算法将找到全局最优(给定的时间)。

此外,该算法试图最小化组间平均时间的标准差,因此它提供了一个自然的度量,说明你得到的答案有多好:即使结果不是精确的,得到0.058的标准差对你的目的来说也是足够接近的。

换句话说:可能有一个精确的解决方案,但随机化的解决方案通常很容易想象,编码不需要很长时间,可以很好地收敛,并且能够产生可接受的答案。

代码语言:javascript
复制
#!/usr/bin/env python3

import numpy as np
import copy
import random

data = [
  (47.20,"John"),
  (51.14,"Mark"),
  (49.95,"Shellie"),
  (48.80,"Scott"),
  (46.60,"Jack"),
  (52.70,"Cheryl"),
  (57.65,"Martin"),
  (55.45,"Karl"),
  (52.30,"Yong"),
  (59.90,"Lynetta"),
  (49.24,"Sueann"),
  (47.88,"Tempie"),
  (51.11,"Mack"),
  (53.20,"Kecia"),
  (48.90,"Jayson"),
  (45.90,"Sanjuanita"),
  (54.43,"Rosita"),
  (52.38,"Lyndia"),
  (49.90,"Deloris"),
  (44.31,"Sophie"),
  (58.12,"Fleta"),
  (61.23,"Tai"),
  (49.38 ,"Cassaundra"),
  (48.39,"Oren")
]

#Divide into initial groupings
NUM_GROUPS = 8
groups = []
for x in range(NUM_GROUPS): #Number of groups desired
  groups.append(data[x*len(data)//NUM_GROUPS:(x+1)*len(data)//NUM_GROUPS])

#Ensure all groups have the same number of members
assert all(len(groups[0])==len(x) for x in groups)

#Get average time of a single group
def FitnessGroup(group): 
  return np.average([x[0] for x in group])

#Get standard deviation of all groups' average times
def Fitness(groups):
  avgtimes = [FitnessGroup(x) for x in groups] #Get all average times
  return np.std(avgtimes) #Return standard deviation of average times

#Initially, the best grouping is just the data
bestgroups  = copy.deepcopy(groups)
bestfitness = Fitness(groups)

#Generate mutations of the best grouping by swapping two randomly chosen members
#between their groups
for x in range(10000): #Run a large number of times
  groups = copy.deepcopy(bestgroups)       #Always start from the best grouping
  g1 = random.randint(0,len(groups)-1)     #Choose a random group A
  g2 = random.randint(0,len(groups)-1)     #Choose a random group B
  m1 = random.randint(0,len(groups[g1])-1) #Choose a random member from group A
  m2 = random.randint(0,len(groups[g2])-1) #Choose a random member from group B
  groups[g1][m1], groups[g2][m2] = groups[g2][m2], groups[g1][m1] #Swap 'em
  fitness = Fitness(groups)                #Calculate fitness of new grouping
  if fitness<bestfitness:                  #Is it a better fitness?
    bestfitness = fitness                  #Save fitness
    bestgroups  = copy.deepcopy(groups)    #Save grouping

#Print the results
for g in bestgroups:
  for m in g:
    print("{0:15}".format(m[1]), end='') 
  print("{0:15.3f}".format(FitnessGroup(g)), end='')
  print("")
print("Standard deviation of teams: {0:.3f}".format(bestfitness))

运行几次后,标准偏差为0.058:

代码语言:javascript
复制
Cheryl         Kecia          Oren                    51.430
Tempie         Mark           Karl                    51.490
Fleta          Deloris        Jack                    51.540
Lynetta        Scott          Sanjuanita              51.533
Mack           Rosita         Sueann                  51.593
Shellie        Lyndia         Yong                    51.543
Jayson         Sophie         Tai                     51.480
Martin         Cassaundra     John                    51.410
Standard deviation of teams: 0.058
票数 2
EN

Stack Overflow用户

发布于 2017-06-30 14:02:23

如果我的理解正确,只需对时间列表进行排序,并将前三名、下三名、前三名进行分组。

编辑:我没有正确理解

所以,我们的想法是把N个人分组成N/3团队,使N/3团队的平均次数比每个团队中的3人更接近,就像我错误地解释得越近越好。在这种情况下,我认为您仍然可以从按时间的递减顺序排序N个驱动程序开始。然后,初始化N/3团队的空列表。然后,对于每一名车手,按圈时间的顺序递减,将他们分配给当前总圈时间最小的车队(或者其中一支队伍,如果是平手)。这是一个简单的垃圾箱包装算法的变种。

下面是一个简单的Python实现:

代码语言:javascript
复制
times = [47.20, 51.14, 49.95, 48.80, 46.60, 52.70, 57.65, 55.45, 52.30, 59.90, 49.24, 47.88, 51.11, 53.20, 48.90, 45.90, 54.43, 52.38, 49.90, 44.31, 58.12, 61.23, 49.38, 48.39]

Nteams = len(times)/3
team_times = [0] * Nteams
team_members = [[]] * Nteams

times = sorted(times,reverse=True)
for m in range(len(times)):
    i = team_times.index(min(team_times))
    team_times[i] += times[m]
    team_members[i] = team_members[i] + [m]

for i in range(len(team_times)):
    print(str(team_members[i]) + ": avg time " + str(round(team_times[i]/3,3)))

其输出是

代码语言:javascript
复制
[0, 15, 23]: avg time 51.593
[1, 14, 22]: avg time 51.727
[2, 13, 21]: avg time 51.54
[3, 12, 20]: avg time 51.6
[4, 11, 19]: avg time 51.48
[5, 10, 18]: avg time 51.32
[6, 9, 17]: avg time 51.433
[7, 8, 16]: avg time 51.327

(请注意,团队成员的编号指的是他们从0开始按圈时间递减的顺序,而不是他们最初的顺序)。

其中一个问题是,如果时间变化很大,那么没有硬性限制让每支球队的球员人数精确到3人。然而,就你的目的而言,如果接力赛接近了接力赛,那也许没什么问题,而且在时间上的距离远远小于平均时间的情况下,这种情况可能是罕见的。

编辑,如果你只想在每支球队中有3名球员,那么在所有情况下,代码都可以进行小修改,以便在每一步找到没有指定三名球员的总圈时间最少的团队。这需要对主代码块进行小修改:

代码语言:javascript
复制
times = sorted(times,reverse=True)
for m in range(len(times)):
    idx = -1
    for i in range(Nteams):
        if len(team_members[i]) < 3:
            if (idx == -1) or (team_times[i] < team_times[idx]):
                idx = i
    team_times[idx] += times[m]
    team_members[idx] = team_members[idx] + [m]

对于问题中的例子,上面的解决方案当然是相同的,因为它没有尝试适合每队3名以上或少于3名球员。

票数 2
EN

Stack Overflow用户

发布于 2017-06-30 16:41:46

下面的算法似乎运行得很好。它需要最快和最慢的人留下来,然后找到中间的人,使群体平均是最接近全球平均水平。由于极值首先被耗尽,尽管选择池有限,但最终的平均值不应该太远。

代码语言:javascript
复制
from bisect import bisect

times = sorted([47.20, 51.14, 49.95, 48.80, 46.60, 52.70, 57.65, 55.45, 52.30, 59.90, 49.24, 47.88, 51.11, 53.20, 48.90, 45.90, 54.43, 52.38, 49.90, 44.31, 58.12, 61.23, 49.38, 48.39])
average = lambda c: sum(c)/len(c)

groups = []
average_time = average(times)

while times:
    group = [times.pop(0), times.pop()]

    # target value for the third person for best average
    target = average_time * 3 - sum(group)
    index = min(bisect(times, target), len(times) - 1)

    # adjust if the left value is better than the right
    if index and abs(target - times[index-1]) < abs(target - times[index]):
        index -= 1

    group.append(times.pop(index))
    groups.append(group)

# [44.31, 61.23, 48.9]
# [45.9, 59.9, 48.8]
# [46.6, 58.12, 49.9]
# [47.2, 57.65, 49.38]
# [47.88, 55.45, 51.14]
# [48.39, 54.43, 51.11]
# [49.24, 53.2, 52.3]
# [49.95, 52.7, 52.38]

排序和迭代二进制搜索都是O(n log ),因此总复杂度为O(n log )。不幸的是,将其扩展到更大的群体可能会很困难。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44844927

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档