Generate all tuples with C - better way than nested loops?

南笙酒味 提交于 2019-12-11 01:33:37

问题


I have an array double x[] of length 11 and a function f(double x[]). I want to find the minimum of the function f() by discretization. So for given values val1, val2, ..., valn I need a loop trough all the tuples of x in {val_1, ..., val_n}^11. I could easily use 11 nested loops, but is this really the most efficient I could do?

Edit: To clarify things: the function f() is defined on an 11 dimensional set. I want to evaluate the function on the vertices of the an 11 dimensional grid. For a grid size h, possible values for the entries of the array x[] could be 0, h, 2*h, ..., n*h = val_1, val_2, ...,val_n . So in the beginning f(val_1, val_1, ..., val_1) should be evaluated, then f(val_1, val_1, ...,val_1, val_2), ... and in the and f(val_n, val_n, ..., val_n). I don't care about the order actually, but I do care about speed, because there are many such tuples. To be precise, there are n^11 such tuples. So for n=10 f() has to evaluated 10^11 times. My computer can evaluate f() approximately 5*10^6 times per second, so for n=10 the evaluation of f() takes 5 hours. That's why I'm searching for the most efficient way to implement it.


回答1:


Here is pseudocode (not necessarily syntactically-correct C code) for a non-recursive approach to iterate over all possible tuples:

const int vals[]        = { val1, val2, val3, ... };

int       x[NUM_DIMS]   = { vals[0], vals[0], ... };  // The current tuple
int       i[NUM_DIMS]   = { 0 };                      // Index of each element in x[]


while (1)
{
    f(x);  // Whatever

    // Update x (we increment i[] treated as a base-N number)
    int j;
    for (j = 0; j < NUM_DIMS; j++)
    {
        i[j]++;                          // Increment the current digit
        x[j] = vals[i[j]];               // Update (via mapping)
        if (i[j] < NUM_VALS) { break; }  // Has this digit wrapped?
        // We've caused a carry to the next digit position
        i[j] = 0;                        // Wrap
        x[j] = vals[0];                  // Update (via mapping)
    }
    if (j == NUM_DIMS) { break; }  // All digits wrapped, therefore game over
}



回答2:


The thing to do when the number of loops that you need is too high or is not known at compile time is to use recursion.

void check(double *x, int count) {
     // Check the tuple
}
void process(double *x, double *tuple, int count, int pos) {
    if (pos == count) {
        check(tuple, count);
    } else {
        for (int i = 0 ; i != count ; i++) {
            tuple[pos] = x[i];
            process(x, tuple, count, pos+1);
        }
    }
}
int main() {
    double x[11] = {1,2,3...}, tuple[11];
    process(x, tuple, 11, 0);        
    return 0;
}



回答3:


You might want to reduce pressure on the processor cache. So perhaps if N in val_N is small, and you represent it by N instead of N*h as @OliCharlesworth suggests, you may be able to use a smaller type (e.g. unsigned char).

Also, the loop could be reduced to:

static inline int incx(uint8_t *x, unsigned int len) {
    ++*x;

    // compute any overflow
    unsigned int i = 0;
    while (x[i] >= len && i < len) {
        x[i++] -= len;
        ++x[i];
    }

    // if the last value overflows, we're done
    return (i < len);
}

uint8_t x[LEN];

memset(x, 0, sizeof(x));

while (incx(x, sizeof(x)))
    f(x);


来源:https://stackoverflow.com/questions/8804852/generate-all-tuples-with-c-better-way-than-nested-loops

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!