【问题标题】:Fix first element, shuffle the rest of a list/array修复第一个元素,打乱列表/数组的其余部分
【发布时间】:2012-07-29 02:56:15
【问题描述】:

是否可以只打乱给定列表(或 numpy 中的数组)的(连续)部分?

如果这通常是不可能的,那么第一个元素是固定的而列表/数组的其余部分需要打乱的特殊情况呢?例如,我有一个列表/数组:

to_be_shuffled = [None, 'a', 'b', 'c', 'd', ...]

第一个元素应该始终停留在哪里,而其余元素将被反复洗牌。

一种可能的方法是先打乱整个列表,然后检查第一个元素,如果它不是特殊的固定元素(例如None),然后将其位置与特殊元素的位置交换(然后需要查找)。

有没有更好的方法来做到这一点?

【问题讨论】:

    标签: python shuffle


    【解决方案1】:

    为什么不直接

    import random
    rest = to_be_shuffled[1:]
    random.shuffle(rest)
    shuffled_lst = [to_be_shuffled[0]] + rest
    

    【讨论】:

    • @DavidRobinson,谢谢。以上是否就地,意味着没有创建列表/数组的一部分的新副本?
    • @kojiro shuffled_lst 在此之前不存在。你可能的意思是:to_be_shuffled[1:] = rest
    • @skyork no,shuffled_lstrest 都是 to_be_shuffled 中数据的副本(部分和完整)。
    • skyork:不,代码没有到位; to_be_shuffled 的顺序没有改变。 to_be_shuffled[1:] 复制一份。
    • @DavidRobinson,我明白了。我想对于就地解决方案,我上面的建议会完成这项工作,尽管效率不高?还有其他更好的就地解决方案吗?
    【解决方案2】:

    numpy 数组不会在切片时复制数据:

    numpy.random.shuffle(a[1:])
    

    【讨论】:

    • 当我选择使用 numpy 数组来实现时,这是我的最佳解决方案。其他解决方案也很有帮助,希望其他人发现它们适合各自的用例。
    【解决方案3】:

    我认为尝试实施比您所要求的更通用的方法会很有趣且具有教育意义。在这里,我将 indices 洗牌到原始列表(而不是列表本身),不包括锁定的索引,并使用该索引列表从原始列表中挑选元素。这不是就地解决方案,而是作为生成器实现的,因此您可以懒惰地选择元素。

    如果可以改进,请随时编辑。

    import random
    
    def partial_shuf(input_list, fixed_indices):
        """Given an input_list, yield elements from that list in random order
        except where elements indices are in fixed_indices."""
        fixed_indices = sorted(set(i for i in fixed_indices if i < len(input_list)))
        i = 0
        for fixed in fixed_indices:
            aslice = range(i, fixed)
            i = 1 + fixed
            random.shuffle(aslice)
            for j in aslice:
                yield input_list[j]
            yield input_list[fixed]
        aslice = range(i, len(input_list))
        random.shuffle(aslice)
        for j in aslice:
            yield input_list[j]
    
    print '\n'.join(' '.join((str(i), str(n))) for i, n in enumerate(partial_shuf(range(4, 36), [0, 4, 9, 17, 25, 40])))
    
    assert sorted(partial_shuf(range(4, 36), [0, 4, 9, 17, 25, 40])) == range(4, 36)
    

    【讨论】:

      【解决方案4】:

      我从标准库 random 模块(在 Lib\random.py 中找到)中获取了 shuffle 函数,并对其进行了轻微修改,使其仅对 startstop 指定的列表的一部分进行随机播放。它做到了这一点。享受吧!

      from random import randint
      
      def shuffle(x, start=0, stop=None):
          if stop is None:
              stop = len(x)
      
          for i in reversed(range(start + 1, stop)):
              # pick an element in x[start: i+1] with which to exchange x[i]
              j = randint(start, i)
              x[i], x[j] = x[j], x[i]
      

      出于您的目的,使用1 作为start 参数调用此函数应该可以解决问题。

      【讨论】:

      • @J.F.Sebastian:我做到了,先生。谢谢!
      • 在 Python 2.x 上使用 xrange 代替 range
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-03
      • 2018-01-10
      • 1970-01-01
      相关资源
      最近更新 更多