【问题标题】:Shuffle specific elements of a list in Python but not every element [closed]在Python中随机播放列表的特定元素,但不是每个元素[关闭]
【发布时间】:2014-05-10 20:12:04
【问题描述】:

假设我有以下列表

F = ["A","B","C","D","E"]

现在我想打乱这个列表中的元素,但只是从 B 到 E 的元素。A 应该始终留在开头。其余的应该改组。

稍后,我想将一些列表放在一起,例如:

FA = [["A1","B1","B2","B3"]["A2","B2","B3","B4"]...]

如果这样做了,我将选择其中一个列表,并且元素(“A”除外)应该被打乱。

想不出解决办法... :(

【问题讨论】:

  • 请把第二种情况解释清楚

标签: python list random shuffle


【解决方案1】:

使用random.shuffle() source code

from random import randrange

def random_shuffle(x, fixed_indexes):
    for i in reversed(range(1, len(x))): # from random.shuffle() source code
        if i not in fixed_indexes:
           # pick an element in x[:i+1] with which to exchange x[i]
           while True:
               j = randrange(i+1)
               if j not in fixed_indexes:
                  break
           # swap
           x[i], x[j] = x[j], x[i]

例子:

>>> F = ["A", "B", "C", "D", "E"]
>>> random_shuffle(F, set([0]))
>>> F
['A', 'E', 'C', 'B', 'D']

【讨论】:

    【解决方案2】:

    我们基本上是打乱索引而不是打乱值:

    import random
    def shuffle_except(lst, keep):
        # keep should be a list of the indexes that we want to keep fixed
        # can also be a range or a sum of range
        # keep = range(0, 2) + range(5, 8)
        keep = set(keep)
    
        start_indexes = [i for i in xrange(len(lst)) if i not in keep]
        end_indexes = [i for i in xrange(len(lst)) if i not in keep]
        # we shuffle end indexes
        random.shuffle(end_indexes)
        # for each element in start_indexes, we move the corresponding element of lst
        # to its counterpart in end_indexes
        for i in xrange(len(start_indexes)):
            start = start_indexes[i]
            end = end_indexes[i]
            lst[start], lst[end] = lst[end], lst[start]
    
    
    a = range(20)
    print a
    print a[1], a[5], a[10], a[15]
    shuffle_except(a, {1, 5, 10, 15})
    print a
    print a[1], a[5], a[10], a[15]
    

    打印:

    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
    1 5 10 15
    [7, 1, 3, 2, 9, 5, 13, 0, 16, 8, 10, 11, 18, 17, 12, 15, 4, 6, 14, 19]
    1 5 10 15
    

    如您所见,除了我们指定的元素之外,所有元素现在都被打乱了。

    现在,如果您还想对多个 shuffle 进行分组,这里有一个示例:

    multi_list = [range(10) for _ in xrange(5)]
    keep = {0, 3, 5}
    
    for lst in multi_list:
        shuffle_except(lst, keep)
    
    print multi_list
    

    结果如下:

    [
    [0, 6, 2, 3, 4, 5, 1, 7, 8, 9], 
    [0, 4, 2, 3, 1, 5, 7, 9, 6, 8], 
    [0, 1, 4, 3, 2, 5, 8, 6, 7, 9], 
    [0, 2, 7, 3, 1, 5, 9, 4, 6, 8], 
    [0, 6, 4, 3, 1, 5, 2, 9, 7, 8]
    ]
    

    再次,您可以看到第 0、3 和 5 列始终具有与原始列表中相同的精确值

    【讨论】:

    • sorted(before) == sorted(after) 的保证是什么?交换 lst[start], lst[end] = lst[end], lst[start] 看起来很可疑。
    • 我们在列表中交换元素,我们没有覆盖它们,100% 保证结束列表包含完全相同的元素。您可以尝试各种组合
    • 想象一下keep=set()(空集)。你明白为什么random.shuffle() 使用for i in reversed(range(len(x))) 并且只用来自x[:i+1] 的元素交换i-th 元素吗?
    • 其实我不确定,我猜他们想确保元素 x[i] 永远不会留在它的位置,对吗?但是即使keep=set(),你也可以测试sorted(before)总是等于sorted(after)
    • 如果您对理论感兴趣,请参阅Fisher–Yates shuffle。顺便说一句,我已经检查了for r in range(10): for L in permutations(range(r)): sorted(before) == sorted(after) 中所有 L 的代码持有
    猜你喜欢
    • 2012-03-22
    • 2011-10-27
    • 2011-04-26
    • 1970-01-01
    • 1970-01-01
    • 2015-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多