Whiteboard Challenges

Question: What companies ask those type of questions. I have been in development/architecture for over 20 years, worked with all major fin/telco locally and did some international stints in automative and then classified military and government work and never ever come across any of this.

They are doing this now, even the small poxy sweatshop dev houses in Roodepoort are doing it. I have had to endure heaps of this, to try and get a new job.

We generally focused on practical work where candidates need to prepare a project and then demonstrate as part of the interview. Most interviews purely focused on the candidates fit and background (if you are smart and willing you will achieve anything). Interestingly enough during an interview for the classified projects, a candidate presented a solution to a problem during the interview which has been implemented across most guidance systems.

They are not interested in that anymore, not even evidence of solutions that you did prior to applying. Also totally not interested in people without any degrees but who can code better than some of the blue collar boys. They want degreed, yes men, who toe the line and bend over.

With the current skillset and candidates mostly coming from Discovery/banks/Telcos I have yet to come across anyone one (despite their salaries being in the 70-80K range) who would be able to solve the missing integer question. Fibonacci who you say? Never worked with him - that would be pretty much the standard response.

The Fibonacci series, is a standard snippet of C code I use to evaluate microcontroller performance. Have been using it, oh gosh, since 1999, or somewhere there. I basically know the code out my head to do it in C. Shouldn't be too hard to do in whatever fancy schmantzy Java, Go or whatever the fashion is now.

The reason why I ask about those companies? Well, the big corporates will have architecture and design teams which solve all those issues and would produce specs right down to class-diagrams, SQL queries and the developer is just a typist. I can only think of startups or companies in the engineering/scientific areas asking questions like this.
you would be surprised.
Let me put it to you like this- Times are hard, the stagnant/negative economy is impacting on the rich people. Rich people aka CEOs and the like, will rather fire everyone than take a lifestyle cut, or even a cheaper beemer. So when they hire someone, not only do they do it at a reduced paycheck (the current max for a developer is R40k/month) but they also want to be 100% sure these people are yes-men, and code according to the university textbook. I have found there is no place anymore for R&D, trying new things, or even exploring different solutions... Business is waiting, if that JIRA task dares go to overdue status... written warning incoming. I really am not interested in working in that environment anymore. The BA/architects, come to work when they feel like it, eventually when they get off the phone speaking to their extended family, only then do they do some work.. and then they go home at 2PM, what a sweet deal.
 
Missing Integer Summary.

The requirements:
The missing integer (Amazon, Microsoft)
  • Question: Write a function that takes a sorted array of integers which contains all integers between 0 and N except one value as a parameter and find the missing integer in that array.
  • Example: if you are passed [0,1,2,3,5], this is an array between 0 and 5, it is sorted. However, the number 4 is missing. Your function would need to return (4).

@cguy also included the following related questions:
  • What if the array wasn't sorted? What would the minimum time complexity be?
  • What if you were only allowed O(1) space complexity?

Thanks again to everyone for the solutions; here's my summary of the solutions presented + 1 more.

1. Sorted Array with a known length:
The approach here is based on a binary search pattern; which recursively divides the problem in half until we identify the missing value. Each time we divide we can isolate the section containing the missing value by comparing the index vs. the value at that index; similarly we check left or right of that index.

Code:
/// Find Missing Integer using Binary Search
///
/// - parameter array: array: Array of Integer values in the range (0 to n); 
///                                      one value is missing
/// - parameter n:     Length of Array
///
/// - returns: Missing Integer value
/// - note: Array must be sorted. Time Complexity O(log n)
public func findMissingIntegerBinarySearch(from array: [Int], n: Int) -> Int? {
  guard array.count > 0 && n > 0 else { return nil }
  var (lhs, rhs) = (0, n - 1)
  while lhs <= rhs {
    let mid = (rhs + lhs) >> 1
    if array[mid] != mid {
      if mid == 0 || array[mid - 1] == mid - 1 {
        return mid
      }
      rhs = mid - 1
    } else {
      lhs = mid + 1
    }
  }
  return lhs == n ? n : nil
}

2. Unsorted Array (Gauss Formula):
Here we make use of the famous mathematician, Gauss's formula to find the sum of a series of numbers. As the story goes: In elementary school in the late 1700’s, Gauss was asked to find the sum of the numbers from 1 to 100. The question was assigned as “busy work” by the teacher, but Gauss found the answer rather quickly by discovering a pattern. His observation was as follows:

1 + 2 + 3 + 4 + … + 98 + 99 + 100

Gauss noticed that if he was to split the numbers into two groups (1 to 50 and 51 to 100), he could add them together vertically to get a sum of 101.

[table="width: 500, class: grid"]
[tr]
[td]1[/td]
[td]2[/td]
[td]3[/td]
[td]4[/td]
[td]5[/td]
[td]...[/td]
[td]48[/td]
[td]49[/td]
[td]50[/td]
[/tr]
[tr]
[td]100[/td]
[td]99[/td]
[td]98[/td]
[td]97[/td]
[td]96[/td]
[td]...[/td]
[td]53[/td]
[td]52[/td]
[td]51[/td]
[/tr]
[tr]
[td]101[/td]
[td]101[/td]
[td]101[/td]
[td]101[/td]
[td]101[/td]
[td]...[/td]
[td]101[/td]
[td]101[/td]
[td]101[/td]
[/tr]
[/table]
Gauss realised then that his final total would be 50(101) = 5050. The sequence of numbers (1, 2, 3, … , 100) is arithmetic and when we are looking for the sum of a sequence, we call it a series. Thanks to Gauss, there is a special formula we can use to find the sum of a series:
jo1.1.gif

S is the sum of the series and n is the number of terms in the series, in this case, 100.
jo1.2.gif

Code:
/// Find Missing Integer using Gauss Formula
///
/// - parameter array: Array of Integer values in the range (0 to n); 
///                             one value is missing
///
/// - returns: Missing Integer value
/// - note: Array can be unsorted. Time Complexity O(n). 
///            Unknown Integer overflow. Java / C are "ok"; 
///            i.e. valid result even with overflow, 
///            Swift by default terminates with EXC_BAD_INSTRUCTION; 
///            to replicate the C behaviour, the function needs to use the 
///            overflow operators: &-, &+, &*
public func findMissingIntegerGauss(for array: [Int]) -> Int
{
  let calc = array.reduce((sum: 0, max: 0)) { ($0.sum + $1, $0.max > $1 ? $0.max : $1) }
  let gaussSum = calc.max * (calc.max + 1) / 2
  return gaussSum - calc.sum
}

3. Unsorted Array (XOR):
The final solution is similar to Gauss in that we subtract the total of the sequence from the total of the array; except that in this case we XOR the values together instead of addition; the only benefit of this solution is in preventing an overflow for very large sequences.
Code:
/// Find Missing Integer using XOR
///
/// - parameter array: Array of Integer values in the range (0 to n); 
///                             one value is missing
///
/// - returns: Missing Integer value
/// - note: Array can be unsorted. Time Complexity O(n) + 
///            avoids Integer overflow for large sequences
public func findMissingIntegerXOR(for array: [Int]) -> Int
{
  let calc1 = array.reduce((xor: 0, max: 0)) { ($0.xor ^ $1, $0.max > $1 ? $0.max : $1) }
  let calc2 = (0...calc1.max).reduce(0) { $0 ^ $1 }
  return calc1.xor ^ calc2
}

Code to test these 3 methods
Code:
import Foundation
let ceiling = 1000
var input = (0...ceiling).map { $0 }
input.remove(at: Int(arc4random_uniform(UInt32(ceiling))))

print(findMissingIntegerBinarySearch(from: input, n: input.max()!))
print(findMissingIntegerGauss(for: input))
print(findMissingIntegerXOR(for: input))

The performance is also in order shown:
  1. Binary Search technique is generally the fastest O(log n), but it requires a sorted array.
  2. Guass formula is next inline; sorting is not required
  3. Lastly XOR; sorting is also not required.

For small sequences, depending on the compiler; you will probably noticed that the Gauss and XOR solution tend to be faster, however with significantly larger sequences, the binary search technique wins out.

Potential for improvement
The only unanswered question is whether there is potential make things faster, utilising for example: multi-threading. The binary search technique doesn't lend itself to this, but both the Gauss and XOR methods do.

Why? because both of these calculations are mathematically associative; meaning the result is the same, irrespective of the order of summation. This means we have the potential to split the summation workload (array slices per thread), and recombine with the same end result. Naturally the cost for parallelisation would nullify this benefit for smaller sequences.

And that's it for this question.


Btw the Gauss calculation also works with odd numbers, for example (series up to 9), we simply include zero:
[table="width: 500, class: grid"]
[tr]
[td]0[/td]
[td]1[/td]
[td]2[/td]
[td]3[/td]
[td]4[/td]
[/tr]
[tr]
[td]9[/td]
[td]8[/td]
[td]7[/td]
[td]6[/td]
[td]5[/td]
[/tr]
[tr]
[td]9[/td]
[td]9[/td]
[td]9[/td]
[td]9[/td]
[td]9[/td]
[/tr]
[/table]
Which gives us 5(9) = 45
 
Last edited:
They are doing this now, even the small poxy sweatshop dev houses in Roodepoort are doing it. I have had to endure heaps of this, to try and get a new job.



They are not interested in that anymore, not even evidence of solutions that you did prior to applying. Also totally not interested in people without any degrees but who can code better than some of the blue collar boys. They want degreed, yes men, who toe the line and bend over.



The Fibonacci series, is a standard snippet of C code I use to evaluate microcontroller performance. Have been using it, oh gosh, since 1999, or somewhere there. I basically know the code out my head to do it in C. Shouldn't be too hard to do in whatever fancy schmantzy Java, Go or whatever the fashion is now.


you would be surprised.
Let me put it to you like this- Times are hard, the stagnant/negative economy is impacting on the rich people. Rich people aka CEOs and the like, will rather fire everyone than take a lifestyle cut, or even a cheaper beemer. So when they hire someone, not only do they do it at a reduced paycheck (the current max for a developer is R40k/month) but they also want to be 100% sure these people are yes-men, and code according to the university textbook. I have found there is no place anymore for R&D, trying new things, or even exploring different solutions... Business is waiting, if that JIRA task dares go to overdue status... written warning incoming. I really am not interested in working in that environment anymore. The BA/architects, come to work when they feel like it, eventually when they get off the phone speaking to their extended family, only then do they do some work.. and then they go home at 2PM, what a sweet deal.

If any of this is true, you can easily solve this, by getting a new job.
 
Next challenge up:
Print the Fibonacci numbers: Write a function that recursively prints the Fibonacci numbers below a ceiling value.

I see we also have a proposed solution for:
Rotate a 2D Array: Write a function that rotates a square 2D Array clockwise by 90 degrees in-place

So let's go ahead and try to resolve both of these? I'll post summary on this early next week; so please submit any solutions may have in the meantime.

As an alternate to the 2D array rotation question; please also submit solution for a pure functional version of array rotation. i.e. returning a new array, without affecting the integrity of the source array.

Going forward
As for the difficulty of the questions; we've started with basic / intermediate level questions. Hereafter I'll slowly start in introducing a mix of more challenging questions (and some easy ones) that have been asked by Microsoft, Amazon, Google, etc...
 
Tomorrow I'll be summarising solutions to the "Rotate a 2D Array" problem (2 scenarios):
  • In-place (mutation of an existing instance)
  • Functional Programming: Rotation (immutability, pure function that returns a new instance)
If you have a pending solutions, please post these before tomorrow afternoon.

On Tuesday, I'll be similarly closing out the Fibonacci challenge, so please post any of your proposed solutions by tomorrow.
 
Rotate a 2D Array - Solution Summary

1. Functional Programming Rotation:
In functional style, we'll create a pure function that returns a new copy of the array rotated clockwise by 90 degrees. Here's a diagram depicting the process of the rotation.
Screen Shot 2016-11-28 at 10.44.19 AM.png
The code:
Code:
public func rotated90<T>(for array: [[T]]) -> [[T]] {
  if array.isEmpty { return [[]] }
  return array[0].indices.map {
    index in array.reversed().map { $0[index] }
  }
}
Basically we read each of the columns in reverse, which serves to both transpose the data (vertical to horizontal) and also to flip it horizontally, completing the rotation.
Note: Functional rotation supports any 2D Array, and can support unbalanced dimensions.

2. Mutable In-place Rotation:
In this example we're going to mutate the array instance; meaning after the rotation, all references to this instance will all reflect the change. Here's a diagram depicting the process:

The rotation is performed in steps, starting at the outer border, and moving inwards. We make use of a few techniques to achieve this; we first read the outer two columns in reverse (transpose & flip), next we read the non overlapping top and bottom (standard read of values from left to right), we then overwrite the current values by looping through the indices of each transposed copies (left/right) and similarly also for the not transposed copies (top/bottom); these indices are then used as the column and row offsets respectively. Here's a diagram depicting this for the outer border.
Screen Shot 2016-11-28 at 11.31.39 AM.png
We repeat this process narrowing inwards to the integer value of the dimension divided in half, for example:
  • A dimensional width of 5, will mean we narrow in two steps.
  • Half of 5 = 2.5. Integer value of 2.5 is 2 (Integer conversion drops the decimals)
The reason for this is that the middle value for 5 width array is a single value, and therefore does not need to change. Here's a set of diagrams depicting the before/after mutation with each narrowing:

Step 1:
Screen Shot 2016-11-28 at 10.41.03 AM.png

Step 2:
Screen Shot 2016-11-28 at 10.41.12 AM.png

Final result:
Screen Shot 2016-11-28 at 10.40.27 AM.png

The Code:
Code:
/// Rotate Errors
///
/// - UnbalancedBounds: 2D array bounds are dissimilar, in-place rotation
///                     is only supported for matching bounds
public enum RotateError: Error {
  case UnbalancedBounds
}

/// Rotate 2D Array (in-place)
///
/// - parameter array: Two Dimensional Array of any type
///
/// - throws: Rotate.UnbalancedBounds
/// - note: Both bounds must be the same
public func rotate90<T>(for array: inout [[T]]) throws {
  if array.isEmpty { return }
  guard array.count == array[0].count else {
    throw RotateError.UnbalancedBounds
  }
  
  func rotateBorder(offset: Int, size: Int) {
    // Range to slice values from Left and Right borders
    let rangeLR = stride(from: size - offset, through: offset, by: -1)
    // Range to slice values from top and bottom borders
    let rangeTB = stride(from: offset + 1, to: size - offset, by: 1)
    
    // Slice Left & Right
    let left = rangeLR.map { array[$0][offset] }
    let right = rangeLR.map { array[$0][size - offset] }
    
    // Slice Top & Bottom
    let top = rangeTB.map { array[offset][$0] }
    let bottom = rangeTB.map { array[size - offset][$0] }
    
    // Replace top & bottom with values from left & right
    left.indices.forEach {
      array[offset][$0 + offset] = left[$0]
      array[size - offset][$0 + offset] = right[$0]
    }
    
    // Replace left & right with values from top & bottom
    top.indices.forEach {
      array[offset + 1 + $0][offset] = bottom[$0]
      array[offset + 1 + $0][size - offset] = top[$0]
    }
  }
  
  let size = array.count
  stride(from: 0, to: size / 2, by: 1).forEach {
    rotateBorder(offset: $0, size: size - 1)
  }
}
Note: In-place rotation requires that the 2D Array has balanced dimensions (a square), hence the function can fail (throw exception for UnbalancedBounds)

Finally, here's the usage code for both approaches:
Code:
// Define the 2D array
var array2D = [["A", "B", "C", "D", "E"],
               ["F", "G", "H", "I", "J"],
               ["K", "L", "M", "N", "O"],
               ["P", "Q", "R", "S", "T"],
               ["U", "V", "W", "X", "Y"]]

// Perform in-place rotation
do {
  try rotate90(for: &array2D)
} catch let error as RotateError {
  print("Error: \(error)")
}

// Return result from functional rotation (a new array)
let result = rotated90(for: array2D)
 
Last edited:
Tomorrow I'll summarize the Fibonacci recursive function challenge, plus I be presenting possibly two techniques that can be used to circumvent stackoverflows caused by recursive function calls:
  • CPS: Continuation Passing Style
  • Trampolines
 
Fibonacci Summary

Print the Fibonacci numbers(Amazon, Google, Microsoft)
  • Question: Write a function that recursively prints the Fibonacci numbers below a ceiling value.


What is a recursive function?
A recursive function is a function that makes a call to itself, usually repeating until a condition is satisfied.

Functions can also be mutually recursive. For example:
  • Function f() can call function g() and function g() can call function f(). This is still considered recursion because a function eventually calls itself.
Tail Call Optimisation:
[table="width: 600, class: outer_border"]
[tr]
[td]Some language compilers choose to optimize tail recursive functions because they know that the final result will be in the very last function call. So, the compilers will not create a new stack frame for each recursive call, and will instead just re-use the same stack frame. This saves memory overhead by using a constant amount of stack space, which is why it is called tail recursion optimization. But, keep in mind that some compilers do not perform this optimization on tail recursive functions, which means that the tail recursive function will be run like a normal recursive function and will use a new stack frame for each and every function call.[/td]
[/tr]
[/table]
Screen Shot 2016-11-29 at 7.54.13 AM.png

Why it's used?
Most often recursion is used because it produces a cleaner answer compared to the iterative version. For example, nearly all code written for tree-like structures is recursive and many sorting algorithms are more naturally written recursively as well (e.g. quick sort).

With recursion we need to realise that it often produces solutions that are very compact (few lines of code). Less code is easier to debug than code with a lot of lines, and often writing code that ought to be recursive without recursion (as a loops) can result in a very messy alternative.

Efficiency Considerations
However, recursive solutions can be VERY inefficient, if you aren't careful. For example, the obvious recursive solution to compute the Nth Fibonacci numbers has exponential running time, even though the loop version runs in O(n). This is one good reason to study algorithms and Big O notation, so that you can make a more informed decision about what the running time of a recursive algorithm would be, and ultimately decide if the approach is worthwhile.

Fibonacci
The Fibonacci algorithm was discovered by Leonardo Bonacci (1175 – 1250), who is not only known for the Fibonacci number sequence, but also for popularising the Hindu–Arabic numeral system within the West World.

The Fibonacci numbers are characterised by the fact that every number after the first two is the sum of the two preceding ones, for example:
  • 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
Now for the solution(s)
There are quite a few ways to represent the Fibonacci sequence in code. With this challenge we've been specifically asked to write a recursive function that will returns the sequence of Fibonacci numbers below a ceiling value.

Example 1
PHP:
/// Fibonacci Numbers below ceiling
///
/// - parameter ceiling: Ceiling value
///
/// - returns: Array of Int
public func fibonacci(below ceiling: Int) -> [Int] {
  func recurse(numbers n: (term1: Int, term2: Int), array a: [Int]) -> [Int] {
    return n.term1 + n.term2 >= ceiling ? a :
      recurse(numbers: (n.term2, n.term1 + n.term2),
              array: a + [n.term1 + n.term2])
  }
  return recurse(numbers: (term1: 1, term2: 1), array: [1, 1])
}
In this example, we've made use of a function within a function to hide the tail call passing of values between recursive function calls. Basically the function outwardly only needs the ceiling value. Inwardly our recursive function requires three values:
  • Previous two numbers in the sequence (wrapped in a tuple for convenience)
  • Composed array of numbers already calculated; to which we append the next calculated value and then pass to the function call.

Alternate ways of calculating the sequence.
We can also use formulas to calculate a Fibonacci number at a specific index position; however these methods are less accurate as they depend on the Golden Ratio, which similar to Pi has an almost infinite number of decimals which cannot be accommodate in full by a computer. This means the Golden ratio will always be limited by the precision of the Type used.
Golden Ratio Formula
PHP:
// MARK: - Phi Φ (golden ratio)
// https://en.wikipedia.org/wiki/Golden_ratio
public extension Double {  
  /// Phi formula
  public static var phi: Double {
    return (1 + sqrt(5)) / 2
  }
}
Here we've added an extension method to the Double Type to return phi Φ (the golden ratio)

Example 2a (Recursion using the Golden Ratio, 1st formula)
PHP:
/// Fibonacci number at a specific sequence number
///
/// - parameter n: sequence number
/// - returns: Int
/// - note: Utilises formula provided by Jordan Malachi Dant (April 2005)
///         http://www.goldennumber.net/fibonacci-series/
///         http://www.goldennumber.net/contributors/
public func fibonacci(atPosition n: Int) -> Int
{
  return Int(round(pow(Double.phi, Double(n)) / (Double.phi + 2)))
}

/// Fibonacci numbers below ceiling using the golden ratio
///
/// - parameter ceiling: Ceiling value
///
/// - returns: Array of Int
public func fibonacci1(below ceiling: Int) -> [Int] {
  func recurse(prevSeq s: Int, array a: [Int]) -> [Int] {
    let fib = fibonacci(atPosition: s + 1)
    return fib >= ceiling ? a : recurse(prevSeq: s + 1, array: a + [fib])
  }
  return recurse(prevSeq: 1, array: [])
}

Example 2b (Recursion using the Golden Ratio, alternate formula)
PHP:
/// Fibonacci number at a specific sequence number
///
/// - parameter n: sequence number
/// - returns: Int
/// - note: Utilises formula
///         http://www.goldennumber.net/fibonacci-series/
public func fibonacci2(atPosition n: Int) -> Int
{
  return Int(round(pow(Double.phi, Double(n)) / pow(5, 0.5)))
}

/// Fibonacci numbers below ceiling using the golden ratio
///
/// - parameter ceiling: Ceiling value
///
/// - returns: Array of Int
public func fibonacci2(below ceiling: Int) -> [Int] {
  func recurse(prevSeq s: Int, array a: [Int]) -> [Int] {
    let fib = fibonacci2(atPosition: s + 1)
    return fib >= ceiling ? a : recurse(prevSeq: s + 1, array: a + [fib])
  }
  return recurse(prevSeq: 0, array: [])
}

This is continued in the next post (Alternatives: Continuation Passing Style and Trampolines)
 
Last edited:
Fibonacci Summary Continued

Example 3 - Continuation Passing Style (CPS)
In order to circumvent some of the cost that is incurred by the loading of the stack with successive recursive function calls, we can implement a variation on a functional programming technique called Continuation Passing Style; which is a style of programming in which control is passed explicitly in the form of a continuation.
PHP:
// Identity Function
// https://en.wikipedia.org/wiki/Identity_function
public func id<T>(value: T) -> T {
  return value
}

/// Fibonacci Numbers below ceiling value
///
/// - parameter ceiling: Ceiling value
///
/// - returns: Array of Int
/// - note: Continuation Passing Style
func fibonacci3(below ceiling: Int) -> [Int] {
  func recurse(_ n: (Int, Int), cont: @escaping ([Int]) -> [Int] = id) -> [Int] {
    return (n.0 + n.1 >= ceiling) ? cont([1, 1]) :
      recurse((n.1, n.0 + n.1)) { r in cont(r + [n.0 + n.1]) }
  }
  return recurse((1, 1))
}
Basically the closure is letting us simulate lazy evaluation to do a pseudo tail-call optimisation. In Continuation-Passing style, we pass in a continuation as an extra parameter to our recursive function. Also we created a special Category theory function called an identity function; this very basic function takes a value of any type and simply returns that value. Now that may appear stupid, but it has it's place in almost all category theory principles; in this case it's specifically used for associativity i.e. the combining of closure calls. In short we used the id function to start the process, similarly to how we use 0 and 1 for the functional Reduce and/or MapReduce methods. Essentially 0 and 1 are called the identity elements because they don't affect the results.

Note: In the above example the id function is used as the default value for the closure parameter: "cont: @escaping ([Int]) -> [Int] = id" i.e. when we call "return recurse((1, 1))" with only the tuple values (excluding the cont parameter, it utilises the default, that being the id function)

Example 4 - Trampolines
Considering many language and compilers do not adequately accommodate tail call optimisation; there are going to be scenarios where an excessive number of recursive calls will not only slow down the computation but potentially could also put us at risk of experiencing a stack overflow (insufficient resources).

First let's understand what happens when a compiler supports tail call optimisation. Basically if the compiler during its optimisation phase detects recursion that can effectively be represented as a loop; it internally substitutes a loop or jump code for the recursion.

However as we said, not all compilers support this (because it's often impossible or very difficult to determine this). So how can we still take advantage of recursion but avoid the risk of a stack overflow -- the answer to this is to use a trampoline. In classical recursion the call stack looks something like this:
Screen Shot 2016-11-29 at 6.23.43 AM.png

With a trampoline, we essentially change the calls to look like this; essentially jumping from one call to the next, and for the older programmers (we're simulating the goto statement):
Screen Shot 2016-11-29 at 6.23.53 AM.png

Here's the code for the trampoline (a generic, reusable function that is used with enum pattern matching)
PHP:
/// Trampoline Result
///
/// - Done: Associated value for completion (Accumulator)
/// - Call: Associated value for call (Element, Accumulator)
enum Result<E,A> {
  case Done(A)
  case Call(E, A)
}

/// Trampoline Function (Solution for lack of tail call optimization)
///
/// - parameter fn: Closure; with a Element and a Accumulator, returns a Trampoline Result<E, A)
///
/// - returns: Returns a closure, requiring an Element & Accumulator and ultimately returning the accumulator
func trampoline<E, A>(with fn: @escaping (E, A) -> Result<E, A>) -> ((E, A) -> A) {
  return { (element: E, accumulator: A) -> A in
    var result = fn(element, accumulator)
    while true {
      switch result {
      case let .Done(accumulator):
        return accumulator
      case let .Call(number, accumulator):
        result = fn(number, accumulator)
      }
    }
  }
}
As you can see the internals of the trampoline is essentially a while loop with a switch statement, evaluating whether the next operation is either a .Call or whether we're .Done:
  • If it's a .Call; we then retrieve the associated values (tuple + array), and wrap in a new function call, assigned to result.
  • Alternatively for .Done, we simply return the accumulator; which in the case of the Fibonacci sequence is the array.
Finally here's the recursive Fibonacci function rewritten with a trampoline
PHP:
/// Fibonacci Numbers below ceiling value
///
/// - parameter ceiling: Ceiling value
///
/// - returns: Array of Int
/// - note: Recurse calling using a trampoline
public func fibonacci4(below ceiling: Int) -> [Int] {
  let fibBounce = trampoline(with: { (n: (Int, Int), array: [Int]) -> Result<(Int, Int), [Int]> in
    if n.0 + n.1 > ceiling {
      return .Done(array)
    }
    return .Call((n.1, n.0 + n.1), array + [n.0 + n.1])
  })
  return fibBounce((1, 1), [1, 1])
}
Basically we similarly wrap the recursive part of the function internally as a variable called fibBounce, which utilises the trampoline, but with a closure signature specific to the needs of the Fibonacci algorithm:
  • Tuple of the last two values in the sequence
  • The array of values already calculated, to which we'll add the new one
It then, in accordance with the trampoline returns an enum Result of the tuple and the array. Internally similar to the other recursive functions, we check if our next value is below the ceiling, if not we return the array, otherwise we pass the tuple (of the last two values) and then append that sum to the array. Finally we set the enum case to reflect another .Call() with the tuple and array.

Example of calling the 5 recursive functions
PHP:
print("\n", fibonacci(below: 1000000000))

print("\n", fibonacci1(below: 1000000000))

print("\n", fibonacci2(below: 1000000000))

print("\n", fibonacci3(below: 1000000000))

print("\n", fibonacci4(below: 1000000000))
3 and 4 are the fastest (< 0.02ms), followed by 1 and 2; the slowest in the bunch is the first fibonacci function shared (~1ms); due to the size of the recursive function and because we're recursively stacking all of the calls, i.e. compiler is unable to optimise the tail calls.

And that's it for this challenge.
Naturally I've provided a lot more than you need to complete this challenge, but I wanted to share with you a few ways of tackling this common problem, and also how to use functional constructs like CPS (Continuation Passing Style) and Trampolines to overcome the negativities of recursive calls i.e. stack loading.

Ps. the code is Swift, not PHP; just decided to use those tags to add a bit of color to the code blocks. Pity MyBB forums don't support something like Rouge or Pygments or even embedded Gists.
 
Last edited:
I'm hoping these posts are not too technical, and that you're at least deriving some benefit from the explanations. Anyway feel free to PM for any question or for a better explanation, or even to help convert these code examples to your preferred language.
 
Next Set of Challenges

Highest Product Of 3(Amazon)
  • Question: Given a list_of_ints, Write a function to find the highest_product you can get from three of the integers. The input list_of_ints will always have at least three integers.

...and then remainder of the questions are binary tree related
(in order of difficulty):


Mirror image of itself (Google)
  • Question: Write a function that returns true if a binary tree is a mirror image of itself provided the root of that tree.

Invert a Binary Tree (Left becomes Right, Right becomes Left: Horizontal Flip) (Google)
  • Question: Write a function that flips the nodes of a binary tree (left to Right, Right to Left)
    Example (Visually picture this rotated 90 degrees clockwise), before and after:
    attachment.php

Balanced Binary Tree(Amazon)
  • Question: Write a function to see if a binary tree balanced. A tree is "superbalanced" if the difference between the depths of any two leaf nodes is no greater than one.

Go ahead submit any solutions for these.
 
Last edited:
I'm going to leave these questions open until next week; on Monday I'll probably (time permitting) post solutions to at least 2 of these.

Highest product of 3 is certainly the easiest one; anyone care to try?
 
Last edited:
[)roi(];18765328 said:
I'm going to leave these questions open until next week; on Monday I'll probably (time permitting) post solutions to at least 2 of these.

Highest product of 3 is certainly the easiest one; anyone care to try?

A pity there are no takers on the fast dot product.

BTW, I think I can see an O(N) solution to the highest product of 3, vs the obvious O(N^3) solution.
 
A pity there are no takers on the fast dot product.

BTW, I think I can see an O(N) solution to the highest product of 3, vs the obvious O(N^3) solution.
I would've gotten there, but thought tackling concurrency would just derail the thread early. Appears it was destined for that all along. Not sure whether it was the selection of questions that was poor: too challenging or not enough? or whether confidence came into play.

Anyway I'm intending to wrap it all up next week; at which stage we can always close off with a discussion of concurrency + a few code examples.

As for highest product of 3; I was thinking more along the lines of O(nlogn) with O(1) space? Simple solution.

/Edit nevertheless I'll present both options: O(n) and the simple one I had in mind.
 
Last edited:
[)roi(];18768256 said:
I would've gotten there, but thought tackling concurrency would just derail the thread early. Appears it was destined for that all along. Not sure whether it was the selection of questions that was poor: too challenging or not enough? or whether confidence came into play.

Anyway I'm intending to wrap it all up next week; at which stage we can always close off with a discussion of concurrency + a few code examples.

As for highest product of 3; I was thinking more along the lines of O(nlogn) with O(1) space? Simple solution.

/Edit nevertheless I'll present both options: O(n) and the simple one I had in mind.

Concurrency is only a small part of the dot product question. One can phrase the question: "Assuming just one tread, what can be done?"

I suspect we had a similar idea for the product of 3 question, however, you don't need to do a full sort, which is where the O(n) comes in.
 
[)roi(];18768256 said:
I would've gotten there, but thought tackling concurrency would just derail the thread early. Appears it was destined for that all along. Not sure whether it was the selection of questions that was poor: too challenging or not enough? or whether confidence came into play.

Anyway I'm intending to wrap it all up next week; at which stage we can always close off with a discussion of concurrency + a few code examples.

As for highest product of 3; I was thinking more along the lines of O(nlogn) with O(1) space? Simple solution.

/Edit nevertheless I'll present both options: O(n) and the simple one I had in mind.

You and cguy are way above us. :)
I'd have joined but my laptop is in for repairs.
 
I suspect we had a similar idea for the product of 3 question, however, you don't need to do a full sort, which is where the O(n) comes in.
Exactly... the reason I leaned towards the full sort is that it's easy... easy to explain and easy to understand, but of course a simple loop and registers is preferred.

Concurrency is only a small part of the dot product question. One can phrase the question: "Assuming just one tread, what can be done?"
tread == thread. The problem here is trying to find an amicable balance between technicality and capability. Nevertheless, let's take a rain cheque on this until the conclusion of the problems; at which point the wheels can fall off...
 
[)roi(];18769512 said:
tread == thread. The problem here is trying to find an amicable balance between technicality and capability. Nevertheless, let's take a rain cheque on this until the conclusion of the problems; at which point the wheels can fall off...

Sure. It's your tread. ;)
 
You and cguy are way above us. :)
I'd have joined but my laptop is in for repairs.
There’s an ever-present hilariousness in programming blogs where person X tries to explain what a .... is, and quite often miserably fails to derive X. Generally in the law of the process: anyone trying to explain what ... X... is probably going to fail, even if they account for how hard explaining ...X... is.
 
Highest Product Of 3 - Summary

Highest Product Of 3(Amazon)
  • Question: Given a list_of_ints, Write a function to find the highest_product you can get from three of the integers. The input list_of_ints will always have at least three integers.

To close out the week; here's two solutions to this challenge.

Sorting the array of Integers,
In order to compare values at the top and bottom of the array. Why? because the product of 2 negative numbers could result in a larger positive value i.e. we compared the following:
  • Product of Top 3 postive values in the sorted array
  • versus. Product of the 2 bottom values in the sorted array + 1 value from the top; i.e. potentially 2 highest negatives, and highest positive value.

Code:
Code:
func largestProductOf3(_ v: inout [Int]) -> [Int] {
  v.sort(by: <)
  let ceiling = v[(v.count - 3...v.count - 1)].map { $0 }
  let floor = (v[0...1] + [v[v.count - 1]]).map { $0 }
  return ceiling.reduce(1, *) > floor.reduce(1, *) ? ceiling : floor
}
This delivers a time complexity of O(nlogn), assuming Introsort (Combo of Quicksort & Heapsort). Space complexity is O(1).

Solution 2: Record highest / lowest values whilst looping once through the array
This solution takes a bit more effort, because we have to not only keep a min/max values, but instead we have to keep the three highest positive values, and the 2 lowest negative values.

Code:
Code:
internal struct StackX {
  private let size: Int
  internal private(set) var value = [Int]()
  
  internal init(size: Int) {
    assert(size > 0, "Invalid StackX size")
    self.size = size
  }
  
  internal mutating func push(_ v: Int) {
    guard self.value.count == self.size else {
      self.value.insert(v, at: 0)
      return
    }
    let positive = v >= 0 ? true : false
    let min = positive ? self.value.min()! : self.value.max()!
    if (positive && min < v) || (!positive && min > v) {
      self.value[self.value.index(of: min)!] = v
    }
  }
}

func largestProductOf3Alt(_ v: [Int]) -> [Int] {
  var negatives = StackX(size: 2)
  var positives = StackX(size: 3)
  v.forEach { _ = $0 >= 0 ? positives.push($0) : negatives.push($0) }
  let max = positives.value.max()!
  let neg_pos = negatives.value + [max]
  return neg_pos.reduce(1, *) > positives.value.reduce(1, *) ? neg_pos : positives.value
}
Essentially we've created a primitive stack type (an imperfect & very topic specific version), onto which we push a quantity of values, and the stack determines whether to replace an existing value for one that is higher (positive) or lower(negative) than what we already have. Time complexity for this one is O(n) and similarly O(1) for space complexity.

Here's the code for running this.
Code:
var arr = [1, 2, 3, 4, -101, 5, 67, -8, -63, -52, 6, 9, 15, 43, 45, 2, 5, 8, 23]
print(largestProductOf3(&arr)) // [-101, -63, 67]

let arrAlt = [1, 2, 3, 4, -101, 5, 67, -8, -63, -52, 6, 9, 15, 43, 45, 2, 5, 8, 23]
print(largestProductOf3Alt(arrant)) // [-101, -63, 67]
That's it for this challenge, enjoy your weekend.
 
Last edited:
Top
Sign up to the MyBroadband newsletter
X