Nothing much has changed.
After a series of embarassing public disasters involving OOP & mutable state, the world has finally realized the wisdom of John Hughes and switched to functional programming. Google has purchased Typesafe & Scala runs natively on Android. Not to be outdone, Apple has banned all iOS apps not coded in Haskell. The Windows ecosystem is now powered entirely by F#.
Other than that, nothing much has changed.
The canonical Mr. Shipper graduates from UPenn and lands his first whiteboard interview at the big G.
Google: So Mr. Shipper, do you know FizzBuzz ?
Shipper: Are you kidding me ? This is 2016, not 2012. Everyone and their grandmother knows Fizzbuzz.
Google: OK, lets try a slightly modified fizzbuzz.
Problem: How do you map the list on the left to the list on the right ?
Google: Yeah
Shipper: But you aren't mapping every element, just the multiples of 3 or 5.
Google: Yeah
Shipper: Looks like a partial function to me.
Google: Partial Function ? What's that ?
Shipper: Lets start with a predicate that defines who qualifies,
scala> val isFizzBuzz = ((x:Int) => x%3 ==0 || x%5 ==0)
Google: Fine. So how do you use that ?
Shipper: I'll make another lambda that defines the mapping.
scala> val toFizzBuzz = ((x:Int) => (x%3,x%5) match {
case (0,0) => "fizzbuzz"
case (0,_) => "fizz"
case _ => "buzz"
})
Google: OK. I see you know how to pattern match...
Shipper: Dude, this is 2016, not 2012 ! Everyone & their grandmother...
Google: Yeah, yeah. Got it. Now tell me what this partial function is ?
Shipper: The partial function just bundles together the predicate and the mapping. So
scala> val fizzbuzz = new PartialFunction[Int,String] {
def isDefinedAt(x:Int) = isFizzBuzz(x)
def apply(x:Int) = toFizzBuzz(x)
}
Now I just collect the results over which this partial function is defined.
scala> (1 to 100).collect( fizzbuzz )
res1: scala.collection.immutable.IndexedSeq[String] = Vector(fizz, buzz, fizz,fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz,
buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizz,buzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz)
Google: Bravo! That is neat.
Shipper: Yeah!
Google: But tell me, Mr. Shipper, why do you need this partial function ? Can't you do something simpler ?
Shipper: You mean like map filter ?
Google: OK, lets try that...
Shipper: So I'd first filter with my predicate and then apply my mapping.
scala> (1 to 100).filter( isFizzBuzz ).map( toFizzBuzz )
res2: scala.collection.immutable.IndexedSeq[String] = Vector(fizz, buzz, fizz,fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz,
buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizz,buzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz)
Google: Good! But there's yet another way to do this.
Shipper: Hmmm...
Google: Think Sets. How do you define a set in math ?
Shipper: You mean, like a list comprehension?
Google: You got it!
Shipper:
scala> for( x<- (1 to 100); if( isFizzBuzz(x))) yield toFizzBuzz(x)
res3: scala.collection.immutable.IndexedSeq[String] = Vector(fizz, buzz, fizz,fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz,
buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizz,buzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz)
Google: Excellent! So you know how to do fizzbuzz atleast 3 different ways.
Shipper: Do I get the job ?
Google: Patience, Mr. Shipper. Patience! This is Google. We can't hire everybody who walks through the door and solves a fizzbuzz. This is 2016, after all. Everybody would get hired.
Shipper: So..
Google: So we here at Google have come up with a new test. Its called the Inverse FizzBuzz.
Shipper: Inverse FizzBuzz !
Google: Good! Looks like you never heard of it.
Shipper: Never!
Google: Well, we just came up with it last week. But its not so hard. You know what an inverse is.
Shipper: Inverse...
Google: Yeah, like 5 plus what is 0 ?
Shipper: -5
Google: So minus 5 is ?
Shipper: Oh! Yeah. OK. Minus 5 is the additive inverse of 5.
Google: So what's the multiplicative inverse of 5 ?
Shipper: Hmmm. 0.2. Because 0.2 times 5 is 1.
Google: You got it! What's the inverse sine of a half ?
Shipper: 30 degrees, because sine(30) = 0.5
Google: Good. What's the inverse logarithm of a half ?
Shipper: The square root of e.
Google: Very Good! So lets talk about the inverse of a fizzbuzz.
Shipper: OK. How do you define Inverse FizzBuzz ?
Google: Very simple. Given a list, give me the shortest contiguous sequence of numbers that produces that list when you run fizbuzz
Shipper : Huh ?!
Google: Inverse FizzBuzz - Given a list, what's the shortest contiguous sequence of numbers that produces that list when you run fizbuzz ?
Shipper: I don't follow.
Google: Lets do a few simple examples. What's the shortest sequence that produces { fizz }
Shipper: Hmm. { 1,2,3 }
Google: Come on Mr. Shipper. You can do better than that.
Shipper: OK. Its just {3}.
Google: What's the shortest sequence that produces { buzz }
Shipper: { 5 }
Google: What's the shortest sequence that produces { fizz, buzz }
Shipper: { 3, 4, 5 }
Google: Nope!
Shipper: OK. Wait....its {9,10}
Google: Excellent! What's the shortest sequence that produces { buzz, fizz }
Shipper: { 5,6 }
Google: What's the shortest sequence that produces { fizz, buzz, fizz }
Shipper: { 3, 4, 5, 6 }
Google: What's the shortest sequence that produces { buzz, fizz, buzz }
Shipper: Hmmm. I don't think that list is valid...
Google: Good. So your program will have to detect that. Lets do a few more. What produces { fizz, fizz } ?
Shipper: { 6,7,8,9 }
Google: What produces {fizz, fizz, buzz }
Shipper: { 6,7,8,9,10}
Google: OK. So here's the examples we've seen so far. This is what your lambda must do.
Shipper: Well, the solution is not unique. The sequence {fizz} maps to { 3 }, but it could also map to {6}. Or {9} ...
Google: Yeah, well, I don't care about uniqueness. Just the shortest sequence.
Shipper: So what's the problem again ?
Google: Given a list, what's the shortest contiguous sequence of numbers that produces that list when you run fizbuzz ?
Shipper: I honestly don't know.
Google: Well, what do you do when you don't know how to solve a problem ?
Shipper: Try everything. Brute force.
Google: Exactly!
Shipper: But brute force! That would be like an infinite number of input strings.
Google: Lets say we only care about the first 100 numbers. 1 to 100. Just like before. How many possible combinations on those ?
Shipper: Well, I could run fizzbuzz on 1. Then {1,2} Then {1,2,3}, Then {1,2,3,4} etc. upto {1,2,....100}
Google: So that's a 100 combinations.Shipper: But then I could run fizzbuzz on 2. Then {2,3} Then {2,3,4}, Then {2,3,4,5} etc. upto {2,....100}
Google: So that's how many ?
Shipper: 99. Ok I got it. So its like 100 + 99 + 98 + ...1 = (101)*100/2 = 5050 combinations.
Google: So five thousand plus. That's a small number.
Shipper: Right. But we kill duplicates. And we only keep the mapping that produces the shortest sequence.
Google: Yeah, so your final list will be much smaller than 5050.
Shipper: OK. I think I can do that quite easily.
scala> val all = (1 to 100).map(x=> (x to 100).map( y=> (x,y) )).flatten
all: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,1), (1,2),(1,3), (1,4), (1,5), (1,6), (1,7), (1,8), (1,9), (1,10), (1,11), (1,12), (1,13),
(1,14), (1,15), (1,16), (1,17), (1,18), (1,19), (1,20), (1,21), (1,22), (1,23), (1,24), (1,25), (1,26), (1,27), (1,28), (1,29), (1,30), (1,31), (1,32), (1,33), (1,34), (1,35), (1,36), (1,37), (1,38), (1,39), (1,40), (1,41), (1,42), (1,43),......(2,2),(2,3),......
scala> all.size
res22: Int = 5050
Google: So we have the 5050 possible ranges.
Shipper: Yes. Now I just run fizzbuzz on every one of those 5050 ranges.
scala> val results = all.map( x=> (x._1 to x._2).collect( fizzbuzz ))
Shipper: Now I'll line up the inputs with the outputs.
scala> val io = all.zip( results )
Shipper: Now I'll group by the output. That should give me a mapping from the list of strings to a list of sequence of numbers.
scala> val inverses = io.groupBy( x=> x._2)
Google: Lets first see if you are on the right track so far. Count the keys in your mapping. It should be much smaller than 5050.
scala> inverses.keys.size
res36: Int = 302
Google: Good! Only 302 unique keys, not 5050. Now how would you find the smallest value the key maps to ?
Shipper: Lemme first test with "fizz".
scala> inverses(Vector("fizz"))
res28: scala.collection.immutable.IndexedSeq[((Int, Int), scala.collection.immutable.IndexedSeq[String])] = Vector(((1,3),Vector(fizz)), ((1,4),Vector(fizz)), ((2,3),Vector(fizz)), ((2,4),Vector(fizz)), ((3,3),Vector(fizz)), ((3,4),Vector(fizz)), ((6,6),Vector(fizz)), ((6,7),Vector(fizz)), ((6,8),Vector(fizz)), ((7,9),Vector(fizz)), ((8,9),Vector(fizz)), ((9,9),Vector(fizz)), ((11,12),Vector(fizz)), ((11,13),Vector(fizz)), ((11,14),Vector(fizz)), ((12,12),Vector(fizz)), ((12,13),Vector(fizz)), ((12,14),Vector(fizz)), ((16,18),Vector(fizz)), ((16,19),Vector(fizz)), ((17,18),Vector(fizz)), ((17,19),Vector(fizz)), ((18,18),Vector(fizz)), ((18,19),Vector(fizz)), ((21,21),Vector(fizz)), ((21,22),Vector(fizz)), ((21,23),Vector(fizz)), ((22,24),Vector(fizz)), ((23,24),Vector(fizz)), ((24,24),V...
Shipper: OK. That gives me all the ranges my key maps to. I just need to find the smallest range. Lets try minBy.
scala> inverses(Vector("fizz")).minBy(x=>x._1._2 - x._1._1)
res33: ((Int, Int), scala.collection.immutable.IndexedSeq[String]) = ((3,3),Vector(fizz))
Shipper: Hey, I think I got it!
scala> val answer = inverses.keys.map( k=> (k,inverses(k).minBy(x => x._1._2 - x._1._1)._1)).toMap
answer: scala.collection.immutable.Map[scala.collection.immutable.IndexedSeq[String],(Int, Int)] = Map(Vector(fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz) -> (3,33), Vector(buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz) -> (10,93), Vector(fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz) -> (15,75), Vector(fizz, buzz, fizz, fizzbuzz) -> (9,15), Vector(fizzbuzz, fizz, buzz, fizz, fizz, b...
Google: Lets check the answers to the 7 examples.
scala> answer(Vector("fizz"))
(3,3)
scala> answer(Vector("buzz"))
(5,5)
scala> answer(Vector("fizz","fizz","buzz"))
(6,10)
scala> answer(Vector("fizz","buzz"))
(9,10)
scala> answer(Vector("buzz","fizz"))
(5,6)
scala> answer(Vector("fizz","buzz","fizz"))
(3,6)
scala> answer(Vector("fizz","fizz"))
(6,9)
Shipper: Voila!
Google: Absolutely. Voila!
Shipper: So do I get the job now ?
Google: Well, you've used a brute force approach. Tell you what. Come up with a non-brute force answer & the job's yours.
Shipper: Thinking.....Thinking.....Thinking.....
PS: Its actually not that hard :)
So, to recap:
Question: Inverse FizzBuzz - Given a list, what's the shortest contiguous sequence of numbers that produces that list when you run fizbuzz ?
Brute force solution:
val all = (1 to 100).map(x=> (x to 100).map( y=> (x,y) )).flatten
val results = all.map( x=> (x._1 to x._2).collect( fizzbuzz ))
val io = all.zip( results )
val inverses = io.groupBy( x=> x._2)
val answer = inverses.keys.map( k=> (k,inverses(k).minBy(x => x._1._2 - x._1._1)._1)).toMap
