【问题标题】:Python - shuffle only some elements of a listPython - 仅随机播放列表中的某些元素
【发布时间】:2012-03-22 08:47:16
【问题描述】:

我正在尝试仅将列表的第 3 个元素打乱到最后一个位置,因此第一个两个将始终保持原位 例如

list = ['a?','b','c','d','e']

进入

list = ['a?','b','d','e','c']

由于某种原因,这不起作用:

list = ['a?','b','c','d','e']
import random
random.shuffle(list[2:])    
print list

有谁知道我做错了什么??

到目前为止,唯一对我有用的是这个(已编辑):

lists = [['a?','b','c','d','e'],['1?','2','3','4','5','6','7']]
import random

for list in lists:
    copy = list[2:]
    random.shuffle(copy)
    list[2:] = copy

print lists

认为这正是我所需要的。

【问题讨论】:

  • list[2:] 创建了一个 new 列表,该列表没有在其他任何地方引用,因此结果丢失了。
  • @FelixKling:将其发布为答案,并附上一个小代码示例,应该如何修复它[如何分配]
  • lst[2:] = random.sample(lst[2:], len(lst[2:])) 或查看下面的功能版本。
  • 对于真正的就地改组,请参阅here

标签: python list random elements shuffle


【解决方案1】:

你要做的是:

copy = list[2:]
random.shuffle(copy)    

这对原始列表没有太大作用。试试这个:

copy = list[2:]
random.shuffle(copy)
list[2:] = copy # overwrite the original

【讨论】:

  • 缺点是这会带来两个副本,这对于大型列表来说可能会很昂贵。
  • 是的,这有点类似于我刚刚发布的内容.. 对于包含多个列表的列表,我们需要考虑这个问题.. 像 [[],[],[],[],[ ]]
【解决方案2】:

如果你想在不复制的情况下洗牌,你可以尝试编写自己的可变切片类,如下所示(这是一个粗略的实现草图,没有边界检查等):

class MutableSlice(object):
    def __init__(self, baselist, begin, end=None):
        self._base = baselist
        self._begin = begin
        self._end = len(baselist) if end is None else end

    def __len__(self):
        return self._end - self._begin

    def __getitem__(self, i):
        return self._base[self._begin + i]

    def __setitem__(self, i, val):
        self._base[i + self._begin] = val

然后将原始列表包装到其中并馈送到标准随机播放:

>>> mylist = [1,2,3,4,5,6]
>>> slice = MutableSlice(mylist, 2)
>>> import random
>>> random.shuffle(slice)
>>> mylist
[1, 2, 4, 3, 5, 6]

【讨论】:

    【解决方案3】:

    您可以创建自己的 shuffle 函数,允许您在可变序列中随机播放切片。它处理对切片副本进行采样并重新分配回切片。您必须传递 slice() 参数,而不是更熟悉的 [2:] 表示法。

    from random import sample
    def myShuffle(x, *s):
        x[slice(*s)] = sample(x[slice(*s)], len(x[slice(*s)]))
    

    用法:

    >>> lst = ['a?','b','c','d','e']   #don't use list as a name
    >>> myShuffle(lst, 2)              #shuffles lst[:2]
    >>> lst
    ['b', 'a?', 'c', 'd', 'e']
    >>> myShuffle(lst, 2, None)        #shuffles lst[2:]
    >>> lst
    ['b', 'a?', 'd', 'e', 'c']
    

    【讨论】:

      【解决方案4】:

      要在原地打乱列表的一部分,而不需要副本,我们可以使用Knuth shuffle

      import random
      def shuffle_slice(a, start, stop):
          i = start
          while (i < stop-1):
              idx = random.randrange(i, stop)
              a[i], a[idx] = a[idx], a[i]
              i += 1
      

      它和 random.shuffle 做同样的事情,除了在切片上:

      >>> a = [0, 1, 2, 3, 4, 5]
      >>> shuffle_slice(a, 0, 3)
      >>> a
      [2, 0, 1, 3, 4, 5]
      

      【讨论】:

        【解决方案5】:

        l[2:] 构造一个新列表,random.shuffle 尝试“就地”更改列表,这对l 本身没有影响。

        您可以为此使用random.sample

        l[2:] = random.sample(l[2:], len(l)-2)
        

        【讨论】:

        • sample 的好技巧,但在这种情况下,子列表也首先被复制。
        • myShuffle = lambda x: sample(x, len(x)), l[2:] = myShuffle(l[2:])
        【解决方案6】:

        利用列表快速删除的事实 并插入和扩展以前的解决方案 (https://stackoverflow.com/a/25229111/3449962):

        列表项

        • 枚举固定元素并复制它们及其索引
        • 从列表中删除固定元素
        • 洗牌剩余的子集
        • 将固定元素放回

        这将使用内存开销取决于的就地操作 列表中固定元素的数量。时间线性。一个可能的 更一般的 shuffle_subset 实现:

        #!/usr/bin/env python
        """Shuffle elements in a list, except for a sub-set of the elments.
        
        The sub-set are those elements that should retain their position in
        the list.  Some example usage:
        
        >>> from collections import namedtuple
        >>> class CAnswer(namedtuple("CAnswer","x fixed")):
        ...             def __bool__(self):
        ...                     return self.fixed is True
        ...             __nonzero__ = __bool__  # For Python 2. Called by bool in Py2.
        ...             def __repr__(self):
        ...                     return "<CA: {}>".format(self.x)
        ...
        >>> val = [3, 2, 0, 1, 5, 9, 4]
        >>> fix = [2, 5]
        >>> lst = [CAnswer(v, i in fix) for i, v in enumerate(val)]
        
        >>> print("Start   ", 0, ": ", lst)
        Start    0 :  [<CA: 3>, <CA: 2>, <CA: 0>, <CA: 1>, <CA: 5>, <CA: 9>, <CA: 4>]
        
        Using a predicate to filter.
        
        >>> for i in range(4):  # doctest: +NORMALIZE_WHITESPACE
        ...     shuffle_subset(lst, lambda x : x.fixed)
        ...     print([lst[i] for i in fix], end=" ")
        ...
        [<CA: 0>, <CA: 9>] [<CA: 0>, <CA: 9>] [<CA: 0>, <CA: 9>] [<CA: 0>, <CA: 9>]
        
        >>> for i in range(4):                # doctest: +NORMALIZE_WHITESPACE
        ...     shuffle_subset(lst)           # predicate = bool()
        ...     print([lst[i] for i in fix], end=" ")
        ...
        [<CA: 0>, <CA: 9>] [<CA: 0>, <CA: 9>] [<CA: 0>, <CA: 9>] [<CA: 0>, <CA: 9>]
        
        Exclude certain postions from the shuffle.  For example, exclude the
        first two elements:
        
        >>> fix = [0, 1]
        >>> lst = [CAnswer(v, i in fix) for i, v in enumerate(val)]
        >>> print("Start   ", 0, ": ", lst)
        Start    0 :  [<CA: 3>, <CA: 2>, <CA: 0>, <CA: 1>, <CA: 5>, <CA: 9>, <CA: 4>]
        >>> for i in range(4):                # doctest: +NORMALIZE_WHITESPACE
        ...     shuffle_subset(lst, fix)
        ...     print([lst[i] for i in fix], end=" ")
        ...
        [<CA: 3>, <CA: 2>] [<CA: 3>, <CA: 2>] [<CA: 3>, <CA: 2>] [<CA: 3>, <CA: 2>]
        
        Using a selector with the same number of elements as lst:
        
        >>> fix = [0, 1]
        >>> lst = [CAnswer(v, i in fix) for i, v in enumerate(val)]
        >>> sel = [(i in fix) for i, _ in enumerate(val)]
        >>> print("Start   ", 0, ": ", lst)
        Start    0 :  [<CA: 3>, <CA: 2>, <CA: 0>, <CA: 1>, <CA: 5>, <CA: 9>, <CA: 4>]
        >>> for i in range(4):                # doctest: +NORMALIZE_WHITESPACE
        ...     shuffle_subset(lst, sel)
        ...     print([lst[i] for i in fix], end=" ")
        ...
        [<CA: 3>, <CA: 2>] [<CA: 3>, <CA: 2>] [<CA: 3>, <CA: 2>] [<CA: 3>, <CA: 2>]
        
        A generator as selector works fine too:
        
        >>> fix = [0, 1]
        >>> lst = [CAnswer(v, i in fix) for i, v in enumerate(val)]
        >>> print("Start   ", 0, ": ", lst)
        Start    0 :  [<CA: 3>, <CA: 2>, <CA: 0>, <CA: 1>, <CA: 5>, <CA: 9>, <CA: 4>]
        >>> for i in range(4):                # doctest: +NORMALIZE_WHITESPACE
        ...     sel = ((i in fix) for i, _ in enumerate(val))
        ...     shuffle_subset(lst, sel)
        ...     print([lst[i] for i in fix], end=" ")
        ...
        [<CA: 3>, <CA: 2>] [<CA: 3>, <CA: 2>] [<CA: 3>, <CA: 2>] [<CA: 3>, <CA: 2>]
        
        """
        from __future__ import print_function
        import random
        
        
        def shuffle_subset(lst, predicate=None):
            """All elements in lst, except a sub-set, are shuffled.
        
            The predicate defines the sub-set of elements in lst that should
            not be shuffled:
        
              + The predicate is a callable that returns True for fixed
              elements, predicate(element) --> True or False.
        
              + If the predicate is None extract those elements where
              bool(element) == True.
        
              + The predicate is an iterable that is True for fixed elements
              or len(predicate) == len(lst).
        
              + The predicate is a list of indices of fixed elements in lst
              with len(predicate) < len(lst).
        
            """
            def extract_fixed_elements(pred, lst):
                try:
                    if callable(pred) or pred is None:
                        pred = bool if pred is None else pred
                        fixed_subset = [(i, e) for i, e in enumerate(lst) if pred(e)]
                    elif (hasattr(pred, '__next__') or len(pred) == len(lst)):
                        fixed_subset = [(i, lst[i]) for i, p in enumerate(pred) if p]
                    elif len(pred) < len(lst):
                        fixed_subset = [(i, lst[i]) for i in pred]
                    else:
                        raise TypeError("Predicate {} not supported.".format(pred))
                except TypeError as err:
                    raise TypeError("Predicate {} not supported. {}".format(pred, err))
                return fixed_subset
            #
            fixed_subset = extract_fixed_elements(predicate, lst)
            fixed_subset.reverse()      # Delete fixed elements from high index to low.
            for i, _ in fixed_subset:
                del lst[i]
            random.shuffle(lst)
            fixed_subset.reverse()      # Insert fixed elements from low index to high.
            for i, e in fixed_subset:
                lst.insert(i, e)
        
        
        if __name__ == "__main__":
            import doctest
            doctest.testmod()
        

        【讨论】:

          【解决方案7】:

          我从 random.shuffle 复制了 shuffle 函数并对其进行了调整,以便它仅在定义的范围内对列表进行随机播放:

          import random
          a = range(0,20)
          b = range(0,20)
          
          def shuffle_slice(x, startIdx, endIdx):
              for i in reversed(xrange(startIdx+1, endIdx)):
                 # pick an element in x[:i+1] with which to exchange x[i]
                 j = random.randint(startIdx, i)
                 x[i], x[j] = x[j], x[i]
          
          #Shuffle from 5 until the end of a
          shuffle_slice(a, 5, len(a))    
          print a
          
          #Shuffle b from 5 ... 15
          shuffle_slice(b, 5, 15)
          print b
          

          上面的代码只打乱指定范围内的元素。随机播放是在原地完成的,即没有创建列表的副本。

          【讨论】:

            【解决方案8】:

            试试这个..它更简单,并且不会复制列表。
            您可以通过使用列表索引来固定任何元素。

            工作:

            1. 创建一个仅包含您想要随机播放的元素的新列表。

            2. 随机播放新列表。

            3. 从原始列表中删除您想要随机播放的元素。

            4. 将新创建的列表插入到旧列表的正确索引处

            随机导入 列表 = ['a?', 'b', 'c', 'd', 'e'] v = [] p = [v.append(list[c]) for c in range(2,len(list))] #step 1 random.shuffle(v) #步骤 2 对于范围内的c(2,len(list)): list.remove(list[c]) #第 3 步 list.insert(c,v[c-2]) #step 4 #c-2 因为要洗牌的部分从这个列表索引开始 打印(列表)

            【讨论】:

              猜你喜欢
              • 2011-08-19
              • 2021-02-19
              • 2011-10-27
              • 2011-04-26
              • 2023-01-23
              • 1970-01-01
              • 2014-05-10
              • 1970-01-01
              • 2014-04-02
              相关资源
              最近更新 更多