Skip to content

Instantly share code, notes, and snippets.

@erica
Created April 24, 2015 20:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save erica/5820cb05acb58820ae37 to your computer and use it in GitHub Desktop.
Save erica/5820cb05acb58820ae37 to your computer and use it in GitHub Desktop.
Collection Randomizer
import Foundation
public struct RandomGenerator<C: CollectionType> : GeneratorType, SequenceType {
private var backingGenerator : PermutationGenerator<C, [C.Index]>
public init(_ elements : C) {
var indices = Array(elements.startIndex..<elements.endIndex)
for index in 0..<count(indices) {
var swapIndex = index + Int(arc4random_uniform(UInt32(count(indices) - index)))
if swapIndex != index {
swap(&indices[index], &indices[swapIndex])
}
}
backingGenerator = PermutationGenerator(elements: elements, indices: indices)
}
public typealias Element = C.Generator.Element
public typealias Generator = PermutationGenerator<C, [C.Index]>
public mutating func next() -> Element? {return backingGenerator.next()}
public func generate() -> PermutationGenerator<C, [C.Index]> {return backingGenerator}
}
let a = "πŸ˜€πŸ˜£πŸ˜¨πŸ˜­πŸ˜±πŸ˜·πŸ˜ΈπŸ˜½πŸ˜ΎπŸ‘£πŸ™€πŸ‘€πŸ˜“"
let b = "Hello There"
let c = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let d = ["A":"B", "C":"D", "E":"F", "G":"H"]
let e = ["A":1, "B":2, "C":3, "D":4]
var f = Set(1...10)
println(Array(RandomGenerator(a)))
println(Array(RandomGenerator(b)))
println(Array(RandomGenerator(c)))
println(Array(RandomGenerator(d)))
println(Array(RandomGenerator(e)))
println(Array(RandomGenerator(f)))
@lilyball
Copy link

Why is RandomGenerator a GeneratorType when it doesn't return itself from the generate() method? If you're returning the underlying PermutationGenerator then you can drop the GeneratorType conformance, the Element typealias, and the next() method.

Also, just a nitpick, but if you want to iterate over the indices of the array it's more idiomatic to say for index in indices(ary) (though you really need to rename the local indices variable in that case).

@lilyball
Copy link

Here's a version that should be more efficient. It uses the "inside-out" Fisher-Yates shuffle:

import Darwin // for arc4random_uniform

public struct RandomSequence<C: CollectionType>: SequenceType {
    public init(_ elements : C) {
        var indices = ContiguousArray<C.Index>()
        indices.reserveCapacity(numericCast(count(elements)))
        // "inside-out" Fisher-Yates shuffle
        for (i, idx) in enumerate(Swift.indices(elements)) {
            let j = Int(arc4random_uniform(UInt32(i)+1)) // in the range [0,i]
            indices.append(idx)
            if j != i {
                swap(&indices[j], &indices[i])
            }
        }
        backingGenerator = PermutationGenerator(elements: elements, indices: indices)
    }
    public typealias Generator = PermutationGenerator<C, ContiguousArray<C.Index>>
    public func generate() -> Generator {return backingGenerator}

    private var backingGenerator: Generator
}

@lilyball
Copy link

Note: I used ContiguousArray mostly for the heck of it. You can replace that with Array if you feel like it. They should be basically the same thing when the element type isn't an obj-c class/protocol.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment