Python中特定的洗牌列表

11 投票
4 回答
703 浏览
提问于 2025-04-17 09:26

我有一个小组的列表

[['a', 'b'], ['c', 'd', 'e'], ['f']]

我需要把这个列表变成一个扁平化的版本,并且打乱顺序

[a, b, c, d, e, f]

这样同一组的元素就不会靠得太近。比如说,

[a, c, b, d, f, e] 这样的顺序是可以的,而 [a, c, b, d, e, f] 就不行,因为 de 是同一组的。

我不在乎它们之间的距离是一个元素还是更多,但任何元素 必须 和同组的其他元素保持一定距离。有没有什么算法可以做到这一点?

这个算法还需要能判断如果无法实现的话。

4 个回答

0

这段代码写得很简单,虽然不太优雅:

example = [[1, 2], [3, 4, 5], [6]]
result = []
block = None
last_block = None

while example:
    if block:
        last_block = block

    block = choice(example)

    if last_block:
        example.append(last_block)

    element = choice(block)
    result.append(element)

    block.remove(element)
    example.remove(block)
    example = [a for a in example if a]

print result

不过可能会遇到一个问题,就是当选择的选项只有一个时,比如这个例子:

[1, 6, 2, 3, 4, 5]

当然,应该有办法处理这种情况,但现在我希望这段代码能给你一些思路,帮助你解决问题。

  • 因为偶尔会出现异常,所以进行了编辑。
  • 我忘了不要把“list”当作变量名。感谢你的提醒,现在已经改正了。
2

让我用一种不同于当前答案的方法来解释

基本的原则是对列表中的每个子列表进行洗牌,然后根据长度进行排序,接着使用zip_longest将这些子列表结合起来,最后用chain把它们连接在一起并展开。这里特别要注意的是,我们如何将一个列表作为可变参数传递给迭代器,这样做让事情变得简单多了:-)

假设你的列表是

yourList=[['a', 'b'],['c', 'd', 'e'],['f']]

我的工作列表(为了保留原始列表而复制的)

workList=yourList[::]

对你列表中的每个子列表进行洗牌

[random.shuffle(x) for x in workList]

接下来,我们根据每个子列表的长度对列表进行排序

workList.sort(key=len,reverse=True)

最后,对洗牌后的列表进行连接

[x for x in itertools.chain(*[x for x in itertools.izip_longest(*workList)]) if x]

最终你的列表看起来是这样的

['e', 'b', 'f', 'c', 'a', 'd']
5

这段代码会复制你原来的小组列表,然后每次从当前剩下的最大的小组中随机选一个元素。虽然不是完全随机,但在没有哪个小组的元素超过总数一半的情况下,这种方法应该是有效的。

import random

list_of_groups = [['a', 'b'], ['c', 'd', 'e'], ['f']]
l = [group[:] for group in list_of_groups] # make a copy
random.shuffle(l)
out = []
prev_i = -1
while any(a for a in l):
    new_i = max(((i,a) for i,a in enumerate(l) if i != prev_i), key=lambda x: len(x[1]))[0]
    out.append(l[new_i].pop(random.randint(0, len(l[new_i]) - 1)))
    prev_i = new_i

print out

撰写回答