Skip to content

Instantly share code, notes, and snippets.

@danfishgold
Last active September 25, 2015 23:47
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 danfishgold/4e2116aa7d680b42db30 to your computer and use it in GitHub Desktop.
Save danfishgold/4e2116aa7d680b42db30 to your computer and use it in GitHub Desktop.
My dirty 2:30 AM solution for the Omni Group’s Swift Bike Shedding Club week two question: http://rubyquiz.com/quiz2.html (via Brent)
/*
sort families by their size. (let's name them A, B, C, D, and E.)
(if the biggest family is over half of the enitre group there's no valid solution.)
while A.count > B.count
pair up santas from A (the biggest family) and E (the smallest)
(e.g., Dan A is Brent E's santa and vice versa)
while there are still unmatched people
make loops of people
(e.g., Alex A is Brian B's santa. Brian is Clair C's santa, ..., and Eric E is Alex A's santa)
After each step of new santas make sure the smallest family isn't empty.
If it is empty just throw it out and treat the next one (D) as the smallest.
*/
import Foundation
struct Person: Hashable, CustomStringConvertible {
var firstName: String
var lastName: String
init(_ name: String) {
let nameParts = name.componentsSeparatedByString(" ")
self.firstName = nameParts[0]
self.lastName = nameParts[1]
}
var description: String {
return "\(firstName) \(lastName)"
}
var hashValue: Int {
return description.hashValue
}
}
func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.hashValue == rhs.hashValue
}
func tally<T, S: Hashable>(array: [T], key: T -> S) -> [S: [T]] {
var res: [S: [T]] = [:]
for element in array {
let k = key(element)
res[k] = (res[k] ?? []) + [element]
}
return res
}
func findSantas(people: [Person]) -> [Person: Person]? {
// Tally by last name, sort by family size.
var families = tally(people, key: { person in person.lastName }).values.sort({ $0.count > $1.count })
// Verify that the biggest family isn't too big.
guard !families.isEmpty && families.first!.count <= people.count/2 else { return nil }
var santas: [Person: Person] = [:]
while families[0].count > families[1].count {
let p1 = families[0].removeLast()
let p2 = families[families.count-1].removeLast()
santas[p1] = p2
santas[p2] = p1
if families.last?.isEmpty ?? false {
families.removeLast()
}
}
while !families.isEmpty {
var previousGuy = families.last!.last!
for i in 0..<families.count {
let newGuy = families[i].removeLast()
santas[previousGuy] = newGuy
previousGuy = newGuy
}
while families.last?.isEmpty ?? false {
families.removeLast()
}
}
return santas
}
let people1 = [Person("Luke Skywalker"), Person("Leia Skywalker"), Person("Toula Portokalos"), Person("Gus Portokalos"), Person("Bruce Wayne"), Person("Virgil Brigman"), Person("Lindsey Brigman"), Person("Franz Kafka"), Person("Courtney Kafka"), Person("Skip Kafka"), Person("Aloysius Kafka")]
let people2 = [Person("Luke Skywalker"), Person("Leia Skywalker"), Person("Toula Portokalos")]
print("Solution exists: \(findSantas(people1))") // valid
print("Solution doesn't exist: \(findSantas(people2))") // returns nil
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment