User:Melab-1/Archives/Reference Desk/Computing/December 20 2008/Number 7

Printing all possible k-subsets of a n-set
Hello. I want to write a program in C++ which given n and k, prints all possible combinations of k distinct numbers chosen out of 1,2...n. For example if n=4 and k=2 I want to print 12,13,14,23,24,34. (The order has to increasing as well, i.e. 32 is not permitted). I can't seem to get the looping done correctly to apply the brute force method for handling this program. Also, the brute force method would become infeasible to implement if n is big (something around 1000). What is the best approach to handle this problem? Thanks.--Shahab (talk) 14:16, 20 December 2008 (UTC)


 * It seems what you want to do is to generate permutations and then apply a trivial function to compose them (in your example, concatenation). If that's what you mean, then Permutation is for you.  87.114.130.249 (talk) 14:32, 20 December 2008 (UTC)


 * No I guess I don't want all permutations. I want a list of all possible k size subsets of a n size set. For example if n=10, k=2 then I want a list of $$\binom{10}{2}=45$$ numbers of the form 12,67,89 etc. That is all possible ways of selecting 2 numbers out of 1,2,...10. The should ideally be listed in increasing order, although that's not especially important. Thanks for the quick response though.--Shahab (talk) 14:41, 20 December 2008 (UTC)


 * You are going to want recursion. With for loops, you need a for loop for each iteration of k.  If k=2, you need 2 for loops.  If k=3, you need 3 for loops.  It is rather difficult to write a program that magically increases or decreases the number of for loops it has.  With recursion, you have 1 loop that calls itself as many times as needed. --  k a i n a w &trade; 14:57, 20 December 2008 (UTC)


 * Recursion is one approach, but a recursive loop 1000 steps deep is likely to break something. So, here's a non-recursive solution I wrote and tested in FORTRAN:


 * Set this declaration as high as you like or use dynamic memory allocation:

integer*2    ARY(29,100000)     ! Array of (digits,solutions). integer*2    I,N,K,RECORD,DIGIT,VAL


 * Initialization:

RECORD = 1                      ! Current solution number. DIGIT = 0                       ! Current digit. VAL   = 0                       ! Current value of digit.


 * Make these user inputs to improve program:

K = 2                           ! Number of digits. N = 4                           ! Number of values allowed for each digit.


 * Body of program:

IF (K .GT. N) GOTO 600          ! Abort if no solutions are possible.

200 DIGIT = DIGIT + 1               ! Go to the next digit.

300 VAL = VAL + 1                   ! Go to, then store, the next value for the ARY(DIGIT,RECORD) = VAL         ! current digit of the current solution. IF (DIGIT .LT. K) GOTO 200      ! Any more digits ? 400 IF (VAL  .LT. N) THEN           ! Any more values allowed for this digit ? RECORD = RECORD + 1           ! Go to the next solution. DO I = 1,DIGIT-1              ! Copy the old solution up to the previous digit. ARY(I,RECORD)= ARY(I,RECORD-1) ENDDO GOTO 300 ENDIF

500 DIGIT = DIGIT - 1               ! Go to the previous digit. IF (DIGIT .LT. 1) GOTO 600      ! If there's no previous digit, we're done. VAL = ARY(DIGIT,RECORD)         ! Get value of previous digit. IF (VAL+K-DIGIT .GE. N) GOTO 500 ! Digit's value already too high ! to allow increasing values ! to end of solution. GOTO 400


 * Program termination:

600 DO I =1,RECORD+1                ! Add proper prints later. print *,ARY(1 ,I),ARY(2 ,I),ARY(3 ,I),ARY(4 ,I),ARY(5 ,I) +        ,ARY(6 ,I),ARY(7 ,I),ARY(8 ,I),ARY(9 ,I),ARY(10,I) +        ,ARY(11,I),ARY(12,I),ARY(13,I),ARY(14,I),ARY(15,I) +        ,ARY(16,I),ARY(17,I),ARY(18,I),ARY(19,I),ARY(20,I) +        ,ARY(21,I),ARY(22,I),ARY(23,I),ARY(24,I),ARY(25,I) +        ,ARY(26,I),ARY(27,I),ARY(28,I),ARY(29,I) ENDDO


 * You will have to convert to C++ (good luck !). StuRat (talk) 16:48, 20 December 2008 (UTC)
 * I am not sure that I understand the concept of the program. I converted it into C++. The code is given below. But the output is as follows: 1200000000000000000000000000424848013000000000000000000000000022895924199168140000000000000000000000000021472993282300000000000000000000000002291 3480240000000000000000000000000229070003400000000000000000000000001999046107229362400000000000000000000000000022901204198987. This makes no sense to me. Perhaps you can explain what I am doing wrong here. Thanks

Cheers--Shahab (talk) 10:23, 21 December 2008 (UTC)


 * Some things to add in C++ that FORTRAN does automatically:


 * 1) Initialize the array to all zero values.


 * 2) Include a space between each item printed.


 * 3) Start each print on a new line.


 * Here's my output:

1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0


 * When I reformat your prints I get the following:

12000000000000000000000000004248480 13000000000000000000000000022895924199168 14000000000000000000000000002147299328 23000000000000000000000000022913480 24000000000000000000000000022907000 34000000000000000000000000019990461072293624 00000000000000000000000000022901204198987


 * So, you're getting the correct output, but with some garbage values at what should be the end of each line, which probably will be fixed by initialization of all array values to 0 and fixing the prints (just printing the first 3 array elements instead of 29 would hide the ugly random numbers and make the output nicer for your particular test case). Also, isn't there something in C++ called "flushing the (print) buffer" ?  Once you get it working, you can fancy up the prints a bit more, say by suppressing any prints of zeros. StuRat (talk) 15:44, 21 December 2008 (UTC)


 * If you still don't get the concept of how the program works, try going through it manually. That is, write all the variable names down on a piece of paper and record how the values change as you step through the program.  For the array, you could maybe make a 3×7 chart and fill in the values as they are assigned.  This technique can be tremendously helpful in understanding how programs work.  Some debuggers can do this all automatically, but you would likely have to reduce the size of the array to 3×7 (I only made it bigger to show that the same techniques would work with much larger values for N and K). StuRat (talk) 04:26, 22 December 2008 (UTC)


 * Here's one in Haskell:

comb :: Int -> [a] -> a comb 0 _     = comb _ []    = [] comb m (x:xs) = map (x:) (comb (m-1) xs) ++ comb m xs

*Main> comb 2 [1..4] 1,2],[1,3],[1,4],[2,3],[2,4],[3,4 --71.141.111.57 (talk) 12:12, 21 December 2008 (UTC)


 * The article Combinadic may be of use to you, pre-fascicle 3A by Knuth referenced at the end is particularly useful. Dmcq (talk) 12:35, 21 December 2008 (UTC)
 * One way to implement it is to make it like mechanical counters - a circle of numbers in unit place turns one circle and moves the next circle by one notch. The same as listing all the numbers sequentially in base k. Shyamal (talk) 12:09, 22 December 2008 (UTC)


 * Thanks for all ideas. Cheers--Shahab (talk) 07:31, 23 December 2008 (UTC)


 * Were you able to follow my suggestions to get the program to work and figure out the logic behind it ? StuRat (talk) 17:08, 23 December 2008 (UTC)


 * You might look at The Art of Computer Programming volume 4 fascicle 3. —Tamfang (talk) 17:59, 28 December 2008 (UTC)