更高效的“前K个数字,它们的数字和是S”算法

时间:2016-04-13 14:11:24

标签: algorithm math time-complexity big-o asymptotic-complexity

整个问题听起来像是:

“我们在输入,K和S上有2个数字。我们想要在输出第一个(最低)K数字上打印,而它们的数字总和恰好是S”

有一个简单的天真算法来解决这个问题(我能够构建和找到)。它的原理是拥有一个函数 bigint digitSum(i),(我写bigint,因为 S 无论如何都不受限制,因为我想要更有效的算法......)这将返回参数号的数字和。我们将从数字0开始,总是递增1,同时将这些数字放在函数中。如果函数返回与 S 相同的总和,请打印该数字并继续,直到我们打印 K 数字。

功能代码在这里:

bigint digitSum(number){
bigint total = 0;
while(number > 0)
  {
     total += number % 10;
     number /= 10;
  }
return total;
}

Big-O中的算法渐近复杂度enter image description here因为enter image description here是搜索数字0,1,2,3 ... n的复杂性,直到我们找到完全 K 所需的数字和enter image description here是我们查找数字和的函数的复杂性,因为它总是将数字除以10.

是否有任何算法或方法可以提高效率?谢谢!

2 个答案:

答案 0 :(得分:1)

要解决这些问题,你必须抓住一些规律。例如,构建第一个数字的序列,其数字和为S

   S  0  1 .. 9  10  11  12 .. 18   19 20  .. 31 ...
F(S)  0  1 .. 9  19  29  39 .. 99  199 299 .. 4999...

我们可以看到第一个数字可以使用值

找到
M = S div 9
R = S mod 9

作为

F(S) = R(9xM)  ////concatenation of digit R and M 9s
for S=31  M=3,R=4, and
F(31) = 4(9x3) = 4999 //concatenation of 4 and three nines

因此我们可以确定O(1)中需要的第一个数字。 然后详细说明具有相同数字总和的下一个数字的规则(注意通常N(i+1) = N(i) + 9

答案 1 :(得分:1)

这是一种递归算法,可为您提供K个最小数字,其数字总和为S。复杂性肯定比你的蛮力算法好,虽然我不确定它在大O符号中是什么。

算法如下:

  • 查找nDigits = 1的所有组合,总计为S
  • 然后nDigits = 2,nDigits = 3,......直到count == K
  • nDigit的所有组合→1 + nDigit-1
  • 的所有组合的所有组合

这里是Java中的代码:

public static void main(String[] args) {
    int[] currentCount = {0};
    int k = 10, s = 10;

    for(int n = s/9 ; currentCount[0] != 10 ; n++) {
        digitSum(new StringBuilder(), n, 0, s, k, currentCount);
    }
}

public static void digitSum(StringBuilder subNumber, int nDigit, int currentSum, int s, int k, int[] currentCount) {
    if(nDigit == 0) {
        if(currentSum == s) {
            System.out.println(subNumber);
            currentCount[0]++;
        }
        return;
    }

    if(currentCount[0] == k) return; //if already have k numbers, terminate

    int remaining = s-currentSum;

    if(remaining > nDigit*9) return; //if not enough digits to reach S, terminate

    final int bound = Integer.min(9, remaining); //what's the largest valid digit

    //zero digit is only valid if subNumber != 0
    if(subNumber.length()!=0) digitSum(new StringBuilder(subNumber).append('0'), nDigit-1, currentSum, s, k, currentCount);

    for(int i = 1 ; i <= bound ; i++) digitSum(new StringBuilder(subNumber).append((char)(i+'0')), nDigit-1, currentSum+i, s, k, currentCount);
}

修改
我大致测量了时间复杂度,显然O(N)如下所示:
enter image description here