问题
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