没有简单的方法,但您可以实现一个类似迭代器的类型,在 n 给定范围内生成笛卡尔积:
enum {
MAX = 8
};
typedef struct Combinator Combinator;
struct Combinator {
size_t index; // running index of generated numbers
size_t n; // number of dimensions
int data[MAX]; // current combination
int start[MAX]; // lower and ...
int end[MAX]; // .. exclusive upper limits
};
/*
* Adds a dimensin with valid range [start, end) to the combinator
*/
void combo_add(Combinator *c, int start, int end)
{
if (c->n < MAX && start < end) {
c->data[c->n] = start;
c->start[c->n] = start;
c->end[c->n] = end;
c->n++;
}
}
/*
* Reset the combinator to the lower limits
*/
void combo_reset(Combinator *c)
{
c->index = 0;
memcpy(c->data, c->start, sizeof(c->start));
}
/*
* Get the next comnination in c->data. Returns 1 if there
* is a next combination, 0 otherwise.
*/
int combo_next(Combinator *c)
{
size_t i = 0;
if (c->index++ == 0) return 1;
do {
c->data[i]++;
if(c->data[i] < c->end[i]) return 1;
c->data[i] = c->start[i];
i++;
} while (i < c->n);
return 0;
}
这实现了一个类似里程表的计数器:它增加第一个计数器。如果它溢出,它会重置它并增加下一个计数器,根据需要移动到下一个计数器。如果最后一个计数器溢出,则停止生成组合。 (第一个组合的索引有点混乱,因此您可以从while 循环的循环条件中控制所有内容。可能有一种更优雅的方法来解决这个问题。)
像这样使用组合器:
Combinator combo = {0}; // Must initialize with zero
combo_add(&combo, 0, 3);
combo_add(&combo, 10, 12);
combo_add(&combo, 4, 7);
while (combo_next(&combo)) {
printf("%4zu: [%d, %d, %d]\n", combo.index,
combo.data[0], combo.data[1], combo.data[2]);
}
这个组合器设计为只使用一次:创建它,设置范围,然后用尽组合。
如果您将break 退出循环,则组合器将保留其状态,以便进一步调用combo_next 在您中断的地方继续。您可以致电combo_reset 重新开始。 (这有点像从文件中读取:通常使用它们的方法是读取所有内容,但您可以rewind。)