Algorithm 减去一个数字';从数字中删除s个数字,直到达到0

Algorithm 减去一个数字';从数字中删除s个数字,直到达到0,algorithm,bignum,Algorithm,Bignum,有人能帮我解决这个问题吗 我们有一个大数字(19位),在循环中,我们从数字本身减去该数字的一位数字 我们继续这样做,直到数字达到零。我们要计算使给定数字达到零的最小减法次数 算法必须在两秒钟内快速响应19位数字(10^19)。例如,提供36的输入将给出7: 1. 36 - 6 = 30 2. 30 - 3 = 27 3. 27 - 7 = 20 4. 20 - 2 = 18 5. 18 - 8 = 10 6. 10 - 1 = 9 7. 9 - 9 = 0 谢谢。我想,要达到零的最小减法

有人能帮我解决这个问题吗

我们有一个大数字(19位),在循环中,我们从数字本身减去该数字的一位数字

我们继续这样做,直到数字达到零。我们要计算使给定数字达到零的最小减法次数

算法必须在两秒钟内快速响应19位数字(10^19)。例如,提供
36
的输入将给出
7

1. 36 - 6 = 30
2. 30 - 3 = 27
3. 27 - 7 = 20
4. 20 - 2 = 18
5. 18 - 8 = 10
6. 10 - 1 =  9
7.  9 - 9 =  0
谢谢。

我想,要达到零的最小减法次数使这成为一个非常棘手的问题,这将需要大量回溯潜在的解决方案,这对于您的时间限制来说可能过于昂贵

但你应该做的第一件事是做一次精神检查。由于最大数字是
9
,因此19位数字需要大约
1018
减法才能达到零。编写一个简单的程序,从1019中连续减去9,直到小于10为止。如果你不能在两秒钟内做到这一点,你就有麻烦了

举例来说,以下程序(a):

这是在
while
循环之前的10亿除数,这意味着完整的迭代次数大约需要48年

所以蛮力法在这里没有帮助,你需要的是一些严肃的数学分析,这可能意味着你应该在上发一个类似的问题,让数学天才们试一试


(a) 如果您想知道为什么我要从用户那里获取值,而不是使用常量
100000000000000000ull
,这是为了防止
gcc
在编译时计算它并将其转换为类似以下内容:

mov  $1, %eax
同样的
返回x
,这将防止它注意到我没有使用
x
的最终值,从而优化循环,使其完全不存在。

我怀疑,最小减法次数达到零,这是一个非常棘手的问题,需要大量回溯潜在解决方案,对于你的时间限制来说可能太贵了

但你应该做的第一件事是做一次精神检查。由于最大数字是
9
,因此19位数字需要大约
1018
减法才能达到零。编写一个简单的程序,从1019中连续减去9,直到小于10为止。如果你不能在两秒钟内做到这一点,你就有麻烦了

举例来说,以下程序(a):

这是在
while
循环之前的10亿除数,这意味着完整的迭代次数大约需要48年

所以蛮力法在这里没有帮助,你需要的是一些严肃的数学分析,这可能意味着你应该在上发一个类似的问题,让数学天才们试一试


(a) 如果您想知道为什么我要从用户那里获取值,而不是使用常量
100000000000000000ull
,这是为了防止
gcc
在编译时计算它并将其转换为类似以下内容:

mov  $1, %eax

同样的
返回x
,这将阻止它注意到我没有使用
x
的最终值,因此完全优化了不存在的循环。

正如已经在评论中所说的,并且同意@paxdiablo的另一个答案,我不确定是否有一种算法可以在没有回溯的情况下找到理想的解决方案;而且数量的大小和时间限制可能也很严格

不过,一般的考虑是:你可能想找到一种方法来决定是否总是减去最高的数字(显然,这将使你当前的数字减少尽可能多的数量),或者通过查看你当前的数字,减去其中哪一个将给你最大的“新”数字

比方说,您当前的数字仅由
0
5
之间的数字组成–然后您可能会尝试减去
5
,以尽可能将数字减少最大值,然后继续下一步。但是,如果您当前数字的最后一位是
3
,那么您可能需要减去
4
——因为这将在数字末尾将
9
作为新数字,而不是减去
5
后得到的“仅”
8

然而,如果您的数字中已经有一个
2
和两个
9
,并且最后一个数字是
1
,那么您可能无论如何都要减去
9
,因为您将在结果中留下第二个
9
(至少在大多数情况下;在某些边缘情况下,它也可能从结果中被删除),因此,减去
2
将不会有给您下一步中可能没有的“高”
9
的优势,并且会有一个缺点,即不会将您的数字降低到减去
9
所能达到的程度


但是,你减去的每一个数字不仅会直接影响下一步,还会间接影响后续步骤——因此,我再次怀疑是否有一种方法可以始终为当前步骤选择理想的数字,而无需任何回溯或类似措施。

如评论中所述,并同意@paxdiablo的另一个答案,我不确定是否有一种算法可以在没有回溯的情况下找到理想的解决方案;而且数量的大小和时间限制可能也很严格

一个普遍的考虑因素是:你可能想找到一种方法来决定是总是减去最高的数字(显然,这将使你当前的数字减少最大可能的数量),还是通过查看你当前的数字并减去
mov  $1, %eax
uint64_t countGreedy(uint64_t inputVal) {
    uint64_t remVal = inputVal;
    uint64_t nStep = 0;
    while (remVal > 0) {
        uint64_t digitVal = remVal;
        uint_fast8_t maxDigit = 0;
        while (digitVal > 0) {
            uint64_t nextDigitVal = digitVal / 10;
            uint_fast8_t digit = digitVal - nextDigitVal * 10;
            if (digit > maxDigit) {
                maxDigit = digit;
            }
            digitVal = nextDigitVal;
        }
        remVal -= maxDigit;
        ++nStep;
    }
    return nStep;
}
optSteps(val) = 1 + min(optSteps(val - d_i))
static uint64_t countDynamic(uint64_t inputVal) {
    uint64_t minSteps[10] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
    uint_fast8_t digit0 = 0;
    for (uint64_t val = 10; val <= inputVal; ++val) {
        digit0 = val % 10;
        uint64_t digitVal = val;
        uint64_t minPrevStep = 0;
        bool prevStepSet = false;
        while (digitVal > 0) {
            uint64_t nextDigitVal = digitVal / 10;
            uint_fast8_t digit = digitVal - nextDigitVal * 10;
            if (digit > 0) {
                uint64_t prevStep = 0;
                if (digit > digit0) {
                    prevStep = minSteps[10 + digit0 - digit];
                } else {
                    prevStep = minSteps[digit0 - digit];
                }
                if (!prevStepSet || prevStep < minPrevStep) {
                    minPrevStep = prevStep;
                    prevStepSet = true;
                }
            }
            digitVal = nextDigitVal;
        }
        minSteps[digit0] = minPrevStep + 1;
    }
    return minSteps[digit0];
}