GIDForums  

Go Back   GIDForums > Computer Programming Forums > C++ Forum
User Name
Password
Register FAQ Members List Calendar Search Today's Posts Mark Forums Read

 
 
Thread Tools Search this Thread Rate Thread
  #1  
Old 12-Oct-2006, 10:18
cpit cpit is offline
Junior Member
 
Join Date: Jan 2006
Posts: 56
cpit is on a distinguished road

combinations


Hi,

I have a problem to obtain all combinations...

The vector lastroute={1, 2, 2, 3} yields 12 combinations (1x2x2x3 = 12)

matrix of combinations:

a b c d
1 1 1 1 ---> combination 1
1 1 1 2 ---> combination 2...
1 1 1 3
1 1 2 1
1 1 2 2
1 1 2 3
1 2 1 1
1 2 1 2
1 2 1 3
1 2 2 1
1 2 2 2
1 2 2 3

The logic we can see is:
the last (d) column vary between 1 and 3. (see lastroute[])
the last-1 (c) column vary between 1 and 2 (but on each column it repeat 3 times because the last column vary between 1 and 3).
the last-2 (b) column vary between 1 and 2 (but repeat 6 times each value
because the columns d vary up to 3 and c up to 2, then 3x2=6).
the last-3 (a) do not vary (but repeat 3x2x2= 12 times).

I thought in this way to create a matrix of combinations, my recursive function reserves memory to routes = new int[nroutes*lastroute.size()]
= 12x4 => routes = new int[48] and fill it column by column from the last to first column.

This strategy runs for small combinations, but I need to treat values like

15.925.248 combinations with 91 columns, or be, I would need a 15.925.248x98 matrix.

Thus, the program needs a lot of memory (about 5GB for this matrix) and I need an alternative form to create all combinations.

The question is:

Can you help-me to obtain the combinations through a line by line strategy and not column by column?

In this way I would can obtain the first combination, perform what I want and discard it, obtain the second combination, perform what I want and discard it... and so on.

regards,

CPIT
  #2  
Old 12-Oct-2006, 13:09
WaltP's Avatar
WaltP WaltP is offline
Outstanding Member
 
Join Date: Feb 2004
Location: Midwest US
Posts: 3,243
WaltP is a name known to allWaltP is a name known to allWaltP is a name known to allWaltP is a name known to allWaltP is a name known to allWaltP is a name known to all

Re: combinations


Quote:
Originally Posted by cpit
Hi,

I have a problem to obtain all combinations...

The vector lastroute={1, 2, 2, 3} yields 12 combinations (1x2x2x3 = 12)

matrix of combinations:

a b c d
1 1 1 1 ---> combination 1
1 1 1 2 ---> combination 2...
1 1 1 3
1 1 2 1
1 1 2 2
1 1 2 3
1 2 1 1
1 2 1 2
1 2 1 3
1 2 2 1
1 2 2 2
1 2 2 3
What about
2 2 2 2
2 1 2 2
2 2 1 2
2 2 2 1
2 1 1 2
2 1 2 1
2 2 1 1
2 1 1 1
2 3 2 2
2 2 3 2
2 2 2 3
2 3 3 2
2 3 2 3
etc...

I think there are more than 12.
__________________

Age is unimportant -- except in cheese
  #3  
Old 12-Oct-2006, 15:30
davekw7x davekw7x is offline
Outstanding Member
 
Join Date: Feb 2004
Location: Left Coast, USA
Posts: 4,703
davekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to behold

Re: combinations


Quote:
Originally Posted by cpit
Hi,

I have a problem to obtain all combinations...

The vector lastroute={1, 2, 2, 3} yields 12 combinations (1x2x2x3 = 12)

Can you help-me to obtain the combinations through a line by line strategy and not column by column?

A couple of things come to mind. I assume you want a generic program that can handle any number of columns with varying numbers of entries in each.

First of all, consider an iterative solution.

With a fixed number of columns, you have a fixed number of loops. I'll show a program that works with your example. This only works for problems with four columns, but I hope you can see how to generalize it:

CPP / C++ / C Code:
#include <stdio.h>

int main()
{
    int column[4] = {1, 2, 2, 3};
    int loop[4];
    int total = 0;
    int j;


    for (loop[0] = 1; loop[0] <= column[0]; loop[0]++) {
        for (loop[1] = 1; loop[1] <= column[1]; loop[1]++) {
            for (loop[2] = 1; loop[2] <= column[2]; loop[2]++) {
                for (loop[3] = 1; loop[3] <= column[3]; loop[3]++) {
                    for (j = 0; j < 4; j++) {
                    printf("%d ", loop[j]);
                    }
                    printf("\n");
                    total++;
                }
            }
        }
    }
    printf("\nTotal count = %d\n", total);
    free(column);
    return 0;
}

Output:
Code:
1 1 1 1 1 1 1 2 1 1 1 3 1 1 2 1 1 1 2 2 1 1 2 3 1 2 1 1 1 2 1 2 1 2 1 3 1 2 2 1 1 2 2 2 1 2 2 3 Total count = 12

Now, if you want to handle a problem with five columns, you have to change the program (change the arrays and add a fifth nested loop). Note that the innermost loop (the one with j) has nothing to do with generating the combinations; it's just there to see what you have each time through the other loops.

I have tried to write it in a way that anyone can see how to create a program with any number of columns. And when I say "anyone", I also mean a program. You could write a program whose output is the C-code (or C++ if you prefer) for a program like this, where the number of columns (and their count values) is an input to the generator program. The generator program could be written in practically any language. (Perhaps perl or java or python or ...) The generator program could be written in C.

I'll get you started:
CPP / C++ / C Code:
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i;
    int num_columns;
    int *column;
    int *loop;
    char *outname = "test.c";
    FILE *outfile;

    /* here is where you would put the code to let the user
     * enter the number of columns and the count values for
     * each. For purposes of illustration, I'll just give them
     * some values.
     */

    /*~~~~~beginning of block that will eventually get user input~~~*/
    num_columns = 4;
    column = malloc(num_columns * sizeof(int));
    column[0] = 1;
    column[1] = 2;
    column[2] = 2;
    column[3] = 3;
    /*~~~~~~~~~~~~~~~~~~~~end of user input block~~~~~~~~~~~~~~~~~~~*/

    /* now generate the program */
    if ((outfile = fopen(outname, "w")) == NULL) {
        printf("Can't open %s for writing\n", outname);
        exit(1);
    }
    printf("Opened output file %s\n", outname);

    fprintf(outfile, "/* test.c */\n");
    fprintf(outfile, "#include <stdio.h>\n");
    fprintf(outfile, "int main() {\n");
    fprintf(outfile, "int column[%d] = {", num_columns);
    for (i = 0; i < num_columns; i++) {
        fprintf(outfile, "%d", column[i]);
        if (i < num_columns-1) {
            fprintf(outfile, ", ");
        }
        else {
            fprintf(outfile, "};\n");
        }
    }
    fprintf(outfile, "int loop[%d];\n", num_columns);
    fprintf(outfile, "int total = 0;\n");
    fprintf(outfile, "int j;\n");
    /* now comes the fun stuff of nested loops. I'll just do something
     * simple
     */
    fprintf(outfile, "printf(\"The 'Hello World' of program generators.\\n\");\n");

    fprintf(outfile, "return 0;\n}\n");

    return 0;
}

Compiling and executing this, you can see the output program test.c:

CPP / C++ / C Code:
/* test.c */
#include <stdio.h>
int main() {
int column[4] = {1, 2, 2, 3};
int loop[4];
int total = 0;
int j;
printf("The 'Hello World' of program generators.\n");
return 0;
}

I haven't tried to maintain any indentation or other stylistic standards here; the idea is to get something that works. (Automatically generated programs like test.c are, typically, ugly from an human's point of view.)

Now, if you compile and execute test.c, you should see something like:
Code:
The 'Hello World' of program generators.

It may or may not be tough to work through generation code for the nested loops, but I think it's a definite possibility.

The other possibility that comes to mind: Forget the code generator, forget the 91 nested loops for the 91-column project. Just use recursion. In this case recursion is probably very practical; the code will probably be shorter, and the maximum recursion depth need be no deeper than the number of columns. It may or may not be faster than going through the program generator, and that little issue might be an easy topic for research for the interested student.

A number of recursive methods for generating combinations can be found on the web; most are concerned with the number of combinations of n things taken k at a time, and not with your problem, where each of the n things has its own modulus. (You might or might not find specific solutions, but if you get a basic recursive combination generator program running, you might see how to enhance it to suit your requirements.

Regards,

Dave

Regards,

Dave
  #4  
Old 13-Oct-2006, 15:51
cpit cpit is offline
Junior Member
 
Join Date: Jan 2006
Posts: 56
cpit is on a distinguished road

Re: combinations


Thank you for help me.

I tried to implement a recursive form to:

CPP / C++ / C Code:
    int column[4] = {1, 2, 2, 3};
    int loop[4];
    int total = 0;
    int j;

   

    for (loop[0] = 1; loop[0] <= column[0]; loop[0]++) {
        for (loop[1] = 1; loop[1] <= column[1]; loop[1]++) {
            for (loop[2] = 1; loop[2] <= column[2]; loop[2]++) {
                for (loop[3] = 1; loop[3] <= column[3]; loop[3]++) {
                    for (j = 0; j < 4; j++) {
                    printf("%d ", loop[j]);
                    }
                    printf("\n");
                    total++;
                }
            }
        }
    }
    printf("\nTotal count = %d\n", total);
    free(column);
Output
1 1 1 1
1 1 1 2
1 1 1 3
1 1 2 1
1 1 2 2
1 1 2 3
1 2 1 1
1 2 1 2
1 2 1 3
1 2 2 1
1 2 2 2
1 2 2 3



Here is my piece of program in c++

CPP / C++ / C Code:
#include <iostream>
#include <vector>


using namespace std;
  
   
void fillrec(int *col_f, vector<int> lastroute_f, int i){
    
   if(i<lastroute_f.size()){
        for(col_f[i] = 1; col_f[i] <= lastroute_f[i]; col_f[i]++) {        
        fillrec(col_f,lastroute_f,++i);
        }
        
        }else{
             for (int j = 0; j < lastroute_f.size(); j++) {
                cout << col_f[j] << " ";
             }
             cout <<endl;                   
   }
 
}


int main()
{

 vector<int> lastroute;
 lastroute.push_back(1);
 lastroute.push_back(2);
 lastroute.push_back(2);
 lastroute.push_back(3);

 int col[lastroute.size()];

 fillrec(col,lastroute,0);
    
 system("PAUSE");
    
 return 0;
}

output
1 1 1 1
1 1 1 2
1 1 2 1
1 1 2 2
1 2 1 1
1 2 1 2
1 2 2 1
1 2 2 2

But the output is wrong and I'm not getting my mistakes.

regards,

CPIT
  #5  
Old 13-Oct-2006, 22:18
davekw7x davekw7x is offline
Outstanding Member
 
Join Date: Feb 2004
Location: Left Coast, USA
Posts: 4,703
davekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to behold

Re: combinations


Quote:
Originally Posted by cpit

I tried to implement a recursive form to:

I won't try to "fix" the code you showed, since recursive techniques are kind of hard to get ones brain around sometimes, and everyone looks at them a little differently.

Here's a way that occurred to me:

Start at the leftmost column. Call a routine that displays that column and everything to the right of it by calling that routine recursively. The actual display is done when the recursion reaches the right most column.

It's almost easier to do than it is to think about. I will use fixed values of N and the vector of counts just for simplicity. In actuality, these would be inputs to the program. (Command-line inputs, or maybe even from a file.)

CPP / C++ / C Code:
#include <stdio.h>

void showvals(int *arr, int *col, int place, int size);

int main()
{
#define N 4
    int col[N] = {1, 2, 2, 3};
    int vals[N];

    showvals(vals, col, 0, N);

    return 0;
}


/* 
 * shows values for everything to the right of "place"
 *
 * "place" is the current column. 
 *
 * Go through the range of the current column and for each
 * value, show everything to the right by calling showvals
 * recursively, using "place + 1"
 *
 * When "place" is the rightmost column, go through the
 * range of that column, using everything to the left of it
 *
 */
void showvals(int *vals, int *col, int place, int size)
{
    int i;
    int j;
    if (place == size-1) {
        for (j = 1; j <= col[size-1]; j++) {
            for (i = 0; i < size-1; i++) {
                printf("%d ", vals[i]);
            }
            printf("%d\n", j);
        }
        return;
    }
    else {
        for(vals[place] = 1; vals[place] <= col[place]; vals[place]++) {
            showvals(vals, col, place+1, size);
        }
    }

}

Output:
Code:
1 1 1 1 1 1 1 2 1 1 1 3 1 1 2 1 1 1 2 2 1 1 2 3 1 2 1 1 1 2 1 2 1 2 1 3 1 2 2 1 1 2 2 2 1 2 2 3


I've done it in C for a couple of reasons: Everyone can play along (I know this is the C++ board, but this program doesn't really need any C++ features). It emphasises the fact that no new arrays are needed as the recursion proceeds.

Using a vector instead of an array would allow you to write the funciton it without having to pass the size of the array (the number of columns) as an argument, but the basic operation would be the same. Note that if you did it with a vector in C++, the vector parameters would be declared as "pass by reference", since the method I showed uses the original array (vector) throughout, and does not allocate more array (vector) storage within the function.

Now, the implementation is simple but the function merely prints all of the required combinations. Your application, if I understand it, would need a function that, once initialized, would supply a new combination every time it is called. Recursive procedures like this are not particularly well suited for this application.

I am trying to envision ways to set it up with some static variables that would allow the function to generate a single combination and then "keep its place" so that the next call would supply the next. I can think of a thing or two that might be worth pursuing, but I think that for purposes of testing, I would simply compile this program as a standalone process (with user inputs that would allow the program to get the value of N and then N values for the array (vector) that I call "col".

Then in a program that needed the output from this program, I would open a pipe and use fgets to read a line at a time from the combinations program. Then I would use sscanf() or some such thing to get the values into the main program to use as it needs.

CPP / C++ / C Code:
/* 
 * Just to show how to get output from
 * the "combinations program" into another
 * program.
 *
 * Actual command line to combinations would
 * include "N" and the vector or array of
 * column counts, or something like that
 */
#include <stdio.h>
#include <stdlib.h>

int main()
{
    FILE *comb;
    char inbuf[BUFSIZ];
    char *pipename = "./combinations";
    int combno = 0;

    comb = popen(pipename, "r");
    if (popen == NULL) {
        printf("Can't open pipe %s\n", pipename);
        exit(1);
    }
    while (fgets(inbuf, sizeof(inbuf), comb)) {
        printf("%2d: %s", ++combno, inbuf);
    }
    pclose(comb);
    return 0;
}


/* 
 * shows values for everything to the right of "place"
 *
 * "place" is the current column. 
 *
 * Go through the range of the current column and for each
 * value, show everything to the right by calling showvals
 * recursively, using "place + 1"
 *
 * When "place" is the rightmost column, go through the
 * range of that column, using everything to the left of it
 *
 */
void showvals(int *vals, int *col, int place, int size)
{
    int i;
    int j;
    if (place == size-1) {
        for (j = 1; j <= col[size-1]; j++) {
            for (i = 0; i < size-1; i++) {
                printf("%d ", vals[i]);
            }
            printf("%d\n", j);
        }
        return;
    }
    else {
        for(vals[place] = 1; vals[place] <= col[place]; vals[place]++) {
            showvals(vals, col, place+1, size);
        }
    }

}
Output using the previous "combinations" program:

Code:
1: 1 1 1 1 2: 1 1 1 2 3: 1 1 1 3 4: 1 1 2 1 5: 1 1 2 2 6: 1 1 2 3 7: 1 2 1 1 8: 1 2 1 2 9: 1 2 1 3 10: 1 2 2 1 11: 1 2 2 2 12: 1 2 2 3

Note that I make no claims of optimality or even of suitability. It's a learning process, and I start with methods that occur to me and that are easy to implement and easy to test, There are, perhaps, many more elegant (or in some cases, less elegant but more efficient) methods. Some may even be "prettier", but ya gotta start somewhere, and this is where I choose to start. Your Mileage May Vary.

I would be very interested to see examples and elaborations of alternatives.

Regards,

Dave
Last edited by davekw7x : 13-Oct-2006 at 23:02.
  #6  
Old 14-Oct-2006, 11:31
davekw7x davekw7x is offline
Outstanding Member
 
Join Date: Feb 2004
Location: Left Coast, USA
Posts: 4,703
davekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to behold

Re: combinations


My previous response showed a complicated (but correct, I claim) way to generate the combinations with loops. It required a separate nested loop for each column of the vector. You can't do that in C directly, so I showed an approach that used a code generator function to actually write a C program that had the proper number of loops properly structured to do the deed.

Seeing that approach made me think immediately of an approach using recursion (something that I usually avoid ---primarily for reasons of documentation and maintenance--- unless there is a clear advantage).


Here, I present a non-recursive approach that is shorter and simpler than the original one, and can be implemented directly in C or C++, since it only uses two loops, and I will hide one of the loops in a function.

The idea is to create a "word" (a vector or array) with as many elements as you have columns.

Start with values of the elements equal to 1. That's the first word.
To create the next word, then increment the word as follows:
Code:
make a loop that starts with a column number equal to the index of the rightmost element and decrements the column number each time increment the element in the current column if the element is too large for that column then set the element to 1 and continue with the loop else since there wasn't a "carry", we are through end if end loop

Here's an example, along the lines of my previous ones:

CPP / C++ / C Code:
#include <stdio.h>
#include <stdlib.h>

int main()
{
    void printvec(int *, int);
    void incvec(int *val, int *col, int n);
    int i;
    int *val;
    int number_of_words;

    /*~~~~~~~~user input stuff here~~~~~*/
#define N 4
    int column[N] = {1, 2, 2, 3};
    /*~~~~~~end of user input~~~~~~~~*/

    val = malloc(N * sizeof(int));
    if (column == NULL) {
        printf("There was a problem allocating %d bytes for column\n",
                N * sizeof(int));
        exit(1);
    }

    /* 
     * Calculate the number of "words" that we will generate
     * it's simply equal to the product of the number of values that
     * the "digits" in each column can take on
     */
    number_of_words = 1;
    for (i = 0; i < N; i++) {
        number_of_words *= column[i];
    }

    /*
     * Initialize the word to all 1
     */
    for (i = 0; i < N; i++) {
        val[i] = 1;
    }

    printf("Number of words that will be generated = %d\n", number_of_words);


    for (i = 0; i < number_of_words; i++) {
        printvec(val, N);
        incvec(val, column, N);
    }
    free(val);

    return 0;
}

void printvec(int *x, int n)
{
    int i;
    for (i = 0; i < n; i++) {
        printf("%d ", x[i]);
    }
    printf("\n");
}

/* 
 * Treat the individual elements as digits
 * Start at the rightmost.
 * If incrementing it makes it too large, then
 * set it back to its lowest value and
 * continue through the loop to the next one
 * to the left.
 * If incrementing a digit does not make it too
 * large, then we are through
 */
void incvec(int *val, int *col, int n)
{
    int i;
    for (i = n-1; i >= 0; i--) {
        if (++val[i] > col[i]) {
            val[i] = 1;
        }
        else {
            break;
        }
    }
}

Output:

Code:
Number of words that will be generated = 12 1 1 1 1 1 1 1 2 1 1 1 3 1 1 2 1 1 1 2 2 1 1 2 3 1 2 1 1 1 2 1 2 1 2 1 3 1 2 2 1 1 2 2 2 1 2 2 3

Regards,

Dave
 
 

Recent GIDBlogMeeting the local Iraqis by crystalattice

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
combinations of elements on vectors cpit C++ Forum 2 28-Sep-2006 04:36
How to interpret characters as they are being entered? nkhambal C Programming Language 18 14-Feb-2006 10:41
paging file control cpit C++ Forum 10 03-Feb-2006 13:18
[c++] number combinations balusss C++ Forum 8 11-Dec-2005 22:41
String Combination Algorithm yevi C++ Forum 2 13-Jan-2005 08:02

Network Sites: GIDNetwork · GIDWebHosts · GIDSearch · Learning Journal by J de Silva, The

All times are GMT -6. The time now is 22:40.


vBulletin, Copyright © 2000 - 2008, Jelsoft Enterprises Ltd.