当给出n的位数之和时,求出n + 1,n + 2 ......的位数之和

时间:2012-09-11 06:53:58

标签: java algorithm math sum digits

我们可以很容易地计算给定数字的位数之和,但是我们可以使用任何数学公式或模式来确定下一个数字的总和而不必一次又一次地对所有数字求和吗?

例如

Sum of 1234 = 1+2+3+4 = 10
Sum of 1235 = 1+2+3+5 = 11
Sum of 1236 = 1+2+3+6 = 12

我可以在这里看到某种模式,但无法提出任何有效的数学算法。

我使用以下方法计算数字之和:

public int sum(long n) {  
    int sum = 0;   
    while (n != 0) {    
        sum += n % 10;
        n /= 10;  
    }  
    return sum;  
}

哪个工作正常,但它是CPU密集型的。我想更快地做到这一点。如果我有一系列数字,请说10->19我只需要计算10的数字,然后为每个数字加上一个数字。

如果我已经拥有以前数字的总和,是否有任何有效的方法来计算数字的总和?

4 个答案:

答案 0 :(得分:8)

DigitSum(n+1) = DigitSum(n) + 1 - (9 * NumberOfEndingZeros(n+1))

如果您不想找到t个连续数字的位数,而是找到t个连续数字(n + 1,n + 2,...,n + t)的数字之和,它更简单。

Sum(DigitSum(i)) for i = n+1 to n+t = a(n+t) - a(n)

其中a(i)是整数序列百科全书中的A037123序列,它有几个公式。我认为这会很快:

a(n) = (1/2) * ( (n+1) * (n - 18 * sum{k>0, floor(n/10^k)} )
               + 9 * sum{k>0, (1+floor(n/10^k))*floor(n/10^k)*10^k}
               )

答案 1 :(得分:5)

最快的方法是提取每个数字并随时添加。

public static void main(String... args) {
    // check values
    int runs = 1000000;
    for (int i = 100; i < runs; i++) {
        int sum = sumDigits(i - 1);
        int sum1 = sumDigits(i);
        int sum2 = sumDigits(sum, i);
        if (sum1 != sum2) throw new AssertionError(i + ": " + sum1 + " != " + sum2);
    }
    long start = System.nanoTime();
    for (int i = 0; i < runs; i++) {
        int sum = sumDigits(i);
        // prevent optimising away.
        if (sum < 0) throw new AssertionError();
    }
    long time = System.nanoTime() - start;
    System.out.printf("sumDigits took an average of %,d ns%n", time / runs);
    long start2 = System.nanoTime();
    int lastSum = 0;
    for (int i = 0; i < runs; i++) {
        int sum = sumDigits(lastSum, i);
        lastSum = sum;
        // prevent optimising away.
        if (sum < 0) throw new AssertionError();
    }
    long time2 = System.nanoTime() - start2;
    System.out.printf("sumDigits using previous value took an average of %,d ns%n", time2 / runs);

    long large = Long.MAX_VALUE - runs - 1;

    long start3 = System.nanoTime();
    for (int i = 0; i < runs; i++) {
        int sum = sumDigits(large + i);
        // prevent optimising away.
        if (sum < 0) throw new AssertionError();
    }
    long time3 = System.nanoTime() - start3;
    System.out.printf("sumDigits took an average of %,d ns%n", time3 / runs);
    long start4 = System.nanoTime();
    int lastSum2 = sumDigits(large);
    for (int i = 0; i < runs; i++) {
        int sum = sumDigits(lastSum2, large + i);
        lastSum2 = sum;
        // prevent optimising away.
        if (sum < 0) throw new AssertionError();
    }
    long time4 = System.nanoTime() - start4;
    System.out.printf("sumDigits using previous value took an average of %,d ns%n", time4 / runs);

}

public static int sumDigits(long n) {
    int sum = 0;
    do {
        sum += n % 10;
        n /= 10;
    } while (n > 0);
    return sum;
}

public static int sumDigits(int prevSum, long n) {
    while (n > 0 && n % 10 == 0) {
        prevSum -= 9;
        n /= 10;
    }
    return prevSum + 1;
}

打印

sumDigits took an average of 32 ns
sumDigits using previous value took an average of 10 ns
sumDigits took an average of 79 ns
sumDigits using previous value took an average of 7 ns

对于较大的值,它可以节省大约70 ns。它为您的代码增加了一些复杂性。你必须使用第一个sumDigit来引导总和,因为你无法从1到10 ^ 18一直计算。

答案 2 :(得分:1)

让我们将数字n的数字之和表示为s(n)

s(49) = 13

s(94) = 13

但是49+1 = 50 s(50) = 5,而94+1 = 95 s(95) = 14。因此,如果仅给出n的数字之和为13,则n+1的数字总和至少有两个不同的可能答案。 您需要一些有关n 的其他信息。

我认为关键是知道如果n以9结尾,s(n+1)最多只能是s(n) - 9.(啊,这就是ypercube的答案所在。)所以如果你有xxxx9 (最后的x不是9),s(xxxx9 + 1) = s(xxxx9) - 9 + 1。如果您有xxx99,s(xxx99 + 1) = s(xxx99) - 18 + 1

因此,如果你计算你所在范围内的所有10,100,数千等,可能会加快速度。

(再次,我看到ypercube击败了我的拳头)。看起来A037123的公式正是如此(但是从0到n)。 (让我们称之为a(n)

所以最后,因为你想要从n到n + r的数字之和的总和,以及而不是从1到n的总和,我们需要看看我们是否可以推导出范围ss(n,n+r)

的总和之和的公式

似乎只是

ss(n,n+r) = a(n+r) - a(n-1)

答案 3 :(得分:0)

这是 wikipedia 中的公式:

This is the formula


这是我在Java中的实现:

public static int digitSum(int x) {
    return IntStream.rangeClosed(0, (int) Math.log10(x)) // From zero to the number of digits of x in base 10...
               .map(n -> (x % (int) Math.pow(10, n + 1) -  x % (int) Math.pow(10, n)) / (int) Math.pow(10, n)) // ...yield each digit...
               .sum() // and sum;
}