对元组列表进行排序,以便不希望按键分组

时间:2016-03-04 19:00:30

标签: python sorting tuples

我有一个代表用户的ID列表,我正在编写一个函数来将ID列表转换为用户匹配(配对)的时间表,如下所示:

ids = [1, 2, 3, 4]

与此相关的时间表如下所示:

week 1: (1, 2), (3, 4)
week 2: (1, 3), (2, 4)
week 3: (1, 4), (2, 3)
week 4: (1, 2), (3, 4) [repeat of week 1]

等等。我试图根据用户ID的数量和由此产生的组合,使用嵌套的for循环执行此操作。

ids = [1,2,3,4]
matchups = []

#generate all the combinations of matchups
for subset in itertools.combinations(ids,2):
    matchups.append(subset)

这将所有潜在的配对作为元组列表返回 - 太棒了!这是我正在寻找的核心。我现在的问题是弄清楚如何将其转化为可用的东西。例如,上面的代码返回matchups

的此列表
[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]

我正在尝试一个复杂的递归函数来构建每周唯一配对的列表,然后我意识到如果我按正确的顺序列出上面的列表,我可以简单地使用它从头到尾分配配对,不管是一周,还是在我到达终点时重复。也就是说,我可以使用每周所需的已知数量的配对和已知的周数来分配配对数周。

为此,我一直在试图找出如何根据相等的元素对元组列表进行排序。 sorted()方法在这种情况下有很多有用的实现方法,但我只能得到像结果一样的结果,如上所述。

我想找到一个sorted()的用法,它将返回以下内容,几乎就像反排序一样:

[(1, 2), (3, 4), (1, 3), (2, 4), (1, 4), (2, 3)]

有没有办法使用lambda呢?

编辑:我刚刚意识到第一个元素需要与第6个元素配对,第2个元素需要与第5个元素配对,第3个元素需要与第4个元素配对。我不知道这是否延伸到一般情况,但我希望它可能,因为我采取了其他措施来确保始终存在偶数个ID。

现在我确定有一些方法可以插入列表来实现这一点。

EDIT2:看起来之前的预感不正确 - 它不适用于6个ID,并且除此之外的任何事情都可能失败。我回过头来试图弄清楚是否有一种分散方式而不是基于键的排序

4 个答案:

答案 0 :(得分:1)

我相信您正在寻找的是规范的round-robin调度算法。

按照您认为方便的顺序列出所有玩家,分为两行:

1 2 3 4
5 6 7 8 

你的首轮配对是1-5,2-6,3-7,4-8。 连续几周,旋转除#1位置以外的所有位置:上排左移,下排右移;那些从结尾掉落的人(2和8)向上/向下移动到空位:

1 3 4 8
2 5 6 7 

如果您还需要平衡主场配对,那么在您完成此次骑行之后,请每隔一周返回并交换一行。

答案 1 :(得分:1)

潜在的单一解决方案,但它的工作原理。也许有人可以让它更加pythonic? :d

def antisort(x):
    if len(x) > 0:
        return [x[0]] + antisort(x[-1:0:-1])
    else:
        return []

>>> antisort([(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)])
[(1, 2), (3, 4), (1, 3), (2, 4), (1, 4), (2, 3)]

这个切片表示法只接受第一个元素之后的所有内容并将其反转:

x[-1:0:-1]

答案 2 :(得分:1)

这就是我在评论中的意思:

a = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
new = []

i, j = len(a)-1, 0

for n in range(i+1):
    if n % 2:
        new.append(a[i])
        i -= 1
    else:
        new.append(a[j])
        j += 1

print(new)

输出:

[(1, 2), (3, 4), (1, 3), (2, 4), (1, 4), (2, 3)]

答案 3 :(得分:1)

这是维基百科循环赛锦标赛算法的实现。

x = range(1, 11) #10 players

def round_robin(someIds):
    someIds = someIds[::2] + someIds[1::2]
    first, someIds = [someIds[0]], someIds[1:]
    n = len(someIds)
    for i in range(n):
        top = someIds[:n/2]
        bottom = someIds[n/2:]
        yield zip(first+top, bottom)
        someIds = someIds[-1:] + someIds[:-1]


for thing in round_robin(x):
    print thing

输出

[(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]
[(1, 3), (4, 5), (6, 7), (8, 9), (10, 2)]
[(1, 4), (5, 6), (7, 8), (9, 10), (2, 3)]
[(1, 5), (6, 7), (8, 9), (10, 2), (3, 4)]
[(1, 6), (7, 8), (9, 10), (2, 3), (4, 5)]
[(1, 7), (8, 9), (10, 2), (3, 4), (5, 6)]
[(1, 8), (9, 10), (2, 3), (4, 5), (6, 7)]
[(1, 9), (10, 2), (3, 4), (5, 6), (7, 8)]
[(1, 10), (2, 3), (4, 5), (6, 7), (8, 9)]