我需要一个干净的c代码来找出数字的组合。 任意数量的数字和任何大小的组合。 例如{1,2,3} 输出应为{1,2,3,12,13,23,123},注23和32相同。 那有没有干净的c程序?
致以最诚挚的问候,
答案 0 :(得分:2)
有很多方法可以做到这一点。这是一种使用位操作的方法。
让给定的集合 a 。
这个代码非常小。理解以下内容,您将了解这一小段代码的工作原理。
你必须要意识到的第一件事是你找到给定集合的(2 n - 1)子集。
任何集都有 2 n 子集,在这里,您已经排除了 null 集。因此(2 n - 1)
现在,要生成这些子集,我们需要一个算法。
请注意以下事项:
001 --- 1
010 --- 2
011 --- 3
100 --- 4
101 --- 5
110 --- 6
111 --- 7
左数字构成右十进制数的二进制表示。
如果我们写出4位数的二进制数,那么就会有15种组合。请注意,我在上面的例子中排除了所有数字为零的组合。
通常,对于 n位二进制数,有(2 n - 1)不同的数字组合。我们可以用它来以非常简单的方式生成子集。
对于集合中的每个元素,您都可以:
(因此,有(2 n - 1)子集)
现在,我说要做以下事情:
for i in [1,2^n - 1]:
Let b = binary representation of i.
for every jth bit in b:
if the jth bit is set:
print a[j]
print a newline character.
这是C代码:
// include your headers
int isJthBitSet(int i,int j)
{
// returns 1 if jth bit is set in the binary representation of i.
return (i & (1 << j));
}
int main()
{
int n = 3; // The size of the set for example.
int a[] = {1,2,3}; // the given set, for example.
for(int i = 1; i < (1 << n); i++) // i is from 1...2^n - 1
{
for(int j = 0; j < n; j++) // for every jth bit in the n-bit representation of i
{
if(isJthBitSet(i,j)) // if the bit is set
printf("%d ", a[j]); // print the corresponding element
}
printf("\n");
}
return 0;
}
这就是它。
答案 1 :(得分:1)
虽然我通常不满足于展示完整的解决方案,但基于一些相对较新的类似问题及其答案,似乎有必要举例说明如何解决这些类型的组合问题。
使用k
元素中的n
元素构造所有唯一集合的简单方法是使用k
嵌套循环,其中循环索引始终按递增顺序排列。例如,要从一组N
个字符打印所有唯一的3个字符三元组,您可以使用
const char all[N] = ...;
char set[4];
size_t i, j, k;
set[3] = '\0'; /* End of string mark */
for (i = 0; i < N-2; i++) {
set[0] = all[i];
for (j = i+1; j < N-1; j++) {
set[1] = all[j];
for (k = j+1; k < N; k++) {
set[2] = all[k];
puts(set);
}
}
}
现在,OP希望所有具有最多 k
元素的唯一子集来自一组n
元素,这意味着我们需要不能使用上面的嵌套循环(因为我们不知道最大k
)。无论如何,明确地说。相反,我们需要考虑如何重写它。
为了更好地掌握结构,让我们看一下五分之三的情况。十个结果集是
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
有明确的顺序和清晰的逻辑:增加最右边,除非它会变得太大。然后,找到左边的下一个索引,我们可以在不继续的情况下递增(为右侧的那些保留足够的元素)。如果我们不能在没有经过的情况下增加最左边,我们已经生成了所有集合。 (如果你考虑一下,这也是可变嵌套循环的直接实现。)递增后,按升序将元素设置为右边。
在大多数情况下,我们希望某种结构或对象能够跟踪状态和当前子集,并具有初始化,释放和切换到下一个子集的功能。这是一种可能性:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
typedef struct {
char *buffer; /* Symbol buffer */
size_t length; /* Number of symbols to choose */
size_t *index; /* Index of each symbol */
char *symbol; /* Array of symbols */
size_t symbols; /* Number of symbols to choose from */
} generator;
void generator_free(generator *const g)
{
if (g) {
free(g->buffer);
free(g->index);
free(g->symbol);
g->buffer = NULL;
g->length = 0;
g->index = NULL;
g->symbol = NULL;
g->symbols = 0;
}
}
const char *generator_current(generator *const g, const char *const none)
{
return (g && g->buffer) ? g->buffer : none;
}
int generator_init(generator *const g, const char *const symbol, const size_t choose)
{
const size_t symbols = (symbol) ? strlen(symbol) : 0;
size_t i;
if (!g || symbols < 1 || choose < 1 || choose > symbols)
return EINVAL;
g->buffer = malloc(choose + 1);
g->index = malloc((choose + 1) * sizeof g->index[0]);
g->symbol = malloc(symbols + 1);
if (!g->buffer || !g->index || !g->symbol) {
free(g->buffer);
free(g->index);
free(g->symbol);
g->buffer = NULL;
g->length = 0;
g->index = NULL;
g->symbol = NULL;
g->symbols = 0;
return ENOMEM;
}
memcpy(g->buffer, symbol, choose);
g->buffer[choose] = '\0';
g->length = choose;
for (i = 0; i < choose; i++)
g->index[i] = i;
g->index[choose] = symbols;
memcpy(g->symbol, symbol, symbols);
g->symbol[symbols] = '\0';
g->symbols = symbols;
return 0;
}
int generator_next(generator *const g)
{
size_t i;
if (!g || !g->buffer || g->length < 1 || !g->index)
return EINVAL;
if (g->index[0] >= g->symbols - g->length)
return ENOENT;
if (++g->index[g->length - 1] >= g->symbols) {
i = g->length - 1;
while (i > 0 && g->index[i] + 1 >= g->symbols - i)
i--;
g->index[i]++;
if (!i && g->index[0] > g->symbols - g->length) {
memset(g->buffer, '\0', g->length + 1);
return ENOENT;
}
while (i++ < g->length)
g->index[i] = g->index[i-1] + 1;
}
for (i = 0; i < g->length; i++)
g->buffer[i] = g->symbol[g->index[i]];
g->buffer[g->length] = '\0';
return 0;
}
generator_current()
提供当前集(作为字符串)。当没有有效集时,它将返回您指定的字符串作为第二个参数,而不是返回NULL
。 (这只是为了方便,这背后没有真正的原因。)
generator_free()
丢弃生成器,generator_init()
初始化一个新生成器,generator_next()
将生成器推进到下一个子集。
注意generator_init()
也初始化第一个子集;连续选择元素的那个。 (虽然->symbol
只是一个包含整个集合中所有字符的字符数组,但该函数会附加一个字符串结尾标记,因此您也可以将其视为字符串。)
generator_next()
中的第一个if子句确保生成器初始化;这只是一个健全检查。第二个检查发电机是否完整。
generator_next()
中的第三个if子句递增最右边的索引,更改子集中的最后一个元素。如果它用完了有效元素,则while循环将搜索索引的i
索引,该索引可以在不耗尽元素的情况下递增。请注意,由于索引按升序排列(确保了唯一的子集),因此必须记住要考虑其余位置所需的元素。
如果i
变为零并且溢出,则不再有子集,并且->buffer
成员被清除为空字符串(以防万一)。
否则,第二个while循环使用连续值填充i
右侧的索引。 (参见上面五个三分之一的示例,第一个元素从1变为2的情况,以说明为什么需要这样做。)
最后,for循环用于根据索引将->symbol
数组中的元素复制到->buffer
。
对于askers情况,子集的大小会有所不同,因此要生成所有子集,需要循环。例如:
generator g;
size_t i;
for (i = 1; i <= 2; i++) {
if (generator_init(&g, "123", i)) {
fprintf(stderr, "generator_init() failed!\n");
exit(EXIT_FAILURE);
}
do {
/* Print the set and a newline */
puts(generator_current(&g, ""));
} while (!generator_next(&g));
generator_free(&g);
}
为了进行测试,我使用了以下辅助函数和main()
:
int parse_size(const char *s, size_t *const dst)
{
const char *endptr = NULL;
unsigned long value;
size_t result;
int skip = -1;
if (!s || !*s)
return EINVAL;
errno = 0;
value = strtoul(s, (char **)&endptr, 0);
if (errno)
return errno;
if (!endptr || endptr == s)
return EEXIST;
(void)sscanf(endptr, " %n", &skip);
if (skip > 0)
endptr += skip;
if (*endptr)
return EEXIST;
result = (size_t)value;
if ((unsigned long)result != value)
return EDOM;
if (dst)
*dst = result;
return 0;
}
int main(int argc, char *argv[])
{
generator g;
size_t symbols, length, len;
if (argc != 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s DIGITS LENGTH\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "This will print each unique set of LENGTH characters from DIGITS,\n");
fprintf(stderr, "one set per line.\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
symbols = (argv[1]) ? strlen(argv[1]) : 0;
if (symbols < 1) {
fprintf(stderr, "No DIGITS specified.\n");
return EXIT_FAILURE;
}
if (parse_size(argv[2], &length) || length < 1 || length > symbols) {
fprintf(stderr, "%s: Invalid LENGTH.\n", argv[2]);
return EXIT_FAILURE;
}
for (len = 1; len <= length; len++) {
if (generator_init(&g, argv[1], len)) {
fprintf(stderr, "Generator initialization failed.\n");
return EXIT_FAILURE;
}
do {
puts(generator_current(&g, ""));
} while (!generator_next(&g));
generator_free(&g);
}
return EXIT_SUCCESS;
}
在Linux中,我更喜欢使用gcc -Wall -Wextra -ansi -pedantic -O2 main.c -o example
编译上述内容。最初的问题要求
./example 123 2
输出
1
2
3
12
13
23
更大的例子更有趣。例如,
./example 12345 3
列出前五位数字中的所有一位,两位和三位数字。输出是
1
2
3
4
5
12
13
14
15
23
24
25
34
35
45
123
124
125
134
135
145
234
235
245
345
有问题吗?