【问题标题】:Specific shuffling list in PythonPython中的特定洗牌列表
【发布时间】:2012-02-01 08:43:46
【问题描述】:

所以,我有一个组列表

[['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 在同一个组中。

我不在乎距离是否只是一个元素或多个元素,但任何元素必须不能靠近其组中的另一个元素。有什么算法吗?

算法还需要判断这是否无法完成。

【问题讨论】:

  • 当一个组的成员数量超过所有其他组的总和时会发生什么?
  • 如果不能完成,你期望会发生什么,例如:[[a],[b,c,d,e,f]]
  • 这是秘密圣诞老人洗牌名单。团体是对互相赠送礼物不感兴趣的人。
  • @moguzalp 不,这是 vkontakte 的一个小应用程序的一部分。

标签: python algorithm list


【解决方案1】:

我将给出“愚蠢”的直截了当的答案:只需将列表展平,然后反复随机打乱它,直到获得可接受的列表。

此方法具有在所有合法列表中均匀分布的特性,并且易于证明正确且易于编码。唯一的缺点是它可能会变得很慢 - 但考虑到另一条评论中提到的用例,我相信它会足够快。

至于“是否可能”——乍一看,我认为当且仅当没有组的元素超过一半时才可能,四舍五入。如果您认为第一个和最后一个元素相邻,请将“向上舍入”更改为“向下舍入”。

【讨论】:

  • 我已经考虑过了,但这是网络应用程序,对于许多用户来说,服务器多次尝试会有点痛苦。而且,考虑到特别不幸的组组合,这可能会大大降低应用程序的速度。
  • @Shark:你说得对,它(可能)会更慢——甚至可能很多更慢——但这并不意味着它实际上会很慢。我不能保证这种方法会足够快,但我建议您不要陷入在您真正意识到之前花费大量精力和牺牲来尝试加快速度的陷阱甚至需要加速。 (我说“牺牲”是因为所有其他发布的解决方案似乎都是非均匀分布的,我假设实际上需要均匀分布)
【解决方案2】:

此代码会复制您的原始组列表,并每次从(在每个时刻)最大的剩余组中获取一个随机元素。不是很随机,但应该适用于没有超过一半元素的组的任何情况。

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

【讨论】:

  • 最可能的组分布类似于[[a, b], [c, d], [e, f], [g], [h], [j]] I. e.几双和几单,所以我认为new_i = random.choice(max(((i,a) for i,a in enumerate(l) if a != prev_i), key=lambda x: len(x[1]))) 会这样做。
  • @Shark - 不,你误解了那句话。 max() 返回元组 i,a,其中 i 是组的 id (0 ... len-1),a 是组。所以我用[0] 获取ID。你不能从那里random.choice。只需使用整个代码,它也适用于您的[[a, b], [c, d], [e, f], [g], [h], [j]]
  • 现在我明白了。我只需要在生成之前洗牌list_of_groups,这样算法每次都会选择相同长度的随机组。
  • 另外,似乎有一个错误输入:不是a != prev_i,而是i != prev_i
  • @Shark - 错字已修复,list_of_groups 已改组,非常好!但是,这对您的 a…j 示例没有多大作用,因为除了单打之外,配对仍然会放在一起。
【解决方案3】:

让我采取与当前答案不同的方法

基本原则是shuffle列表中的每个列表,sort它基于长度,zip_longest基于从列表传递的可变参数,最后chain列表然后打开它.这里需要特别注意的是我们如何将列表作为变量 args 传递给迭代器,这让这里的生活更轻松:-)

假设您的列表是

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

【讨论】:

  • 这就像@eumiro 的回答,但更优雅:)
  • 我喜欢你的风格,但是当代码行之间的 cmets 是实际的 cmets 时,复制和粘贴会更容易。
【解决方案4】:

只是一个快速、不太优雅的代码:

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”之类的词作为变量名。感谢您的评论 - 没有更正

【讨论】:

  • 不要使用list作为变量名。
猜你喜欢
  • 2018-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-02
  • 1970-01-01
  • 1970-01-01
  • 2021-03-20
  • 1970-01-01
相关资源
最近更新 更多