Reputation: 22694
This must be a classic interview question, however, I'm having a problem understanding it.
Below is my implementation in Python and if you run it, it's only printing ab, ac, ad
. It doesn't go to the 'b' (bc, bd)
level.
def Print_nCk (the_list, k, str_builder, used):
if len(str_builder) == k:
print str_builder
return
else:
for i in xrange(len(the_list)):
if used[i] !=True:
str_builder+=the_list[i]
used[i] = True
Print_nCk(the_list, k, str_builder, used)
str_builder = str_builder[:-1]
Print_nCk(['a','b','c','d'], 2, "",[False,False,False,False])
The right answer is ab,ac,ad,bc,bd,cd
when above line is passed.
I know the right implementation from here without using used
param (http://www.geeksforgeeks.org/print-all-possible-combinations-of-r-elements-in-a-given-array-of-size-n/) but
my question is what is wrong is my implementation?
Can you shed some light on it?
To debug, I printed out "used" every time. The used
param becomes (True, True, True, True) after printing "ad" then it doesn't go deeper than that. What's the smart way to fix the used
, if I insist on using used
?
Upvotes: 2
Views: 1401
Reputation: 402844
Bit late to the party, but I think you should take more advantage of recursion. There's no need to pass any superfluous arguments.
Here's a simpler approach:
def Print_nCk(the_list, size):
combs = []
if size == 1: # the base case
return the_list
for i, c in enumerate(the_list[:-size + 1]):
for sub_comb in Print_nCk(the_list[i + 1:], size - 1): # generate and return all sub-combos of size - 1
combs.append(c + sub_comb) # for each sub-combo, add the sizeth (or n - sizeth) character
return combs
This approach generates combinations of size - 1
and combines them with the size
th character.
For size-2 combinations:
>>> Print_nCk(['a','b','c','d'], 2)
['ab', 'ac', 'ad', 'bc', 'bd', 'cd']
For size-3 combinations:
>>> Print_nCk(['a','b','c','d'], 3)
['abc', 'abd', 'acd', 'bcd']
Upvotes: 2
Reputation: 477210
You forgot to unset used[i]
when you backtrack:
def Print_nCk (the_list, k, str_builder, used):
if len(str_builder) == k:
print str_builder
return
else:
for i in xrange(len(the_list)):
if used[i] != True:
str_builder += the_list[i]
used[i] = True
Print_nCk(the_list, k, str_builder, used)
str_builder = str_builder[:-1]
used[i] = False
Print_nCk(['a','b','c','d'], 2, "",[False,False,False,False])
In your current implementation, you set used[i]
to True
from the moment you pick the i
as value. If however later, you decide to pick another branch, you should do the bookkeeping correctly and thus unset the used[i]
.
Note that now "ab"
and "ba"
will be generated. You thus generate combinations with symmetry. If you do not want that, you can use an additional parameter. That makes sure that you do not use an index lower than the previously picked one:
def Print_nCk (the_list, k, str_builder, used, prev = 0):
if len(str_builder) == k:
print str_builder
return
else:
for i in xrange(prev,len(the_list)):
if used[i] != True:
str_builder += the_list[i]
used[i] = True
Print_nCk(the_list, k, str_builder, used, i+1)
str_builder = str_builder[:-1]
used[i] = False
Print_nCk(['a','b','c','d'], 2, "",[False,False,False,False])
Nevertheless, this more or less defeats the purpose of using a "used" array. You can simply use the prev
:
def Print_nCk (the_list, k, str_builder, prev = 0):
if len(str_builder) == k:
print str_builder
return
else:
for i in xrange(prev,len(the_list)):
str_builder += the_list[i]
Print_nCk(the_list, k, str_builder, i+1)
str_builder = str_builder[:-1]
Print_nCk(['a','b','c','d'], 2, "")
This then prints:
>>> Print_nCk(['a','b','c','d'], 2, "")
ab
ac
ad
bc
bd
cd
Upvotes: 6