使用C生成所有元组 - 比嵌套循环更好的方法?

时间:2012-01-10 14:30:14

标签: c loops nested-loops

我有一个长度为11的数组double x[]和一个函数f(double x[])。我希望通过离散化找到函数f()的最小值。因此,对于给定的值val1, val2, ..., valn,我需要在{val_1,...,val_n} ^ 11中通过x的所有元组进行循环。我可以很容易地使用11个嵌套循环,但这真的是我能做的最有效吗?

修改 澄清事情:函数f()在11维集上定义。我想评估一个11维网格的顶点上的函数。对于网格大小h,数组x[]的条目的可能值可以是0h2*h,...,{{1 } = val_1,val_2,...,val_n。因此,应该首先评估n*h,然后评估f(val_1, val_1, ..., val_1),...和f(val_1, val_1, ...,val_1, val_2)。我实际上并不关心顺序,但我确实关心速度,因为有很多这样的元组。确切地说,有n ^ 11个这样的元组。因此,对于n = 10,f(val_n, val_n, ..., val_n)必须评估10 ^ 11次。我的计算机每秒可以评估f()大约5 * 10 ^ 6次,因此对于n = 10,f()的评估需要5个小时。这就是为什么我在寻找最有效的方法来实现它。

3 个答案:

答案 0 :(得分:6)

这是伪代码(不一定是语法正确的C代码),用于迭代所有可能元组的非递归方法:

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
}

答案 1 :(得分:3)

当你需要的循环数太高或在编译时不知道时要做的事情是使用递归。

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;
}

答案 2 :(得分:1)

您可能希望减轻处理器缓存的压力。因此,如果N中的val_N很小,并且您按照@ {OliCharlesworth建议的N而不是N*h来表示它,那么您可以使用较小的类型(例如unsigned char)。

此外,循环可以简化为:

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);