【问题标题】:Unique Combinations of Digits in CC语言中独特的数字组合
【发布时间】:2015-02-23 21:09:06
【问题描述】:

我需要用 C 语言设计一个算法来计算 0 到 1,000,000 的唯一数字组合。例如,当 13 出现时,31 将不会包含在此序列中。谁能帮我找到一个算法来描述这个?该系列的前几个数字是:

 1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19,22,23,24,25,26,27,28,29,33,etc

谢谢!

编辑 - 抱歉,忘了说不包括零

【问题讨论】:

  • 这个系列的前几个数字是,0,1,2,3,4,5,6,7,8,9,11,12,13,14,15,16, 17、18、19、22、23、24等
  • 你应该解释一下为什么 10 没有出现在列表中。
  • 10 也不在该系列中。是否假定与01 相同?前面有隐式零的 6 位数字的组合应该只在列表中出现一次?
  • 我猜一个数字在列表中,如果它的所有数字都按递增顺序排列。
  • @DanielKleinstein 132 不应该在列表中,因为 123 已经在列表中

标签: c algorithm digits


【解决方案1】:
#include <stdio.h>
int main(void) {
    int i, n;
    for (n = 1; n < 1000000; n++) {
        for (i = n;;) {
            if (i / 10 % 10 > i % 10) break;
            if ((i /= 10) == 0) { printf("%d\n", n); break; }
        }
    }
}

从 0 到 1000000 的系列中的 5004 个数字

更快的版本:

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

static long long enumerate(char *p, int i, int n, int digit, int silent) {
    long long count = 0;
    if (i >= n) {
        if (!silent) printf("%s\n", p);
        return 1;
    }
    for (p[i] = digit; p[i] <= '9'; p[i]++)
        count += enumerate(p, i + 1, n, p[i], silent);
    return count;
}

int main(int argc, char **argv) {
    char array[256];
    int i, n;
    int max = (argc > 1) ? strtol(argv[1], NULL, 0) : 6;
    int silent = 0;
    long long count = 0;
    if (max < 0) {
        max = -max;
        silent = 1;
    }
    array[sizeof(array)-1] = '\0';
    for (n = 1; n <= max; n++) {
        count += enumerate(array + sizeof(array) - 1 - n, 0, n, '1', silent);
        if (silent)
            printf("%lld combinations between 0 and 1E%d\n", count, n);
    }
}

使用正数调用以枚举组合,使用负数调用以仅计算它们。

【讨论】:

  • 24309 组合达到 1 亿,48619 组合低于 10 亿。系列长度似乎像 log(N) 一样增长。找到 N=10^n 的解析公式是你的新任务!
  • 我知道我应该做你的功课,但我无法抗拒。我测试 1 和上限之间的每个数字。我检查最后一个数字是否至少与前一个数字一样大,并通过将数字除以 10 直到它为 0 对前一对再次执行此操作。如果测试达到 0,则该数字正常并被打印。
  • 这种方法又快又脏。它对于 1000000 来说足够快,但对于更高的界限变得相当慢。找到一个更快的方法,它包含一个字符数组,可以在线性时间内找到所有组合。
  • 它的运行速度有多快?对我来说它已经运行了几分钟......你确定你的内部循环中不应该有停止条件吗?我认为你应该保留i &gt; 0
  • 我的错!测试i &gt; 0 是多余的,但我忘记了break;
【解决方案2】:

函数next 将数组a 更新为下一个数字,返回底部数字的值。 main 函数遍历序列,在最高位为 10 时停止(因为一旦数组用完,next 就一直递增最高位)。

算法,用文字和忽略边界检查,可以描述为“找到下一个数字,在底部数字上加一个,如果溢出找到下一个数字忽略底部数字,然后复制新的底部数字。”

#include <stdio.h>

int next(int *a, size_t len) {
    if (*a == 9 && len > 1) {
        *a = next(a-1, len-1);
    } else {
        *a += 1;
    }
    return *a;
}

#define N 6

int main(int argc, char *argv[]) {
    int a[N] = {0};
    while (next(a+N-1, N) != 10) {
        for (int i = 0; i < N; i++) {
            if (a[i] != 0) printf("%d", a[i]);
        }
        printf("\n");
    }
    return 0;
}

您可以在 O(N) 时间内计算解决方案(其中 N 是位数)。如果 K(n, d) 是正好 n 位的解的数量,并且其最高位是 9-d,则 K(0, d) = 1,并且 K(n+1, d) = K(n, 0) + K(n, 1) + ... + K(n, d)。具有 n 位或更少位数的解的数量为 K(1, 8) + K(2, 8) + ... + K(n, 8)。这些观察产生了这种动态规划解决方案:

int count(int n) {
    int r[9] = {1};
    int t = 0;
    for (int i = 0; i < n+1; i++) {
        for (int j = 1; j < 9; j++) {
            r[j] += r[j-1];
        }
        t += r[8];
    }
    return t - 1;
}

int main(int argc, char* argv[]) {
    printf("there are %d numbers.\n", count(6));
    return 0;
}

给予:

there are 5004 numbers.

【讨论】:

  • 相当优雅! 4263421511270 1 和 1 googol 之间的组合。我不得不在count 中使用unsigned long long,但它非常快。你统治!
猜你喜欢
  • 1970-01-01
  • 2011-11-16
  • 2021-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多