Python中特定的洗牌列表
我有一个小组的列表
[['a', 'b'], ['c', 'd', 'e'], ['f']]
我需要把这个列表变成一个扁平化的版本,并且打乱顺序
[a, b, c, d, e, f]
这样同一组的元素就不会靠得太近。比如说,
[a, c, b, d, f, e]
这样的顺序是可以的,而 [a, c, b, d, e, f]
就不行,因为 d
和 e
是同一组的。
我不在乎它们之间的距离是一个元素还是更多,但任何元素 必须 和同组的其他元素保持一定距离。有没有什么算法可以做到这一点?
这个算法还需要能判断如果无法实现的话。
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