0

The question is. There is a set An, and it consists of integers from 1 to n.

An={1, 2, 3, ..., n}

Print all subsets of An with given size K. And it must be generated in order like the example below.

So for example, n=5 k=3

{1, 2, 3} {1, 2, 4} {1, 2, 5} {1, 3, 4} {1, 3, 5} {1, 4, 5} {2, 3, 4} ... {3, 4, 5}

I am not sure about if there is other way not using recursion. I did this with recursion but the problem is all test cases should be done within 1 sec. When N and K are like 5, 3 and 12, 6 it is okay but When it comes to like 50, 48 or 100, 95, it takes too long. All problem should be done within 1 second. I am having real struggle with this problem.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void subset(int n, int k, int arr[], int pos[], int index, int start){
    int i, j;

    if(index == k){
        for(j=0; j<k; j++){
            if(j==k-1)
                printf("%d\n", pos[j]);
            else{
                printf("%d ", pos[j]);
            }
        }
        return;
    }

    for(i=start; i<n; i++){
        pos[index] = arr[i]; 
        subset(n, k, arr, pos, index+1, i+1);
    }
}

int main(){
    int n, k, arr[100], index=0, start=0;

    scanf("%d %d", &n, &k);

    // 1<=n<=100 ; 1<=k<=n
    if(n>100||n<1 && k>n||k<1)
        return 0;

    int i;
    for(i=0; i<n; i++)
        arr[i]=i+1;

    int *pos = (int*)malloc(sizeof(int)*k);

    time_t clockstart=0, clockend=0;
    float gap;
    clockstart = clock();
    subset(n, k, arr, pos, index, start);
    clockend = clock();
    gap = (float)(clockend-clockstart)/(CLOCKS_PER_SEC);

    printf("%f\n", gap);

    return 0;
}

I think i should use something like tail recursion or vector in C++. But I cant figure out with those.

Roy Yoon
  • 9
  • 3
  • `it should be fast`..compared to?? – Sourav Ghosh Mar 28 '18 at 07:31
  • 5
    Finding all the subsets will take 2^n iterations, whether you like it or not. That's how it is. – Ashutosh Mar 28 '18 at 07:35
  • Not sure what "Print subsets N of given set K" actually means. But in general there are a lot of subsets, so this isn't going to be fast for larger sets. – Henry Mar 28 '18 at 07:35
  • Maybe the sets are unordered, and you're only supposed to print unique combinations…? – nemequ Mar 28 '18 at 07:37
  • Well, you could remove the conditional from the loop, and simply print the newline after the loop! – Weather Vane Mar 28 '18 at 07:40
  • No, because how do you know if you have to printf a space or not ? It true that visually, "1 2 3\n" and "1 2 3 \n" is the same, but sometime you have to follow a hard format (because it will be used by other program) and the extra space can't be tolerated. – Tom's Mar 28 '18 at 08:19
  • And all have to be generated in 1 second. I will attach whole code – Roy Yoon Mar 28 '18 at 09:03
  • Are you sure that 5 3 should give "1 2" as a solution ? What "3" stand for ? Because for me, it seem that the solution should have "3" element, not only 2 like in "1 2". – Tom's Mar 28 '18 at 09:07
  • Maybe I should have written this N means 1~n so An={1, 2, 3, ... n} And K means the size of subset – Roy Yoon Mar 28 '18 at 09:11
  • So when you scan 5 3 It should be 1 2, 1 3, 1 4, 1 5, 2 3, 2 4, 2 5, 3 4, 3 5, 4 5 – Roy Yoon Mar 28 '18 at 09:20
  • If the order of the answer doesn't matter, you could use multi-threading, but be advised that multi-threading performance are hugely linked to the hardware where the program run. Your code could be simplified a little (var arr useless since it "1 to n") but since the recursion is mandatory, I don't see any way of improving your algorithm itself. If the recursion was optionnal, then using array to "simulate" the recursion should be a little quicker (no function call, which come to a cost when used in recursion like that), but not something to pass under the 1 seconde threeshold. – Tom's Mar 28 '18 at 12:01
  • @Tom's Yeah I was thinking that too that simulating the recursive. But I could not find any answer. So if the recursion doesn't matter how should I solve this? I don't really know about multi-threading but the order matters so I don't think that is the way. – Roy Yoon Mar 28 '18 at 12:23
  • And to think of this, it is actually same as searching values of Combination method. Is there any good algorithm with it? – Roy Yoon Mar 28 '18 at 12:25
  • "All problem should be done within 1 second" - Simple solution: get a faster computer. – too honest for this site Mar 28 '18 at 14:51
  • I'm voting to close this question as off-topic because questions about improving *working code* belong on [Code Review](https://codereview.stackexchange.com/help/on-topic). – Gert Arnold Mar 28 '18 at 14:56
  • 1
    @GertArnold It is okay to recommend the OP post on CR but in the future, please don't use Code Review as a reason to close a question. Evaluate the request and use a reason like *too broad*, *primarily opinion-based*, etc. Then you can mention to the OP that it can be posted on Code Review if it is [on-topic](https://codereview.stackexchange.com/help/on-topic). Please see the section **What you should not do** in [this answer to _A guide to Code Review for Stack Overflow users_](https://codereview.meta.stackexchange.com/a/5778/120114) – Sᴀᴍ Onᴇᴌᴀ Mar 28 '18 at 15:29
  • @SamOnela I only do this when the question in its current form would fit on CR, not when it would be off-topic there to begin with, but OK, noted. – Gert Arnold Mar 28 '18 at 15:33

1 Answers1

1

The only way to increase your "speed algorithm" without touching it is to manually bufferise printf.

printf is a function that will do a system call at some time. Every system call is costly, that's why each function that do some kind of system call usually do "buffering".

For malloc, in reallity, you allocate much more that you think (malloc(1) will not end up by allocating 1 octet but much more under the hood) and when you free the memory, in reallity it's not really released (that way, if you do another malloc, you will not do a system call but reuse the memory freed). Of course, it's OS dependant AND inmplementation dependend (all is under the hood). You can see some system call under linux by using "strace".

The same thing apply to "printf" : since it will do a system call, there a buffer that retain what you want to print and do the print only time to time.

So when the printf's buffer is really printed ? We can't know, it's implementation defined (event the man page of printf doesn't say a word about the printf buffering), but usually, it can be at 3 occasion :

  • when the buffer is full
  • when you force the flush by using fflush
  • when you have the '\n' caracter in what you want to print.

Since you do an "\n" at each subnet, printf may have to do the system call every time : it's time consumming.

By using a buffer and print in the buffer instead of stdout, you can speed up your code :

#define BUF_LEN 2048

typedef struct mybuf {
    char   buffer[BUF_LEN];
    size_t len;
} mybuf;

// For convenience, I use global varaible, but it's BAD
mybuf buf = {.buffer = "", .len = 0};

void MyBuf_PrintOnStdout(void)
{
    write(1, buf.buffer, buf.len);
    buf.len = 0;
}

void MyBuf_Flush(void)
{
    MyBuf_PrintOnStdout();
    fflush(stdout);
}

void MyBuf_PrintInteger(int integer)
{
    int printedLen;

    // An 64bit integer take 20digit + 1 char for potential "-"
    // So 21 is the max len for an integer.
    // Of course, if your int is bigger than 64bit, this if is false.
    if (buf.len + 21 >= BUF_LEN) {
        MyBuf_PrintOnStdout();
    }
    printedLen = sprintf(buf.buffer + buf.len, "%d", integer);
    // Error check (printedLen < 0 == error)
    buf.len += printedLen;

}

void MyBuf_PrintCharacter(char character)
{
    if (buf.len + 1 >= BUF_LEN) {
        MyBuf_PrintOnStdout();
    }
    buf.buffer[buf.len] = character;
    ++buf.len;
}

void subset(int n, int k, int arr[], int pos[], int index, int start)
{   
    if (index == k) {
        for (int j = 0; j < k; ++j) {
            MyBuf_PrintInteger(pos[j]);
            MyBuf_PrintCharacter(j == k-1 ? '\n' : ' ');
        }
        return;
    }

    for(int i = start; i<n; i++){
        pos[index] = arr[i]; 
        subset(n, k, arr, pos, index+1, i+1);
    }
}

Don't forget to call "MyBuf_Flush" at the end, because without it you will probably missing some printing.

Edit : With the complet code, I do some testing. While it's true there is improvement (N = 30, k = 20 with your code take ~88s and with the write take ~78s), it really too poor to make that work on less than 1s. Is it possible to resolve your problem without having a supercalculator ?


Another edit : Okay, I have confused the meaning of "should" and "must", sorry. English is not my motherlanguage. (I thinked that you must use recursion).

Since you are free to not use recursion, here something interesting : I've implemented recursion and not recursion of n=30, k=20. For each implementation, I have enable and disabled the printing.

The result are clear :

  • recursion, printing with printf : ~88s
  • recursion, printing buffered : ~78s
  • recursion, no printing : ~7s

--

  • no recursion, printing with printf : ~80s
  • no recursion, printing buffered : ~70s
  • no recursion, no printing : ~0,47s

So in conclusion, it's more the printing part that is really taking time than finding the solution itself.

Here the no recursive implementation :

#define BUF_LEN 4096

typedef struct mybuf {
    char   buffer[BUF_LEN];
    size_t len;
} mybuf;

// For convenience, I use global varaible, but it's BAD
mybuf buf = {.buffer = "", .len = 0};

void MyBuf_PrintOnStdout(void)
{
    /*buf.buffer[buf.len] = '\0';
    printf("%s", buf.buffer);*/
    write(1, buf.buffer, buf.len);
    buf.len = 0;


}

void MyBuf_Flush(void)
{
    MyBuf_PrintOnStdout();
    fflush(stdout);
}

void MyBuf_PrintInteger(int integer)
{
    int printedLen;

    if (buf.len + 21 >= BUF_LEN) {
        MyBuf_PrintOnStdout();
    }
    printedLen = sprintf(buf.buffer + buf.len, "%d", integer);
    // Error check (printedLen < 0 == error)
    buf.len += printedLen;

}

void MyBuf_PrintCharacter(char character)
{
    if (buf.len + 1 >= BUF_LEN) {
        MyBuf_PrintOnStdout();
    }
    buf.buffer[buf.len] = character;
    ++buf.len;
}


void subset_no_recursion(int n, int k)
{
    int pos[k];

    for (int i = 0; i < k; ++i) {
        pos[i] = k - i;
    }

    for (;;) {
        // Last digit incrementation
        while (pos[0] <= n) {
            /* print
            for (int i = k - 1; i >= 0; --i) {
                MyBuf_PrintInteger(pos[i]);
                MyBuf_PrintCharacter(i == 0 ? '\n' : ' ');
            }*/

            ++pos[0];
        }

        // We find where we can increment the digit without overflow N
        int pivot = 1;
        while (pos[pivot] == n - pivot) {
            ++pivot;
        }
        if (pivot == k) {
            return;
        }
        ++pos[pivot];
        while (pivot) {
            pos[pivot - 1] = pos[pivot] + 1;
            --pivot;
        }
    }
}


void subset_recursion(int n, int k, int pos[], int index, int start)
{
    if (index == k) {
        for (int i = 0; i < k; ++i) {
            MyBuf_PrintInteger(pos[i]);
            MyBuf_PrintCharacter(i == k-1 ? '\n' : ' ');
        }
        return;
    }

    for (int i = start; i < n; i++) {
        pos[index] = i + 1;
        subset_recursion(n, k, pos, index + 1, i + 1);
    }
}



#define N 30
#define K 20

int main(void)
{
    int pos[K];
    time_t clockstart;
    time_t clockend;

    clockstart = clock();
    subset3(N, K);
    clockend = clock();


    printf("%f\n", ((float)(clockend - clockstart)) / CLOCKS_PER_SEC);

    return 0;
}
Tom's
  • 2,448
  • 10
  • 22
  • I've edited my answer. – Tom's Mar 28 '18 at 13:43
  • Depending on the system, `printf` is already buffered. – too honest for this site Mar 28 '18 at 14:53
  • Yes, but "\n" force flush the buffer. Did you read my answer ? – Tom's Mar 28 '18 at 14:54
  • 1
    That's implementtion defined, too. Anyway, we don't do code reviews like the one requested. Please refrain from answering off-topic questions. – too honest for this site Mar 28 '18 at 15:08
  • I was surprised by what you say, and I checked the manual of printf and indeed, there are not a word about the buffering or "\n" behavior. So, yikes, my bad. And sorry for the "off-topic" answer, i'm rather new and from my point of view, it's a question that can have an answer. – Tom's Mar 28 '18 at 15:13
  • 1
    "How to grill a steak right to the point" also has an answer. Yet it would be off-topic here, too and should be closed and deleted, not answered. (and >2 years and 1k rep is definitively not "rather new") – too honest for this site Mar 28 '18 at 15:16
  • Well, I don't intend to pollute the comment section, but since you're putting your word this way, I answer : I think there are a rather huge difference between "How to grill a steak to the point" and "How can I make this code faster". Excuse me if I don't see the similarity between them. And also, yes, I'm rather new at answering SO question : you have checked the year of creation, but check when I earned the 1K rep, and you will see that 75% or more is the past month. I don't have a really good level in english, that's why I never really answered before this point. Because sometime, it's hard – Tom's Mar 28 '18 at 15:29
  • for me to understand some subtility ("should" / "must", for exemple) and sometime it's hard to explain with clarity what I mean. – Tom's Mar 28 '18 at 15:30
  • It's as simple as "they are both **obviously** off-topic. And the rest is a non sequitur. If you join a community, you are expected to follow the rules. Try moving to a different country, violate a rule and state "I'm new here". Ignorantia legis non excusat. Anyway, I'll leave it here. (oh, and your English is well enough) – too honest for this site Mar 28 '18 at 15:34
  • 1
    Ever heard about the word "tolerance" ? It obvious for you, and I say it's not obvious for me. When I do something, I try to apply by the rules, but if the rules were binary, then we will have no use of judge, advocat and jurist. And I like to see YOU going somewhere and magically know all the rules and act accordingly to them. Especially a country, since you take that as exemple. – Tom's Mar 28 '18 at 15:38