Qual é a melhor maneira de randomizar uma matriz de seqüências de caracteres com o .NET? Minha matriz contém cerca de 500 strings e eu gostaria de criar um novo Array
com as mesmas strings, mas em uma ordem aleatória.
Inclua um exemplo de C # na sua resposta.
myArray.Shuffled().ToArray()
(oumyArray.Shuffle()
se você quiser transformar a matriz atual)Respostas:
Se você usa o .NET 3.5, pode usar o seguinte frescor IEnumerable (VB.NET, não C #, mas a ideia deve ficar clara ...):
Edit: OK e aqui está o código VB.NET correspondente:
Segunda edição, em resposta às observações de que System.Random "não é seguro para threads" e "adequado apenas para aplicativos de brinquedo" devido ao retorno de uma sequência baseada em tempo: conforme usado no meu exemplo, Random () é perfeitamente seguro para threads, a menos que você está permitindo que a rotina na qual você aleatoriamente a matriz seja reinserida; nesse caso, você precisará de algo semelhante
lock (MyRandomArray)
para não corromper seus dados, o que protegerárnd
.Além disso, deve-se entender que o System.Random como fonte de entropia não é muito forte. Conforme observado na documentação do MSDN , você deve usar algo derivado
System.Security.Cryptography.RandomNumberGenerator
se estiver fazendo algo relacionado à segurança. Por exemplo:...
...
fonte
OrderBy
armazene em cache as chaves de classificação internamente, isso também tem o problema de violar a propriedade transitiva das comparações ordenadas. Se houver alguma verificação no modo de depuração queOrderBy
produza resultados corretos, em teoria, isso poderá gerar uma exceção.A implementação a seguir usa o algoritmo Fisher-Yates AKA, o Knuth Shuffle. Ele é executado no tempo O (n) e é embaralhado no lugar, por isso tem melhor desempenho do que a técnica 'ordenar aleatoriamente', embora haja mais linhas de código. Veja aqui algumas medidas de desempenho comparativas. Eu usei System.Random, o que é bom para fins não criptográficos. *
Uso:
* Para matrizes mais longas, para tornar igualmente provável o número (extremamente grande) de permutações, seria necessário executar um gerador de números pseudo-aleatórios (PRNG) através de muitas iterações para cada troca para produzir entropia suficiente. Para uma matriz de 500 elementos, apenas uma fração muito pequena dos 500 possíveis! É possível obter permutações usando um PRNG. No entanto, o algoritmo Fisher-Yates é imparcial e, portanto, o shuffle será tão bom quanto o RNG usado.
fonte
array.Shuffle(new Random());
..?new Random()
é inicializado com um valor inicial baseado na hora atual do sistema, que atualiza apenas a cada ~ 16ms.Você está procurando um algoritmo de embaralhamento, certo?
Ok, existem duas maneiras de fazer isso: o inteligente, mas as pessoas sempre parecem entender mal e entender errado, então talvez o seu não seja tão inteligente depois de tudo maneira, e a maneira idiota como pedras, mas quem se importa porque funciona.
Maneira burra
Esse algoritmo funciona bem, mas verifique se é improvável que seu gerador de números aleatórios identifique duas strings com o mesmo número. Por causa do chamado Paradoxo de Aniversário , isso acontece com mais frequência do que você imagina. Sua complexidade de tempo é O ( n log n ).
Maneira inteligente
Vou descrever isso como um algoritmo recursivo:
O equivalente iterativo é percorrer um iterador pela matriz, trocando com elementos aleatórios à medida que avança, mas observe que você não pode trocar com um elemento depois daquele que o iterador aponta. Este é um erro muito comum e leva a um embaralhamento parcial.
A complexidade do tempo é O ( n ).
fonte
Esse algoritmo é simples, mas não eficiente, O (N 2 ). Todos os algoritmos "ordenar por" são normalmente O (N log N). Provavelmente não faz diferença abaixo de centenas de milhares de elementos, mas faria para grandes listas.
A razão pela qual é O (N 2 ) é sutil: List.RemoveAt () é uma operação O (N), a menos que você remova a ordem do final.
fonte
Você também pode criar um método de extensão com Matt Howells. Exemplo.
Então você pode usá-lo como:
fonte
A randomização da matriz é intensiva, pois você precisa mudar um monte de strings. Por que não apenas ler aleatoriamente a partir da matriz? No pior dos casos, você pode até criar uma classe de wrapper com um getNextString (). Se você realmente precisar criar uma matriz aleatória, poderá fazer algo como
O * 5 é arbitrário.
fonte
Pensando em cima da minha cabeça, você poderia fazer o seguinte:
fonte
Gere uma matriz de flutuadores aleatórios ou ints do mesmo comprimento. Classifique essa matriz e faça trocas correspondentes na matriz de destino.
Isso produz um tipo verdadeiramente independente.
fonte
fonte
Jacco, sua solução é um IComparer personalizado não é seguro. As rotinas de classificação exigem que o comparador esteja em conformidade com vários requisitos para funcionar corretamente. O primeiro deles é a consistência. Se o comparador for chamado no mesmo par de objetos, sempre deverá retornar o mesmo resultado. (a comparação também deve ser transitiva).
O não cumprimento desses requisitos pode causar vários problemas na rotina de classificação, incluindo a possibilidade de um loop infinito.
Em relação às soluções que associam um valor numérico aleatório a cada entrada e, em seguida, classificam por esse valor, elas levam a um viés inerente na saída porque sempre que duas entradas recebem o mesmo valor numérico, a aleatoriedade da saída fica comprometida. (Em uma rotina de classificação "estável", o que ocorrer primeiro na entrada será o primeiro na saída. Array.Sort não é estável, mas ainda existe um viés com base no particionamento realizado pelo algoritmo Quicksort).
Você precisa pensar um pouco sobre o nível de aleatoriedade necessário. Se você está executando um site de pôquer no qual precisa de níveis criptográficos de aleatoriedade para se proteger contra um invasor determinado, possui requisitos muito diferentes de alguém que deseja apenas aleatoriamente uma lista de reprodução de músicas.
Para a reprodução aleatória da lista de músicas, não há problema em usar um PRNG inicial (como System.Random). Para um site de pôquer, nem sequer é uma opção e você precisa pensar muito mais sobre o problema do que qualquer um pode fazer por você no stackoverflow. (usar um RNG criptográfico é apenas o começo, é necessário garantir que seu algoritmo não introduza um viés, que você tenha fontes suficientes de entropia e que não exponha nenhum estado interno que comprometa a aleatoriedade subsequente).
fonte
Este post já foi muito bem respondido - use uma implementação de Durstenfeld do embaralhamento Fisher-Yates para obter um resultado rápido e imparcial. Houve até algumas implementações publicadas, embora note que algumas estão realmente incorretas.
Há algum tempo, escrevi algumas postagens sobre a implementação de shuffles completos e parciais usando essa técnica e (neste segundo link, espero acrescentar valor), também um post de acompanhamento sobre como verificar se sua implementação é imparcial , que pode ser usado para verificar qualquer algoritmo de reprodução aleatória. Você pode ver no final do segundo post o efeito de um simples erro na seleção de números aleatórios.
fonte
Ok, isso é claramente um problema do meu lado (pede desculpas ...), mas geralmente uso um método bastante geral e criptograficamente forte.
Shuffle () é uma extensão em qualquer IEnumerable, portanto é possível obter, digamos, números de 0 a 1000 em ordem aleatória em uma lista com
Esse método também não surpreende quando se trata de classificação, uma vez que o valor da classificação é gerado e lembrado exatamente uma vez por elemento na sequência.
fonte
Você não precisa de algoritmos complicados.
Apenas uma linha simples:
Observe que precisamos converter o primeiro
Array
para umList
, se você não usarList
em primeiro lugar.Além disso, lembre-se de que isso não é eficiente para matrizes muito grandes! Caso contrário, é limpo e simples.
fonte
Esta é uma solução completa de console de trabalho com base no exemplo fornecido aqui :
fonte
fonte
Aqui está uma maneira simples de usar o OLINQ:
fonte
fonte
sortedList = source.ToList().OrderBy(x => generator.Next()).ToArray();