Перемешивание списка объектов

771

У меня есть список объектов, и я хочу перетасовать их. Я думал, что мог бы использовать random.shuffleметод, но это, кажется, не удается, когда список объектов. Есть ли способ перетасовки объектов или другой способ обойти это?

import random

class A:
    foo = "bar"

a1 = a()
a2 = a()
b = [a1, a2]

print(random.shuffle(b))

Это не удастся.

utdiscant
источник
6
Можете ли вы привести пример, как это не удается? random.shuffle должен работать инвариантно к типу объектов в списке.
Байер
3
>>> a1 = a () >>> a2 = a () >>> b = [a1, a2] >>> b [<__ main __. экземпляр в 0xb7df9e6c>, <__ main __. экземпляр в 0xb7df9e2c>]> >> print random.shuffle (b) Нет
недисциплинировано
135
Как указано ниже, random.shuffle не возвращает новый перетасованный список; это перемешивает список на месте. Так что вам не следует говорить «print random.shuffle (b)», а вместо этого делать shuffle на одной строке и печатать b на следующей.
Эли Кортрайт
Если вы пытаетесь перетасовать пустые массивы, см. Мой ответ ниже.
Гордон Бин
1
есть ли опция, которая не изменяет исходный массив, но возвращает новый перемешанный массив?
Чарли Паркер

Ответы:

1241

random.shuffleдолжно сработать. Вот пример, где объекты являются списками:

from random import shuffle
x = [[i] for i in range(10)]
shuffle(x)

# print(x)  gives  [[9], [2], [7], [0], [4], [5], [3], [1], [8], [6]]
# of course your results will vary

Обратите внимание, что shuffle работает на месте и возвращает None.

tom10
источник
1
@seokhoonlee Ни то, ни другое. Это генератор псевдослучайных чисел, который, по возможности, заполняется источником реальной случайности из ОС. Для всех целей, кроме криптографии, это случайное «достаточно». Это подробно изложено в документации randomмодуля .
dimo414
2
использовать клон для нового списка
Мохаммад Махди КучакЯзди
8
есть ли опция, которая не изменяет исходный массив, но возвращает новый перемешанный массив?
Чарли Паркер
5
@CharlieParker: Не то, что я знаю. Вы можете использовать random.sample(x, len(x))или просто сделать копию и shuffleтому подобное . Для list.sortкоторого есть аналогичная проблема, есть сейчас list.sorted, но нет аналогичного варианта для shuffle.
Tom10
6
@seokhonlee для крипто-безопасной случайности, используйте from random import SystemRandomвместо этого; добавьте cryptorand = SystemRandom()и измените строку 3 наcryptorand.shuffle(x)
просмотрите
115

Как вы узнали, проблема была в перестановке на месте. У меня также часто бывают проблемы, и я часто забываю, как копировать список. Использование sample(a, len(a))является решением, используя len(a)в качестве размера выборки. См. Https://docs.python.org/3.6/library/random.html#random.sample для документации по Python.

Вот простая версия, использующая random.sample()возвращенный случайный результат в виде нового списка.

import random

a = range(5)
b = random.sample(a, len(a))
print a, b, "two list same:", a == b
# print: [0, 1, 2, 3, 4] [2, 1, 3, 4, 0] two list same: False

# The function sample allows no duplicates.
# Result can be smaller but not larger than the input.
a = range(555)
b = random.sample(a, len(a))
print "no duplicates:", a == list(set(b))

try:
    random.sample(a, len(a) + 1)
except ValueError as e:
    print "Nope!", e

# print: no duplicates: True
# print: Nope! sample larger than population
Тед
источник
есть ли опция, которая не изменяет исходный массив, но возвращает новый перемешанный массив?
Чарли Паркер
просто скопируйте список @CharlieParker: old = [1,2,3,4,5]; new = list(old); random.shuffle(new); print(old); print(new)(замените; на
новые
old[:]также может сделать мелкую копию для списка old.
Сяо
sample()особенно полезно для прототипирования анализа данных. sample(data, 2)для настройки кода клея конвейера, а затем пошагово «расширяя» его до len(data).
Катрин Лейнвебер
58

Мне потребовалось некоторое время, чтобы получить это тоже. Но документация по shuffle очень понятна:

Перемешать список х на месте ; вернуть нет.

Так что не стоит print(random.shuffle(b)). Вместо этого делай random.shuffle(b)и потом print(b).

Охад Коэн
источник
44
#!/usr/bin/python3

import random

s=list(range(5))
random.shuffle(s) # << shuffle before print or assignment
print(s)

# print: [2, 4, 1, 3, 0]
Майкл
источник
31

Если вы уже используете numpy (очень популярный для научных и финансовых приложений), вы можете сэкономить импорт.

import numpy as np    
np.random.shuffle(b)
print(b)

http://docs.scipy.org/doc/numpy/reference/generated/numpy.random.shuffle.html

fantabolous
источник
Что мне нравится в этом ответе, так это то, что я могу контролировать случайные семена в numpy. Могу поспорить, что есть способ сделать это в случайном модуле, но это не очевидно для меня прямо сейчас ... что означает, что мне нужно больше читать.
ВанБантам
25
>>> import random
>>> a = ['hi','world','cat','dog']
>>> random.shuffle(a,random.random)
>>> a
['hi', 'cat', 'dog', 'world']

Он отлично работает для меня. Обязательно установите случайный метод.

Дэн Лоренц
источник
Все еще не работает для меня, см. Мой пример кода в отредактированном вопросе.
utdiscant
4
Второй параметр по умолчанию равен random.random. Совершенно безопасно оставить это.
cbare
3
@alvas random.shuffle (a) не возвращает ничего, т. е. возвращает None. Таким образом, вы должны проверить не возвращаемое значение.
sonus21
15

Если у вас есть несколько списков, вы можете сначала определить перестановку (способ перестановки списка / перестановки элементов в списке), а затем применить его ко всем спискам:

import random

perm = list(range(len(list_one)))
random.shuffle(perm)
list_one = [list_one[index] for index in perm]
list_two = [list_two[index] for index in perm]

Numpy / Scipy

Если ваши списки являются массивами, это проще:

import numpy as np

perm = np.random.permutation(len(list_one))
list_one = list_one[perm]
list_two = list_two[perm]

MPU

Я создал небольшой пакет утилит, mpuкоторый имеет consistent_shuffleфункцию:

import mpu

# Necessary if you want consistent results
import random
random.seed(8)

# Define example lists
list_one = [1,2,3]
list_two = ['a', 'b', 'c']

# Call the function
list_one, list_two = mpu.consistent_shuffle(list_one, list_two)

Обратите внимание, что mpu.consistent_shuffleпринимает произвольное количество аргументов. Таким образом, вы также можете перемешать три или более списков с ним.

Мартин Тома
источник
10
from random import random
my_list = range(10)
shuffled_list = sorted(my_list, key=lambda x: random())

Эта альтернатива может быть полезна для некоторых приложений, где вы хотите поменять местами функцию упорядочения.

Джефф
источник
Кроме того, обратите внимание, что благодаря sortedэтому, это функциональная случайность (если вам это нравится).
Инамати
1
Это не случайное распределение значений из-за стабильности Timsort. (Значения с одним и тем же ключом остаются в их первоначальном порядке.) РЕДАКТИРОВАТЬ: я полагаю, это не имеет значения, поскольку риск столкновения с 64-разрядными числами с плавающей запятой весьма минимален.
Матин Улхак
10

В некоторых случаях при использовании массивов NumPy, используя random.shuffle дублированные данные в массиве.

Альтернативой является использование numpy.random.shuffle. Если вы уже работаете с numpy, это предпочтительный метод по сравнению с универсальным random.shuffle.

numpy.random.shuffle

пример

>>> import numpy as np
>>> import random

Использование random.shuffle:

>>> foo = np.array([[1,2,3],[4,5,6],[7,8,9]])
>>> foo

array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])


>>> random.shuffle(foo)
>>> foo

array([[1, 2, 3],
       [1, 2, 3],
       [4, 5, 6]])

Использование numpy.random.shuffle:

>>> foo = np.array([[1,2,3],[4,5,6],[7,8,9]])
>>> foo

array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])


>>> np.random.shuffle(foo)
>>> foo

array([[1, 2, 3],
       [7, 8, 9],
       [4, 5, 6]])
Гордон Бин
источник
1
Также numpy.random.permutationмогут представлять интерес: stackoverflow.com/questions/15474159/shuffle-vs-permute-numpy
Гордон Бин
У вас есть пример создания дублированных данных в массиве при использовании random.shuffle?
nurettin
Да, это включено в мой ответ. Смотрите раздел под названием «Пример». ;)
Гордон Бин
Неважно, я видел три элемента и думал, что это то же самое. Хорошая находка
nurettin
1
random.shuffleдокументация должна кричать Не используйте Numpy массивы
winterlight
10

Для однострочников используйте random.sample(list_to_be_shuffled, length_of_the_list)пример:

import random
random.sample(list(range(10)), 10)

выходы: [2, 9, 7, 8, 3, 0, 4, 1, 6, 5]

weiyixie
источник
6

«print func (foo)» напечатает возвращаемое значение «func» при вызове с «foo». 'shuffle', однако, имеет None в качестве возвращаемого типа, поскольку список будет изменен на месте, следовательно, он ничего не печатает. Временное решение:

# shuffle the list in place 
random.shuffle(b)

# print it
print(b)

Если вы больше знакомы с функциональным стилем программирования, вы можете сделать следующую функцию-обертку:

def myshuffle(ls):
    random.shuffle(ls)
    return ls
JonDoe
источник
2
Так как это передает ссылку на список, оригинал изменяется. Возможно, вы захотите скопировать список перед перетасовкой с помощью Deepcopy
shivram.ss
@ shivram.ss В этом случае вам нужно что-то вроде, random.sample(ls, len(ls))если вы действительно хотите пойти по этому пути.
Арда Си
4

Можно определить функцию под названием shuffled(в том же смысле sortпротив sorted)

def shuffled(x):
    import random
    y = x[:]
    random.shuffle(y)
    return y

x = shuffled([1, 2, 3, 4])
print x
malbarbo
источник
3
import random

class a:
    foo = "bar"

a1 = a()
a2 = a()
a3 = a()
a4 = a()
b = [a1,a2,a3,a4]

random.shuffle(b)
print(b)

shuffle на месте, так что не печатать результат None, а список.

Рави Танвар
источник
1

Вы можете пойти на это:

>>> A = ['r','a','n','d','o','m']
>>> B = [1,2,3,4,5,6]
>>> import random
>>> random.sample(A+B, len(A+B))
[3, 'r', 4, 'n', 6, 5, 'm', 2, 1, 'a', 'o', 'd']

Если вы хотите вернуться к двум спискам, вы можете разделить этот длинный список на два.

kiriloff
источник
1

Вы могли бы построить функцию, которая принимает список в качестве параметра и возвращает перемешанную версию списка:

from random import *

def listshuffler(inputlist):
    for i in range(len(inputlist)):
        swap = randint(0,len(inputlist)-1)
        temp = inputlist[swap]
        inputlist[swap] = inputlist[i]
        inputlist[i] = temp
    return inputlist
user8327014
источник
1
""" to shuffle random, set random= True """

def shuffle(x,random=False):
     shuffled = []
     ma = x
     if random == True:
         rando = [ma[i] for i in np.random.randint(0,len(ma),len(ma))]
         return rando
     if random == False:
          for i in range(len(ma)):
          ave = len(ma)//3
          if i < ave:
             shuffled.append(ma[i+ave])
          else:
             shuffled.append(ma[i-ave])    
     return shuffled
Джош Аниш
источник
небольшое введение или объяснение было бы полезно?
Kacase
функция полезна для перетасовки действий, представьте, что вам нужно перетасовать список чисел три раза, и в трех случаях вам требуется случайное перемешивание, а затем просто поверните случайный аргумент в значение True, если вам не требуется случайность и вы хотите сохраняется тот же порядок перетасовки, затем не вносите никаких изменений, просто запустите код.
Джош Аниш
Поскольку не существует варианта использования, когда вызывающая сторона этой функции во время выполнения решает, хочет ли он случайное или неслучайное перемешивание, эту функцию следует разделить на две части.
инструментальный инструмент
Нет описания того, что должен делать неслучайный случайный случай. (По касательной это не ответ, поэтому вопрос, поэтому он не служит цели переполнения стека.)
toolforger
1

Вы можете использовать случайный порядок или образец. оба из которых приходят из случайного модуля.

import random
def shuffle(arr1):
    n=len(arr1)
    b=random.sample(arr1,n)
    return b

ИЛИ

import random
def shuffle(arr1):
    random.shuffle(arr1)
    return arr1
Рави Танвар
источник
0

Убедитесь, что вы не называете свой исходный файл random.py, и что в вашем рабочем каталоге нет файла с именем random.pyc .. либо это может привести к тому, что ваша программа попытается импортировать ваш локальный файл random.py вместо случайного модуля pythons ,

user3298224
источник
0
def shuffle(_list):
    if not _list == []:
        import random
        list2 = []
        while _list != []:
            card = random.choice(_list)
            _list.remove(card)
            list2.append(card)
        while list2 != []:
            card1 = list2[0]
            list2.remove(card1)
            _list.append(card1)
        return _list
Pogramist
источник
Эта функция может помочь вам, если вы не хотите использовать случайный модуль
Pogramist
Это решение не только многословно, но и неэффективно (время выполнения пропорционально квадрату размера списка).
инструментальный инструмент
Второй цикл может быть заменен на _list.extend(list2), который является более кратким и более эффективным.
инструментальный инструмент
Функция Python, которая изменяет параметр, никогда не должна возвращать результат. Это просто соглашение, но полезное: людям часто не хватает времени, чтобы посмотреть на реализацию всех функций, которые они вызывают, поэтому любой, кто просто видит имя вашей функции и имеет результат, будет очень удивлен, увидев функцию обновить его параметр.
инструментальный инструмент
0
import random
class a:
    foo = "bar"

a1 = a()
a2 = a()
b = [a1.foo,a2.foo]
random.shuffle(b)
Ксавье
источник
-1

Процесс перетасовки "с заменой" , поэтому появление каждого элемента может измениться! По крайней мере, когда, когда элементы в вашем списке также список.

Например,

ml = [[0], [1]] * 10

После,

random.shuffle(ml)

Число [0] может быть 9 или 8, но не точно 10.

Гэтсби
источник
-1

План: Запишите случайное перемешивание, не полагаясь на библиотеку для выполнения тяжелой работы. Пример: просмотреть список с начала, начиная с элемента 0; найдите для него новую случайную позицию, скажем 6, поместите значение 0 в 6 и значение 6 в 0. Перейдите к элементу 1 и повторите этот процесс, и так далее до конца списка.

import random
iteration = random.randint(2, 100)
temp_var = 0
while iteration > 0:

    for i in range(1, len(my_list)): # have to use range with len()
        for j in range(1, len(my_list) - i):
            # Using temp_var as my place holder so I don't lose values
            temp_var = my_list[i]
            my_list[i] = my_list[j]
            my_list[j] = temp_var

        iteration -= 1
Enber
источник
Вы можете поменять переменные в python следующим образом:my_list[i], my_list[j] = my_list[j], my_list[i]
Karolis Ryselis
-2

Работает нормально. Я пытаюсь это здесь с функциями в качестве объектов списка:

    from random import shuffle

    def foo1():
        print "foo1",

    def foo2():
        print "foo2",

    def foo3():
        print "foo3",

    A=[foo1,foo2,foo3]

    for x in A:
        x()

    print "\r"

    shuffle(A)
    for y in A:
        y()

Распечатывается: foo1 foo2 foo3 foo2 foo3 foo1 (foos в последнем ряду имеют случайный порядок)

Стефан Грюнвальд
источник