5

This was an interview question, which I failed to answer, but am still curious about how to solve.

You have big family of N persons, 1, 2, 3, ..., N years old respectively. You want to take a picture of your very large family.There were to be present all of your family members arranged in one row.

"I, a friend of the family, advised to arrange the family members as follows:"

  1. The 1 year old family member is to sit at the left end of the row.
  2. The difference in ages of every two family members sitting beside of each other mustn’t exceed 2 years.

Input: integer number N, 1 ≤ N ≤ 55.

Output: The number of possible photos that can be made by the photographer.

Example -> Input: 4, Output: 4

Arrays that match the conditions:

[1,2,3,4][1,2,4,3][1,3,2,4][1,3,4,2]

Another Example:

Input: 5 Output: 6

Arrays that match the conditions:

[1,2,3,4,5][1,2,3,5,4][1,2,4,3,5][1,2,4,5,3][1,3,2,4,5][1,3,5,4,2]

I "solved" this problem, in Ruby, my tool of choice, by generating every permutation and filtering them, first by checking condition #1, making sure that the first entry of the array == 1, which is nice and quick.

Second by walking each array from left to right and ensuring the absolute value difference of each pair does not exceed 2. Which is terrible and slow.

My implementation, gets very slow when N > 9. As it is sorting through 300k permutations. From there the time taken is quadratic(I believe, still figuring this out).

How should I go about improving this situation?

I'm not really looking for code samples here, more ideas, which algorithm should be used to sort the permutations efficiently? Should I write my own permutation(probably yes).

Along those lines I did copy this version of QuickPerm https://stackoverflow.com/a/2390969/1265528

Added a condition around the results << yield(a) to only pick the arrays starting with 1, but I'm not sure how to best implement the rest of the aforementioned conditions from there.

EDIT

Here is the incredibly disappointing solution.

I really wanted to figure out how to generate the permutations, not an integer representing the number of possible correct permutations. -

def number_of_possible_perms(n_persons)
  array = []
  array[0] = 0
  array[1] = array[2] = 1
  for i in 3..n_persons
    array[i] = array[i-1] + array[i-3] + 1
  end
  return array[n_persons]
end
Community
  • 1
  • 1
James
  • 4,927
  • 3
  • 22
  • 27
  • 1
    Try to apply dynamic programming here. Start from the left. There cannot be many possible combinations at each stage. For example, if the 1st 2 entries are 1,3 then the next entry can only be 2 or 4, cannot be 5 or more. – Abhishek Bansal Jul 30 '14 at 17:35
  • I don't believe this is a dynamic programming (DP) problem. DPis a techinque for finding an optimal solution, given an objective. When there are multiple optima (e.g., if all solutions that satisfy the permutation requirement have a value of `1` and all others a value of `0`, given an appropriate definition for a "state variable"), DP could be used to find one feasible permutation, but not all of them. Dynamic programming uses recursion, and recursion may be appropriate here, but that doen't mean DP is. – Cary Swoveland Jul 30 '14 at 20:06
  • @CarySwoveland I think you might be confusing definitions. DP is not exclusively an optimization technique - it is often used to describe the "bottom up" approach of solving certain classes of recursive problems. Based on Jared's answer, it looks like this is the perfect kind of problem for that sort of DP. – Max Jul 30 '14 at 22:01
  • @user1990169, I used to teach courses on optimization techniques that included dynamic programming. Your comment caused me to wonder if the nomenclature has changed. Based on [this](http://en.wikipedia.org/wiki/Dynamic_programming) and [this](http://web.mit.edu/15.053/www/AMP-Chapter-11.pdf), for example, I think not. – Cary Swoveland Jul 31 '14 at 00:44
  • @CarySwoveland AFAIU, Dynamic Programming is a general technique for solving a class of problems where the problem can be divided into simpler sub-problems and by solving these sub-problems, we can get to the solution of the original problem in an efficient way. Whether this can be applied only for optimization problems, I don't know. However, I will I will take back the word "dynamic Programming" and replace it with "technique similar to dynamic programming optimization technique". – Abhishek Bansal Jul 31 '14 at 04:13

4 Answers4

8

If we map out the possible transitions, it should make it clearer how to figure this out:

  2   6---8
 /|\ /|\ /|\
1 | 4 | 7 | 10...etc
 \|/ \|/ \|/
  3---5   9

Let the total number of paths that touch every number only once and begin at 1 be C_n where n is the number of nodes. Let's consider some possible cases:

  • C_1 = 1
  • C_2 = 1
  • C_3 = 2

Now suppose n > 3. Let's consider some possible sequences:

  • 1,2,... we know that if it begins this way, we can rearrange our graph by removing 1 and setting 2 as the start, and it's identical to the graph from 1 to n-1. So we have C_(n-1) sequences beginning with 1,2.
  • 1,3,2,... we can do the same thing here since our next step must be 4. Rearrange the graph to begin at 4 and we have C_(n-3) sequences beginning with 1,3,2.
  • 1,3,4,... We have two possibilities here: either we have only 4 nodes and 1 sequence (1,3,4,2) or we have more than 4 nodes and 0 sequences.
  • 1,3,5,... We have two possibilities again: either we have only 4 nodes and 0 sequences or we have more than 4 nodes and 1 sequence (once you've gone up by 2 (after 3) you must go up by 2 until you reach the end, and then go down by 2).

So we now have that C_n = C_(n-1) + C_(n-3) + 1.

0

I'm not sure whether an optimized algorithm for this problem exists, but a brute-force approach that comes to my mind is backtracking.

It is more efficient than iterating over all possible precomputed permutations, as it can immediately stop if the first non-matching pair is found. So, it can prune huge parts of the search space (in other words, it does not have to look at all N! permutations).

Philipp Claßen
  • 41,306
  • 31
  • 146
  • 239
0

Create a method f that takes N and returns an array of the possible arrays. In doing this, use recursion. When N = 1, you know that the possibility is only [1], so the output should be [[1]]. Otherwise, calculate f(N - 1). To get f(N) from it, consider which positions N can be inserted into in each array in f(N - 1).

sawa
  • 165,429
  • 45
  • 277
  • 381
0

I would do it as follows.

Terms

  • N: number of persons
  • A(n,i): the set of all permutations that satisfy the ordering requirements ("feasible permutation") among the first n family members such that the person age i is last, 2 <= i <= n.

Objective

Determine the union of A(N,i) taken over all i=2..N.

Algorithm

n = 2

There is but one way to order persons 1 and 2:

A(2,2) = { [1,2] }

n = 3

and two ways to order the first 3:

A(3,2) = { [1,3,2] }
A(3,3) = { [1,2,3] }

n = 4

We're starting to get something a little more interesting when we consider the first four. Initially, disregard the requirement that adjacent members differ in age by at most two years.

A(4,2) = { [1,4,3,2], [1,3,4,2] }
A(4,3) = { [1,4,2,3], [1,2,4,3] }
A(4,4) = { [1,3,2,4], [1,2,3,4] }

Notice how these sets were determined. Consider A(4,2). We take the single permutation in A(3,2), and insert 4 into each of two possible locations.

We next remove all combinations that don't satisfy the adjacent age difference requirement, and are left with the following:

A(4,2) = { [1,3,4,2] }
A(4,3) = { [1,2,4,3] }
A(4,4) = { [1,3,2,4], [1,2,3,4] }

n=5

Again, first calculate the sets for n=5 without reference to the adjacent age requirement:

A(5,2) = { [1,5,3,4,2], [1,3,5,4,2], [1,3,4,5,2] }
A(5,3) = { [1,5,2,4,3], [1,2,5,4,3], [1,2,4,5,3] }
A(5,4) = { [1,5,3,2,4], [1,3,5,2,4], [1,3,2,5,4], 
           [1,5,2,3,4], [1,2,5,3,4], [1,2,3,5,4] }
A(5,5) = { [1,3,4,2,5], [1,2,4,3,5], [1,3,2,4,5], [1,2,3,4,5 }

Notice how these sets were generated combinatorially from A(4,2), A(4,3), A(4,4) and A(5,2). To compute A(5,2), for example, for each permuation in A(4,2) (there's just one), we insert 5 into each position after the first and before the last.

Now remove all but the feasible permuations, leaving us with:

A(5,2) = { [1,3,5,4,2], [1,3,4,5,2] }
A(5,3) = { }
A(5,4) = { [1,3,5,2,4], [1,2,3,5,4] }
A(5,5) = { [1,2,4,3,5], [1,3,2,4,5], [1,2,3,4,5 }

Continue in this fashion until A(N,i) have been computed for all i=2,...N.

If N => 5, the feasible permutations would be the union of these four sets:

{ [1,3,5,4,2], [1,3,4,5,2], [1,3,5,2,4], [1,2,3,5,4],
  [1,2,4,3,5], [1,3,2,4,5], [1,2,3,4,5 }

I expect additional logic could be applied to speed the calculations, by eliminating groups of permutations at one time.

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100