Baralhar uma lista de objetos

771

Eu tenho uma lista de objetos e quero embaralhá-los. Eu pensei que poderia usar o random.shufflemétodo, mas isso parece falhar quando a lista é de objetos. Existe um método para embaralhar objetos ou outra maneira de contornar isso?

import random

class A:
    foo = "bar"

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

print(random.shuffle(b))

Isso irá falhar.

utdiscant
fonte
6
Você pode dar um exemplo de como isso falha? random.shuffle deve funcionar invariável ao tipo de objetos na lista.
Bayer
3
>>> a1 = a () >>> a2 = a () >>> b = [a1, a2] >>> b [<__ principal __. uma instância em 0xb7df9e6c>, <__ main __. uma instância em 0xb7df9e2c>]> >> print random.shuffle (b) None
utdiscant
135
Conforme declarado abaixo, o random.shuffle não retorna uma nova lista aleatória; embaralha a lista no lugar. Portanto, você não deve dizer "print random.shuffle (b)" e, em vez disso, deve fazer o shuffle em uma linha e imprimir b na próxima linha.
Eli Courtwright 10/06/09
Se você estiver tentando embaralhar matrizes numpy, veja minha resposta abaixo.
Gordon Bean
1
existe uma opção que não modifique a matriz original, mas retorne uma nova matriz aleatória?
Charlie Parker

Respostas:

1241

random.shuffleDeveria trabalhar. Aqui está um exemplo, onde os objetos são listas:

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

Observe que o shuffle funciona no lugar e retorna Nenhum.

tom10
fonte
1
@seokhoonlee Nem. É um gerador de números aleatórios pseduo que, quando possível, é propagado por uma fonte de aleatoriedade real do sistema operacional. Para todos, exceto para fins de criptografia, é aleatório "suficiente". Isso é detalhado em detalhes na randomdocumentação do módulo .
Dimo414
2
use clone para uma nova lista
Mohammad Mahdi KouchakYazdi
8
existe uma opção que não modifique a matriz original, mas retorne uma nova matriz aleatória?
Charlie Parker
5
@CharlieParker: Não que eu saiba. Você pode usar random.sample(x, len(x))ou apenas fazer uma cópia e shuffleisso. Para o list.sortqual há um problema semelhante, existe agora list.sorted, mas não existe uma variante semelhante para shuffle.
tom10
6
@seokhonlee para aleatoriedade criptografada, use from random import SystemRandom; adicionar cryptorand = SystemRandom()e alterar a linha 3 paracryptorand.shuffle(x)
browly
115

Como você aprendeu, o embaralhamento no local foi o problema. Eu também tenho problemas com frequência e também pareço esquecer também de como copiar uma lista. Usar sample(a, len(a))é a solução, usando len(a)como tamanho da amostra. Consulte https://docs.python.org/3.6/library/random.html#random.sample para obter a documentação do Python.

Aqui está uma versão simples random.sample()que retorna o resultado aleatório como uma nova lista.

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
Ted
fonte
existe uma opção que não modifique a matriz original, mas retorne uma nova matriz aleatória?
Charlie Parker
basta copiar a lista @CharlieParker: old = [1,2,3,4,5]; new = list(old); random.shuffle(new); print(old); print(new)(substitua; com novas linhas)
fjsj
old[:]também poderia fazer uma cópia superficial para a lista old.
Xiao
sample()é particularmente útil para prototipar uma análise de dados. sample(data, 2)para configurar o código de cola de um pipeline e, em seguida, "ampliá-lo" passo a passo, até len(data).
Katrin Leinweber
58

Levei algum tempo para conseguir isso também. Mas a documentação do shuffle é muito clara:

lista aleatória x no lugar ; retornar Nenhum.

Então você não deveria print(random.shuffle(b)). Em vez disso, faça random.shuffle(b)e depois print(b).

Ohad Cohen
fonte
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]
Michael
fonte
31

Se você já estiver usando o numpy (muito popular para aplicativos científicos e financeiros), poderá salvar uma importação.

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

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

fantabolous
fonte
O que eu gosto nessa resposta é que eu posso controlar a semente aleatória em numpy. Aposto que existe uma maneira de fazer isso no módulo aleatório, mas isso não é óbvio para mim agora ... o que significa que preciso ler mais.
VanBantam
25
>>> import random
>>> a = ['hi','world','cat','dog']
>>> random.shuffle(a,random.random)
>>> a
['hi', 'cat', 'dog', 'world']

Isso funciona bem para mim. Certifique-se de definir o método aleatório.

Dan Lorenc
fonte
Ainda não funciona para mim, veja meu código de exemplo na pergunta editada.
utdiscant
4
O segundo parâmetro é padronizado como random.random. É perfeitamente seguro deixá-lo de fora.
cbare
3
@alvas random.shuffle (a) não retorna nada, isto é, retorna None. Então você tem que verificar um valor não retornado.
sonus21
15

Se você tiver várias listas, convém definir a permutação (a maneira como embaralha a lista / reorganiza os itens da lista) primeiro e depois aplica-a a todas as listas:

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

Se suas listas são matrizes numpy, é mais simples:

import numpy as np

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

mpu

Eu criei o pequeno pacote utilitário mpuque tem a consistent_shufflefunção:

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)

Observe que é mpu.consistent_shufflenecessário um número arbitrário de argumentos. Então você também pode embaralhar três ou mais listas com ele.

Martin Thoma
fonte
10
from random import random
my_list = range(10)
shuffled_list = sorted(my_list, key=lambda x: random())

Essa alternativa pode ser útil para alguns aplicativos em que você deseja trocar a função de pedido.

Jeff
fonte
Além disso, observe que, graças a sorted, este é um shuffle funcional (se você gosta desse tipo de coisa).
Inaimathi
1
Isso realmente não distribui aleatoriamente os valores devido à estabilidade do Timsort. (Os valores com a mesma chave são deixados em sua ordem original.) EDIT: Suponho que não importa, pois o risco de colisão com flutuadores de 64 bits é bastante mínimo.
Mateen Ulhaq 13/11/19
10

Em alguns casos, ao usar matrizes numpy, use random.shuffledados duplicados criados na matriz.

Uma alternativa é usar numpy.random.shuffle. Se você já estiver trabalhando com numpy, esse é o método preferido em relação ao genérico random.shuffle.

numpy.random.shuffle

Exemplo

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

Usando 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]])

Usando 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]])
Gordon Bean
fonte
1
Além disso, numpy.random.permutationpodem ser de interesse: stackoverflow.com/questions/15474159/shuffle-vs-permute-numpy
Gordon Feijão
Você tem um exemplo de dados duplicados sendo criados em uma matriz ao usar random.shuffle?
Nurettin
Sim - está incluído na minha resposta. Veja a seção "Exemplo". ;)
Gordon Bean
Deixa pra lá, eu vi três elementos e pensei que era o mesmo. Bom encontrar
nurettin
1
random.shuffledocumentação deve gritar Não use com arrays numpy
winterlight
10

Para one-liners, use random.sample(list_to_be_shuffled, length_of_the_list)com um exemplo:

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

saídas: [2, 9, 7, 8, 3, 0, 4, 1, 6, 5]

weiyixie
fonte
6

'print func (foo)' imprimirá o valor de retorno de 'func' quando chamado com 'foo'. 'shuffle', no entanto, tem None como seu tipo de retorno, pois a lista será modificada no local e, portanto, não imprime nada. Gambiarra:

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

# print it
print(b)

Se você gosta mais do estilo de programação funcional, convém fazer a seguinte função de wrapper:

def myshuffle(ls):
    random.shuffle(ls)
    return ls
JonDoe
fonte
2
Como isso passa uma referência à lista, o original é modificado. Você pode querer copiar a lista antes de baralhar usando deepcopy
shivram.ss
@ shivram.ss Nesse caso, você gostaria de algo como random.sample(ls, len(ls))se realmente deseja seguir esse caminho.
Arda Xi
4

Pode-se definir uma função chamada shuffled(no mesmo sentido de sortvs sorted)

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

x = shuffled([1, 2, 3, 4])
print x
malbarbo
fonte
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 está no lugar, portanto, não imprima o resultado None, mas a lista.

Ravi Tanwar
fonte
1

Você pode fazer isso:

>>> 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']

se você quiser voltar para duas listas, divida essa lista longa em duas.

Kiriloff
fonte
1

você pode criar uma função que usa uma lista como parâmetro e retorna uma versão aleatória da lista:

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
fonte
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
Josh Anish
fonte
uma pequena introdução ou explicação seria útil?
Kacase # 28/18
a função é útil para embaralhar a atividade, imagine que você precise embaralhar uma lista de números por três vezes e, nas três vezes em que você precisar que a embaralhamento aleatório ocorra, basta virar o argumento aleatório para True caso contrário, se você não precisar de aleatoriedade e quiser mesma ordem aleatória a ser preservada, não faça alterações, basta executar o código.
21418 Josh Anish
Como não há nenhum caso de uso em que o chamador dessa função decida em tempo de execução se ele deseja a aleatória ou a aleatória não aleatória, essa função deve ser dividida em duas.
toolforger
Não há descrição do que o shuffle não aleatório deve fazer. (Em uma tangente, não é uma resposta então a questão, de modo que não servem ao propósito de estouro de pilha.)
toolforger
1

você pode usar a reprodução aleatória ou a amostra. sendo que ambos vêm do módulo aleatório.

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

OU

import random
def shuffle(arr1):
    random.shuffle(arr1)
    return arr1
Ravi Tanwar
fonte
0

Verifique se você não está nomeando seu arquivo de origem random.py e se não há um arquivo no diretório ativo chamado random.pyc. Isso pode fazer com que o seu programa tente importar o arquivo random.py local em vez do módulo aleatório pythons .

user3298224
fonte
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
fonte
Esta função pode ajudá-lo se você não quiser usar o módulo aleatório
Pogramist
Essa solução não é apenas detalhada, mas ineficiente (o tempo de execução é proporcional ao quadrado do tamanho da lista).
toolforger
O segundo loop pode ser substituído por _list.extend(list2), que é mais sucinto E mais eficiente.
Toolforger # 13/18
Uma função Python que modifica um parâmetro nunca deve retornar um resultado. É apenas uma convenção, mas útil: as pessoas geralmente não têm tempo para analisar a implementação de todas as funções que chamam; portanto, quem vê apenas o nome da sua função e que tem um resultado ficará muito surpreso ao vê-la. atualize seu parâmetro.
Toolforger # 13/18
0
import random
class a:
    foo = "bar"

a1 = a()
a2 = a()
b = [a1.foo,a2.foo]
random.shuffle(b)
Xavier
fonte
-1

O processo de embaralhamento é "com substituição" , portanto a ocorrência de cada item pode mudar! Pelo menos quando os itens da sua lista também estiverem listados.

Por exemplo,

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

Depois de,

random.shuffle(ml)

O número de [0] pode ser 9 ou 8, mas não exatamente 10.

Gatsby
fonte
-1

Plano: escreva o shuffle sem depender de uma biblioteca para fazer o trabalho pesado. Exemplo: Percorra a lista desde o início, começando com o elemento 0; encontre uma nova posição aleatória para ela, digamos 6, coloque o valor de 0 em 6 e o ​​valor de 6 em 0. Passe para o elemento 1 e repita esse processo, e assim por diante pelo resto da lista

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
fonte
você pode trocar variáveis ​​em python assim:my_list[i], my_list[j] = my_list[j], my_list[i]
Karolis Ryselis
-2

Funciona bem. Estou tentando aqui com funções como objetos de lista:

    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()

Ele imprime: foo1 foo2 foo3 foo2 foo3 foo1 (os foos na última linha têm uma ordem aleatória)

Stefan Gruenwald
fonte