Google Interview Question
Software Engineer / DevelopersCountry: United States
Interview Type: Phone Interview
Yes, C(0)=1. There is exactly 1 way to take 0 items: just sit there. Also, if C(0) is defined at all, it must be defined as 1 in order to satisfy the recurrence relation at n=3.
However, if having C(0) defined bothers you that much, you could start the definition at 1:
C(1)=1
C(2)=2
C(3)=4
C(n)=C(n-3)+C(n-2)+C(n-1) (if n>3).
Don't buy your argument for defining C(0) to be 1.
Yes, it fits neatly with the identity, but your argument is bogus.
For n=4.. you have to take 1,2 or 3.. So for n=1 , you have to take nothing.. so C(1) = 0.. C(2) = 1 ( {1, 1}), C(3) = 3 ({1,1,1},{2,1},{1,2})... and by your algo: C(4) = C(3) + C(2) + C(1) which gives 4(3+1+0) .. And also C(5) = 15 and by your algorithm it is 13.. a wrong answer..
Why is the argument for C(0) = 1 bogus? There is exactly one way to take 0 items. I would argue that the real base cases should be C(0) = 1, C(all negative numbers) = 0. The logic here is that if you're left with no items, there's only one thing to do - nothing - and if you've used more items than you have, then you're in an invalid state and there are 0 ways to proceed from there.
Before you decide that this is unreasonable, consider how you'd even solve this problem if the problem wasn't asking you to choose items in groups of 1, 2, or 3, but in groups of 1, 2, ..., or 100. How would you get all the base cases? C(1) would be 1 because if you take 1 you're left in a valid state (0 items left); take any more and you're in an invalid state. Similarly C(2) would be 2 because taking 1 or 2 leaves you in a valid state. C(3) would be 4 because you'd consider C(0) + C(1) + C(2) -- the three possible numbers of remaining items that leave you in a valid state. The C(0) term corresponds to the possibility of taking all 3 items at once. C(4) would then be 8 since it would be C(0) + C(1) + ... + C(3). Again, the C(0) term corresponds to the possibility of taking all 4 items and so should be set to 1 for the equation to make sense.
Well, one could argue that doing nothing, is well, doing nothing and so there are _no_ ways to take 0 items and hence C(0) = 0.
What you say is not a formal mathematical proof. (How do you even formalize your statements?)
It might be a good _convention_ to define C(0) to be 1, but you haven't proved anything i.e. It might be reasonable to _define_ C(0) to be 1, but it is just that. A definition.
A more convincing argument (to me at least) as to why we must define C(0) to be 1, is the generating function approach.
C(n) is the coefficient of x^n in (x + x^2 + x^3)^n.
This is exactly 1, in the case of n = 0 (note that in some approaches to definitions of real numbers, x^0 = 1 is just an axiomatic definition).
This way we map C(0) to the empty _product_ and then it makes sense to define it to be the multiplicative identity, which is 1.
Anyway... this is off-topic.
Yes, I'm aware it's just a definition.
All I'm arguing is that defining C(0) = 1 can be a part of a reasonable method for solving this problem. In fact, making this definition and further defining C(anything negative) = 0 is a solution that has fewer base cases and that also, in some sense, explains the origin of the base cases in skeptical_scientist's initial answer.
There's a reason why nowhere in my reply do I claim that I have given a rigorous mathematical proof demonstrating C(0) = 1. I suppose whether the expression C(0) has any meaning that is mandated by this problem statement is a somewhat philosophical question.
No one is disputing defining c(0) =1. The issue is with the 'doing nothing' reason, posed as if it is something obvious.
C(0) can't be 0, as you don't know how many objects are there in bucket without checking... so in order to make sure there are no objects are left you need to make one attempt, so C(0) = 1, makes sense.
nt print_combinations(int n)
{
int c = 0;
if(n == 1 || n == 2 || n == 3)
return n;
c = c + print_combinations(n - 1);
return n + c;
}
I'm so stupid that I'm satisfied when I found the recursive relation.
Thanks for the reminder of skeptical.scientist that we could use matrix to speed up the computation.
The following is my matrix implementation together with some simple tests:
class G212
{
public static void main(String[] args)
{
int[] C = new int[21];
C[0] = 1; C[1] = 1; C[2] = 2;
for (int i = 3; i <= 20; i++)
{
C[i] = C[i - 3] + C[i - 2] + C[i - 1];
System.out.println("C(" + i + ") = " + C(i));
System.out.println("C(" + i + ") = " + C[i]);
}
}
public static long[][] matrix_multiply(long[][] A, long[][] B)
{
int N = A.length;
long[][] C = new long[N][N];
for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) for (int k = 0; k < N; k++)
{
C[i][j] += A[i][k] * B[k][j];
}
return C;
}
public static long[][] matrix_pow(long[][] A, int pow)
{
int N = A.length;
long[][] B = new long[N][N];
for (int i = 0; i < N; i++) {B[i][i] = 1;}
while (pow > 0)
{
if (1 == (pow % 2)) {B = matrix_multiply(A, B);}
A = matrix_multiply(A, A);
pow /= 2;
}
return B;
}
public static long C(int N)
{
assert(N >= 1);
long[][] matrix = {{1, 1, 1}, {1, 0, 0}, {0, 1, 0}};
matrix = matrix_pow(matrix, N - 1);
return (matrix[0][0] + matrix[1][0] + matrix[2][0]);
}
}
@Anonymous:
Since Alva's hasn't answered your time space question, I will. It's O(1) space and O(log(N)) time.
For this kind of thing, you want to work it out function by function. In this case we start with matrix_multiply since it only uses elementary operations, then work out matrix_pow since it only uses matrix_multiply and elementary ops, and finally C since it calls matrix_pow.
1) matrix_multiply() takes O(N^2) space (because of the new long[N][N]) and O(N^3) time (because of the three nested for(i,j,k < N) loops) to multiply NxN matrices. All other operations are elementary O(1) operations.
2) matrix_pow() takes O(N^2) space (because of the new long[N][N]) and O(N^3*log(pow)) time (because while(pow>0) { pow /= 2; } runs for log(pow) iterations, and in each iteration we are doing two O(N^3) matrix_multiply() calls on NxN matrices).
3) C takes O(log(N)) time and O(1) space, since it calls matrix_pow() on a 3x3 matrix with pow=N, and otherwise only does elementary operations.
Here is a different O(log n) algorithm (i.e different from the matrix method).
I will not explain this algorithm for now, will let you guys try and figure it out. But I will explain it later (after a few days), if no one posts an explanation and someone is actually interested in knowing.
Below is some python code and its output. I am just starting to learn python, so if you have some suggestions to write differently/better, I will be glad to know
Sorry if the post gets too long.
import time # for timing test.
import math # for pow
# tribonacci numbers satisfy t_{n+2} = t_{n-1} + t_{n} + t_{n+1}
# we start with t_{0} = 1, t_{1} = t_{2} = 2, t_{4} = 7.
# so first few are 1, 1,2,4,7,13,24,44,81,149,274.
# check out: // oeis.org / A000073
# Computes tribonacci numbers in O(log n) time.
def tribonacci(n):
t = tribonacci_log(n)
return t[2]
# returns the list [t_{n+2}, t_{n+1}, t_n]
def tribonacci_log(n):
base_case = [274,149,81,44,24,13,7,4,2,1,1]
# we have this as we compute upto t_{n/2 - 4}.
if n <= 8:
return base_case[8-n: 11-n]
t = tribonacci_log(n/2)
# compute t_{n-4}, t_{n-3},t_{n-2}, t_{n-1}
for i in (1,2,3,4):
x = t[-3] - (t[-1] + t[-2])
t.append(x)
# t now looks like [t_{n+2}, ..., t_{n-4}]
# t_{n} = t[2]
rett = [];
# The crux of the algorithm.
for i in (5,4,3):
x = t[2]*t[i] + t[1]*(t[i] + t[i+1]) + t[0]*t[i-1]
rett.insert(0,x)
rett.insert(0,sum(rett));
if n%2 == 0:
return rett[-3:]
else:
return rett[-4:-1]
# Computes tribonacci numbers using the simple linear algo.
def tribonacci_linear(n):
t = [1,1,2]
while n > 0:
t_new = t[0] + t[1] + t[2]
t[0], t[1], t[2] = t[1], t[2], t_new
n = n-1
return t[0]
def tribonacci_correctness_test(num_values):
if num_values <= 0 :
return;
print "Correctness Test (", num_values, ") :"
passed = True
for n in range(1, num_values, 1):
if tribonacci(n) != tribonacci_linear(n):
print "\t Failed", n, tribonacci(n)
passed = False
if passed:
print "\t Passed"
def tribonacci_time_test(timing_value):
print "Timing Test (", timing_value, ") :"
delta1, t1 = time_func(timing_value, tribonacci);
delta2, t2 = time_func(timing_value, tribonacci_linear);
print "\tRecursive method:", delta1;
print "\tLinear method:", delta2;
print "\tComparing values:", t1 == t2
# yeah, can make it take multiple args, whatever.
def time_func(n, func):
before = time.time()
ret = func(n);
after = time.time();
return after - before, ret;
def main():
max_num_values = 2500
tribonacci_correctness_test(max_num_values)
max_timing_value = math.pow(10, 6)
n = 1;
while n <= max_timing_value:
tribonacci_time_test(n)
n = n*10
if __name__ == "__main__":
main()
And this is the output
Correctness Test ( 2500 ) :
Passed
Timing Test ( 1 ) :
Recursive method: 2.14576721191e-06
Linear method: 9.53674316406e-07
Comparing values: True
Timing Test ( 10 ) :
Recursive method: 5.96046447754e-06
Linear method: 3.09944152832e-06
Comparing values: True
Timing Test ( 100 ) :
Recursive method: 1.59740447998e-05
Linear method: 3.40938568115e-05
Comparing values: True
Timing Test ( 1000 ) :
Recursive method: 3.2901763916e-05
Linear method: 0.000290155410767
Comparing values: True
Timing Test ( 10000 ) :
Recursive method: 0.000332832336426
Linear method: 0.00647592544556
Comparing values: True
Timing Test ( 100000 ) :
Recursive method: 0.0115561485291
Linear method: 0.333210945129
Comparing values: True
Timing Test ( 1000000 ) :
Recursive method: 0.437210083008
Linear method: 29.7244770527
Comparing values: True
The comments around the for i in (1,2,3,4) are not exactly right.
The n there should actually be n/2.
The next for loop (in (5,3,4)) is the one that computes t_n.
I ran it and it works.
Suggestion: IN the timing test, print the time immediately after running the test. This was you can see that the recursive version is fast, but the linear one is slow.
A note: we can get best of both worlds by modifying tribonacci to call linear for small values, and log for the rest.
"But I will explain it later (after a few days), if no one posts an explanation and someone is actually interested in knowing."
Ha. Haha. Ha.
I guess I'll have to figure it out myself.
This is based on the recurrence C(n+m) = C(n)C(m-3)+C(n+1)(C(m-3)+C(m-4))+C(n+2)C(m-2). The recurrence is easily proved by induction on m. By using this recurrence, you can find C(2n), C(2n+1), C(2n+2) in terms of C(n), C(n+1), C(n+2) in O(1) time. This gives an O(log(n)) algorithm for computing C(n).
The way this works in the python code here is by substituting m=n, n+1, n+2 in this recurrence relation, to get equations for C(2n), C(2n+1), C(2n+2) in terms of C(n-4)...C(n+2). On input k, set n = k//2, so k==2n or k==2n+1. The function is first called recursively to determine C(n)...C(n+2), then the standard recurrence relation C(n+3) = C(n)+C(n+1)+C(n+2) is applied backwards to find C(n-4)...C(n-1). Then the three equations can be applied to find C(2n), C(2n+1), C(2n+2). Finally the algorithm returns either [C(2n+2), C(2n+1), C(2n)] or [C(2n+3), C(2n+2), C(2n+1)], depending on whether k is even (so k==2n) or k is odd (so k==2n+1).
This problem is similar to coin change problem .. you can solve it using DP
value is the array containing all the possible values
C(n,i) = C(n,i+1)+c(n-value[i],i+1) // Recursive solution
Print out the selections:
void Choice(int& choice, int count, int cur, int arr[], int& pos)
{
if (count == cur)
{
int sum = 0;
for (int i = 0; i< count && sum < count; ++i)
{
printf("%d,", arr[i]);
sum += arr[i];
}
printf("\n\n");
++choice;
return;
}
else if (count < cur)
{
return;
}
else
{
for (int i = 1; i < count; ++i)
{
arr[pos] = i;
Choice (choice, count, i + cur, arr, ++pos);
--pos;
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
const int pickNum = 4;
int arr[pickNum];
int choice = 0;
int pos = 0;
Choice(choice, pickNum, 0, arr, pos);
printf("C(N) = %d\n", choice);
return 0;
}
public static int countC(int n) {
int ways = 0;
if(n>2) {
ways += countC(n-3);
ways += countC(n-2);
ways += countC(n-1);
} else
if(n>1) {
ways += countC(n-2);
ways += countC(n-1);
} else {
ways += 1;
}
return ways;
}
That works. But it is extremely slow. It will take you around 10 minutes to compute C(40) with this method, and two decades to compute C(60). A better algorithm will take under a millisecond.
This is because your algorithm requires O(1.84^n) arithmetic operations to compute C(n). A reasonable algorithm will take O(n) arithmetic operations to compute C(n). A fast algorithm will take O(log n) operations.
Here is a solution without recursion or DP.
Define the combinations by the number of 1s, 2s and 3s in the sequence,
eg 112 has n3 =0, n2 = 1 and n1 =2
and 1111 has n3 = n2 = 0 and n1 =4
This way we can define different combinations by n1,n2,n3,
eg for n1=2,n2=1,n3=0 the three possible combinations are:
112
121
211
the total combinations = (n1+n2+n3)! / (n1! * n2! * n3! )
The maximum possible n3 for a number n is n/n3.
So all we have to do is loop over all the possible n1,n2,n3 for the given n and add the totals along the way.
The code is:
int C(int n){
int total = 0;
for(n3 = 0; n3<= n/n3; n3++){
for(n2 = 0; n2 = (n - 3*n3)/2;n2++){
n1 = n - 3*n3 - 2*n2
total += fact(n1+n2+n3)/fact(n1)*fact(n2)fact(n3)
}
}
return total;
}
Criticisms and comments welcome.
"It's not hard to see that C(n) has the following recursive definition (similar to the Fibonacci numbers)"
Well, maybe I'm not smart enough. I'm not be able to figure out why C(n)=C(n-1)+C(n-2)+C(n-3)
for me
C(1)=1
C(2)=2
C(3)=4
C(4)=7
C(5)=10
And 7 + 4 + 2 = 13, not 10
why C(5)=10?
11111
2111
1211
1121
1112
311
131
113
32
23
Did I missed anything?
ok, I missed
221
212
122
so yes, looks like the equation is correct, but can someone explain why?
Let's say there are n objects in the bucket. You can start by picking 1, 2, or 3. If you start by picking 1, there are C(n-1) ways to pick the remaining n-1 objects. That means there are C(n-1) ways to pick n objects, if you start by taking a single object. Similarly, there are C(n-2) ways to pick n objects if you start with taking 2 objects, and C(n-3) if you start with 3. So in total there are C(n-1)+C(n-2)+C(n-3) ways of picking n objects out of the bucket.
This code print all the permutations, it can be easily changed to print the total number. Also this algorithm prints n too.
#include <iostream>
#include <sstream>
void permutation(const int n,std::string preFix = "")
{
if (preFix.empty()) {//first time only. can be taken out of this function
for (int i = 0 ; i < n ; ++i)
std::cout << preFix << 1;
std::cout << std::endl;
}
for(int i=2; i<=n;i++) {
int pos=0;
while(pos <= (n - i)) {
int numOfOnesAfterPos=0;
std::ostringstream num;
std::ostringstream newPreFix;
bool addedPos=false;
for(int j =0 ; j<n;++j) {
if(j == pos) {
num << i;
j+=i;
addedPos=true;
newPreFix << preFix << num.str();
}
if(j<n) {
num << "1";
if (addedPos) {++numOfOnesAfterPos;}
}
}
std::cout << preFix << num.str() << std::endl;
permutation(numOfOnesAfterPos, newPreFix.str());
++pos;
}
}
}
int main ()
{
std::cout << "start" <<std::endl;
permutation(7);
return 0;
}
#include<stdio.h>
#include<stdlib.h>
void print(int arr[],int n)
{
int j;
if(n!=1)
{for(j=0;j<n;j++)
printf("%d ",arr[j]);
printf("\n");
}
}
void comb(int n ,int index)
{
static int arr[100];
if(n==0)
{
print(arr,index);
return ;
}
else if(n>0)
{
int i;
for(i=1;i<=n;i++)
{
arr[index]=i;
comb(n-i,index+1);
}
}
}
main()
{
comb(4,0);
}
#include<stdio.h>
#include<stdlib.h>
void print(int arr[],int n)
{
int j;
if(n!=1)
{for(j=0;j<n;j++)
printf("%d ",arr[j]);
printf("\n");
}
}
void comb(int n ,int index)
{
static int arr[100];
if(n==0)
{
print(arr,index);
return ;
}
else if(n>0)
{
int i;
for(i=1;i<=n;i++)
{
arr[index]=i;
comb(n-i,index+1);
}
}
}
main()
{
comb(4,0);
}
public static int find(int n){
if(n < 0){
return 0;
}
if(n == 0){
return 1;
}
return find(n - 1) + find(n - 2) + find(n - 3);
}
int count(int n)
{
if (n == 0) return 1;
else if (n == 1) return 1;
else if (n == 2) return 2;
else return count(n-3) + count(n-2) + count(n-1);
}
Others have mentioned this same algorithm. It works, but takes exponential time. You can write a much more efficient algorithm.
Some simple memorization will cause the time to be linear. You can reduce it to logarithmic time, however (or at least a logarithmic number of arithmetic ops, which may beyond some point not be strictly O(1)).
Anonymous replier:
Yes, you can reduce to log(n) arithmetic ops, on input n. C(n) grows exponentially in n, so at least some of the numbers involved will have O(n) bits, and arithmetic ops will take O(n) time if n is large. So you end up with an O(n log n) algorithm that way. Since the final output has O(n) bits, every algorithm must be Omega(n), so O(n log n) is reasonably close to optimal, if it's not actually optimal.
If this question appeared in some sort of programming competition, it would probably ask for the answer modulo a number fitting into an int, and then the arithmetic ops would be done modulo that number and could be assumed to be O(1). The repeated-matrix-squaring approach and the iterative linear approach would then truly be O(log n) and O(n) respectively. Otherwise, if you have to print the entire number (and nothing in the problem statement suggests otherwise), the fast matrix-based approach is O(nlogn) like you indicated (with the best known fast multiplication algorithm), and the naive iterative algorithm is O(n^2).
This can be viewed as a BFS traversal of a graph.
public static int CalculateCn(int num){
int count = 0;
ArrayList<Integer> queue = new ArrayList<Integer>();
queue.add(0);
while(!queue.isEmpty()){
int sum = queue.remove(0);
if(sum == num)
count ++;
else
for(int i=1;i<=Math.min(3, num-sum);i++)
queue.add(sum+i);
}
return count;
}
Interesting, I wouldn't have thought of it as a graph problem, although I can see how it is.
However, this is a really slow brute-force solution, which takes exponential time.
C(40) = 23837527729, and C(100) = 180396380815100901214157639. If you are trying to calculate C(n) by storing and incrementing a count, you're going to have a bad time.
We can do this combinatorially. Since the items are all the same, we can imagine setting all the items side by side next to one another. Suppose we have 4 items, then our layout would look like:
O O O O
What we essentially want to do now is to see all possible ways we could group these items together, suppose we use a sticks to divide.
We can use 1 stick
O | O O O (1 + 3)
O O | O O (2 + 2)
O O O | O (3 + 1)
We can use 2 sticks
O | O | O O (1 + 1 + 2)
O | O O | O (1 + 2 + 1)
O O | O | O (2 + 1 + 1)
We can use 3 sticks
O | O | O | O (1 + 1 + 1 + 1)
Overall we can use anywhere from 1 to n-1 sticks. We get the closed form
C(n) = sigma (n-1) choose i where i = 1:n-1
import math
def C(n):
totalWays = 0
for x in range(1, n):
totalWays += nCk(n-1,x)
return totalWays
def nCk(n, k):
if k == 0 or n == k:
return 1
return int(math.factorial(n) / (math.factorial(n-k) * math.factorial(k)))
public class Program15072768 {
static int count = 0;
static void C(int n,int tsum)
{
if(tsum == n)
{
count++;
return;
}
else if(tsum > n)
{
return;
}
else{
for(int i=1;i<n;i++)
{
C(n,i+tsum);
}
}
}
public static void main(String[] args) {
C(4,0);
System.out.println(count);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("enter the number of identical items greater than 0");
string s = Console.ReadLine();
int x = int.Parse(s);
int firstgroup = 1;
abc ob = new abc();
Console.WriteLine(1+ob.NumberOfways(x));
Console.WriteLine();
}
}
class abc
{
public long NumberOfways(int n)
{
long prevalue=0;
for (int c = 1; c <= 3; c++)
prevalue=prevalue+ fact(n - c) / fact(n - c - 1);
return prevalue;
}
public long fact(int number)
{
if (number == 0)
return 1;
else
return number * fact(number - 1);
}
}
}
pattern should be:
C(1) = 0
C(2) = 1
C(3) = 3
C(4) = 7
C(5) = 15
which follows a trend of powers of 2. So it is just 2^(n-1) - 1
Solution is:
int C(int n)
{
return pow(2,n-1) - 1;
}
C(1)=1: take 1
C(2)=2: take 2, or 1+1
C(3)=4: take 3, 2+1, 1+2, or 1+1+1
C(4) is 7, as in the problem statement.
C(5)=13: take 3+2, 2+3, 3+1+1, 1+3+1, 1+1+3, 2+2+1, 2+1+2, 1+2+2, 2+1+1+1, 1+2+1+1, 1+1+2+1, 1+1+1+2, 1+1+1+1+1
Try again.
import java.util.*;
public class Main {
public static void main(String args[]){
int num =4;
int runningsum=0;
ArrayList<Integer> perm = new ArrayList<Integer>();
permutate(num,runningsum, perm);
}
public static void permutate(int num,int runningsum, ArrayList<Integer> perm){
if(runningsum ==num){
for(int i=0;i<perm.size();i++){
System.out.print(perm.get(i) + " ");
}
System.out.println();
}
for(int i=1;i<=3 && i+runningsum<=num;i++){
Integer cur = i;
perm.add(cur);
permutate(num,i+runningsum,perm);
perm.remove(cur);
}
}
}
It's not hard to see that C(n) has the following recursive definition (similar to the Fibonacci numbers):
C(0)=1
C(1)=1
C(2)=2
C(n)=C(n-3)+C(n-2)+C(n-1) (if n>2)
This recursive definition leads to an obvious (and slow) algorithm for computing C(n). Slightly better is:
This is okay, but still slow if n is very large. Faster algorithms can be found using linear algebra, similar to fast algorithms for the Fibonacci numbers. One way to regard the previous algorithm is to think of L=[1,1,2] as a vector, and then each iteration replaces L by M*L, where M is the matrix
So what we are really doing is computing the first entry of (M^n)*L. Fast algorithms for matrix exponentiation can accelerate this much faster than just repeatedly multiplying (on the left) by M, n times in a row.
- skeptical.scientist January 05, 2013