一尘不染

查找给定数字集的所有组合

algorithm

说我有一组数字‘0’,‘1’,‘2’,…,‘9’。我想查找所有包含正好包含集合中每个数字之一的数字。

问题是:在开始程序之前,我不知道我的集合将包含多少个数字和哪些数字。(例如,该集合可以包含数字“ 1”,“ 3”和“ 14”。)

我在互联网上搜索,偶然发现了“动态编程”一词,这显然可以用来解决像我这样的问题,但我不理解这些示例。

有人可以给我一个关于如何解决这个问题的提示(可能是动态编程)吗?

编辑:当集合中包含“ 14”之类的数字时,当然必须通过某种方式将集合中的不同数字分开,例如,当集合中包含数字“ 1”,“ 3”和“
14”时,组合可以类似于1-3-14或3-14-1(=各个数字之间用“-”字符分隔)。


阅读 248

收藏
2020-07-28

共1个答案

一尘不染

要检查所有组合而又不事先知道输出必须有多少个数字,我曾经编写以下代码:

#include <stdio.h>
#include <stdlib.h>

#define ARRSIZE(arr)    (sizeof(arr)/sizeof(*(arr)))

int main()
{
    const char values[]= {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
    char * buffer=NULL;
    int * stack=NULL;
    int combinationLength=-1;
    int valuesNumber=-1;
    int curPos=0;
    fprintf(stderr, "%s", "Length of a combination: ");
    if(scanf("%d", &combinationLength)!=1 || combinationLength<1)
    {
        fputs("Invalid value.\n",stderr);
        return 1;
    }
    fprintf(stderr, "%s (%lu max): ", "Possible digit values",(long unsigned)ARRSIZE(values));
    if(scanf("%d", &valuesNumber)!=1 || valuesNumber<1 || (size_t)valuesNumber>ARRSIZE(values))
    {
        fputs("Invalid value.\n", stderr);
        return 1;
    }
    buffer=(char *)malloc(combinationLength);
    stack=(int *)malloc(combinationLength*sizeof(*stack));
    if(buffer==NULL || stack==NULL)
    {
        fputs("Cannot allocate memory.\n", stderr);
        free(buffer);
        free(stack);
        return 2;
    }
    /* Combinations generator */
    for(;;)
    {
        /* If we reached the last digit symbol... */
        if(stack[curPos]==valuesNumber)
        {
            /* ...get back to the previous position, if we finished exit */
            if(--curPos==-1)
                break;
            /* Repeat this check */
            continue;
        }
        buffer[curPos]=values[stack[curPos]];
        /* If we are in the most inner fake-cycle write the combination */
        if(curPos==combinationLength-1)
            puts(buffer);
        stack[curPos]++;
        /* If we aren't on the last position, start working on the next one */
        if(curPos<combinationLength-1)
        {
            curPos++;
            stack[curPos]=0;
        }
    }
    /* Cleanup */
    free(buffer);
    free(stack);
    return 0;    
}

它只是在一个周期内完成所有操作,以避免递归和函数调用的开销,即使使用堆栈数组“伪造”了所需的嵌套嵌套循环也是如此。
它的性能相当好,在我4岁的Athlon64 3800+上,它需要2‘4“的用户时间(=>实际计算时间)才能生成36 ^ 6 =2176782336组合,因此每秒计算大约1750万组合。

matteo@teoubuntu:~/cpp$ gcc -Wall -Wextra -ansi -pedantic -O3 combinations.c -o combinations.x
matteo@teoubuntu:~/cpp$ time ./combinations.x > /media/Dati/combinations.txt
Length of a combination: 6
Possible digit values (36 max): 36

real    13m6.685s
user    2m3.900s
sys 0m53.930s
matteo@teoubuntu:~/cpp$ head /media/Dati/combinations.txt
000000
000001
000002
000003
000004
000005
000006
000007
000008
000009
matteo@teoubuntu:~/cpp$ tail /media/Dati/combinations.txt
zzzzzq
zzzzzr
zzzzzs
zzzzzt
zzzzzu
zzzzzv
zzzzzw
zzzzzx
zzzzzy
zzzzzz
matteo@teoubuntu:~/cpp$ ls -lh /media/Dati/combinations.txt 
-rwxrwxrwx 1 root root 15G 2010-01-02 14:16 /media/Dati/combinations.txt
matteo@teoubuntu:~/cpp$

“实时”时间非常长,因为与此同时我也在PC上做其他事情。

2020-07-28