Se você possui um NSMutableArray
, como embaralha os elementos aleatoriamente?
(Eu tenho minha própria resposta para isso, que está publicada abaixo, mas eu sou novo no Cocoa e estou interessado em saber se existe uma maneira melhor.)
Atualização: conforme observado pelo @Mukesh, no iOS 10+ e no macOS 10.12+, existe um -[NSMutableArray shuffledArray]
método que pode ser usado para embaralhar. Consulte https://developer.apple.com/documentation/foundation/nsarray/1640855-shuffledarray?language=objc para obter detalhes. (Mas observe que isso cria uma nova matriz, em vez de embaralhar os elementos no lugar.)
objective-c
cocoa
shuffle
Kristopher Johnson
fonte
fonte
for (NSUInteger i = self.count; i > 1; i--) [self exchangeObjectAtIndex:i - 1 withObjectAtIndex:arc4random_uniform((u_int32_t)i)];
API
é que ele retorna um novoArray
endereço para um novo local na memória.Respostas:
Você não precisa do método swapObjectAtIndex. exchangeObjectAtIndex: withObjectAtIndex: já existe.
fonte
Resolvi isso adicionando uma categoria ao NSMutableArray.
Edit: Removido o método desnecessário graças à resposta de Ladd.
Edit: Alterado
(arc4random() % nElements)
paraarc4random_uniform(nElements)
obrigado para responder por Gregory Goltsov e comentários por miho e blahdiblahEdit: Melhoria do loop, graças ao comentário de Ron
Edit: Adicionado verificação de que o array não está vazio, graças ao comentário de Mahesh Agrawal
fonte
arc4random_uniform(nElements)
vez dearc4random()%nElements
. Veja a página de manual do arc4random e esta explicação do viés do módulo para mais informações.Como ainda não posso comentar, pensei em contribuir com uma resposta completa. Modifiquei a implementação de Kristopher Johnson para o meu projeto de várias maneiras (realmente tentando torná-lo o mais conciso possível), uma delas
arc4random_uniform()
porque evita o viés do módulo .fonte
[self count]
(um getter de propriedade) duas vezes em cada iteração pelo loop. Eu acho que movê-lo para fora do circuito vale a pena a perda de concisão.[object method]
vez deobject.method
: as pessoas tendem a esquecer que o último não é tão barato quanto acessar um membro struct, ele vem com o custo de uma chamada de método ... muito ruim em um loop.Se você importar
GameplayKit
, há umashuffled
API:https://developer.apple.com/reference/foundation/nsarray/1640855-shuffled
fonte
shuffledArray = [array shuffledArray];
GameplayKit
então você precisa importá-lo.Uma solução ligeiramente aprimorada e concisa (em comparação com as principais respostas).
O algoritmo é o mesmo e é descrito na literatura como " shuffle de Fisher-Yates ".
No Objetivo-C:
No Swift 3.2 e 4.x:
No Swift 3.0 e 3.1:
Nota: É possível uma solução mais concisa no Swift no iOS10 usando
GameplayKit
.Nota: Um algoritmo para embaralhar instável (com todas as posições forçadas a mudar se a contagem> 1) também estiver disponível
fonte
Esta é a maneira mais simples e rápida de embaralhar NSArrays ou NSMutableArrays (quebra-cabeças de objetos é um NSMutableArray, contém objetos de quebra-cabeça. Adicionei ao índice de variável de objeto de quebra-cabeça que indica a posição inicial na matriz)
saída de log:
você também pode comparar obj1 com obj2 e decidir o que deseja retornar possíveis valores:
fonte
Existe uma boa biblioteca popular, que tem esse método como parte, chamada SSToolKit no GitHub . O arquivo NSMutableArray + SSToolkitAdditions.h contém o método shuffle. Você pode usá-lo também. Entre isso, parece haver toneladas de coisas úteis.
A página principal desta biblioteca está aqui .
Se você usar isso, seu código será assim:
Esta biblioteca também possui um Pod (consulte CocoaPods)
fonte
No iOS 10, você pode usar o NSArray
shuffled()
no GameplayKit . Aqui está um ajudante para o Array no Swift 3:fonte
Se os elementos tiverem repetições.
por exemplo, matriz: AAABB ou BBAAA
única solução é: ABABA
sequenceSelected
é um NSMutableArray que armazena elementos da classe obj, que são ponteiros para alguma sequência.fonte
static
impede impede o trabalho em várias instâncias: seria muito mais seguro e legível usar dois métodos, um principal que embaralha e chama o método secundário, enquanto o método secundário apenas se autodenomina e nunca reorganiza. Também há um erro de ortografia.fonte
arc4random_uniform([theArray count])
seria ainda melhor, se disponível na versão do Mac OS X ou iOS compatível.Resposta de Kristopher Johnson é bastante agradável, mas não é totalmente aleatória.
Dada uma matriz de 2 elementos, essa função sempre retorna a matriz inversa, porque você está gerando o intervalo aleatório nos demais índices. Uma
shuffle()
função mais precisa seria comofonte
i < (count-1)
.)Edit: Isso não está correto. Para fins de referência, não excluí esta postagem. Veja comentários sobre o motivo pelo qual essa abordagem não está correta.
Código simples aqui:
fonte