使用查找表计算数字数量
你可以生成一个表格,存储有多少个d位数的整数其各位数字之和大于一个数字x。然后,你可以快速查找任意10、100、1000……个整数范围内有多少重量级数字。这些表格仅包含9×d个值,因此它们占用的空间非常小,可以快速生成。
然后,要检查B具有d位数字的范围A-B,你需要构建1到d-1位数的表格,然后将范围A-B分成10、100、1000……的块,并在表格中查找值,例如对于范围A = 782,B = 4321:
RANGE DIGITS TARGET LOOKUP VALUE
782 - 789 78x > 6 table[1][ 6] 3 <- incomplete range: 2-9
790 - 799 79x > 5 table[1][ 5] 4
800 - 899 8xx >13 table[2][13] 15
900 - 999 9xx >12 table[2][12] 21
1000 - 1999 1xxx >27 table[3][27] 0
2000 - 2999 2xxx >26 table[3][26] 1
3000 - 3999 3xxx >25 table[3][25] 4
4000 - 4099 40xx >24 impossible 0
4100 - 4199 41xx >23 impossible 0
4200 - 4299 42xx >22 impossible 0
4300 - 4309 430x >21 impossible 0
4310 - 4319 431x >20 impossible 0
4320 - 4321 432x >19 impossible 0 <- incomplete range: 0-1
--
48
如果第一个和最后一个范围不完整(不是*0 - *9),请检查起始值或结束值是否大于目标值。(在本例中,2不大于6,因此所有3个重数字都包括在范围内。)
生成查找表
对于1位小数整数,大于值x的整数n的数量为:
x: 0 1 2 3 4 5 6 7 8 9
n: 9 8 7 6 5 4 3 2 1 0
如您所见,这可以通过取n = 9-x来轻松计算。
对于两位十进制整数,其数位之和大于值x的整数数量为:n。
x: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
n: 99 97 94 90 85 79 72 64 55 45 36 28 21 15 10 6 3 1 0
对于三位小数整数,其各个数字之和大于值
x的整数
n的数量为:
x: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
n: 999 996 990 980 965 944 916 880 835 780 717 648 575 500 425 352 283 220 165 120 84 56 35 20 10 4 1 0
这些序列中的每一个都可以从前一个生成:从值10d开始,然后从该值中逆向减去前一个序列(跳过第一个零)。例如,要从2位数序列生成3位数序列,请从103=1000开始,然后:
0. 1000 - 1 = 999
1. 999 - 3 = 996
2. 996 - 6 = 990
3. 990 - 10 = 980
4. 980 - 15 = 965
5. 965 - 21 = 944
6. 944 - 28 = 916
7. 916 - 36 = 880
8. 880 - 45 = 835
9. 835 - 55 = 780
10. 780 - 64 + 1 = 717 <- after 10 steps, start adding the previous sequence again
11. 717 - 72 + 3 = 648
12. 648 - 79 + 6 = 575
13. 575 - 85 + 10 = 500
14. 500 - 90 + 15 = 425
15. 425 - 94 + 21 = 352
16. 352 - 97 + 28 = 283
17. 283 - 99 + 36 = 220
18. 220 - 100 + 45 = 165 <- at the end of the sequence, keep subtracting 10^(d-1)
19. 165 - 100 + 55 = 120
20. 120 - 100 + 64 = 84
21. 84 - 100 + 72 = 56
22. 56 - 100 + 79 = 35
23. 35 - 100 + 85 = 20
24. 20 - 100 + 90 = 10
25. 10 - 100 + 94 = 4
26. 4 - 100 + 97 = 1
27. 1 - 100 + 99 = 0
顺便提一下,如果“重”数字定义的值不是7,您仍然可以使用相同的表格。
代码示例
以下是一个Javascript代码片段(我不会说Java),演示了该方法。它并不是非常优化,但它在不到0.07毫秒的时间内完成了0→100,000,000的示例。它也适用于除7以外的权重。翻译成Java后,它应该轻松击败任何实际运行数字并检查它们的权重的算法。
function countHeavy(A, B, weight) {
var a = decimalDigits(A), b = decimalDigits(B);
while (a.length < b.length) a.push(0);
var digits = b.length, table = weightTable();
var count = 0, diff = B - A + 1, d = 0;
for (var i = digits - 1; i >= 0; i--) if (a[i]) d = i;
while (diff) {
while (a[d] == 10) {
a[d++] = 0;
++a[d];
}
var step = Math.pow(10, d);
if (step <= diff) {
diff -= step;
count += increment(d);
}
else --d;
}
return count;
function weightTable() {
var t = [[],[9,8,7,6,5,4,3,2,1,0]];
for (var i = 2; i < digits; i++) {
var total = Math.pow(10, i), final = total / 10;
t[i] = [];
for (var j = 9 * i; total > 0; --j) {
if (j > 9) total -= t[i - 1][j - 10]; else total -= final;
if (j < 9 * (i - 1)) total += t[i - 1][j];
t[i].push(total);
}
}
return t;
}
function increment(d) {
var sum = 0, size = digits;
for (var i = digits - 1; i >= d; i--) {
if (a[i] == 0 && i == size - 1) size = i;
sum += a[i];
}
++a[d];
var target = weight * size - sum;
if (d == 0) return (target < 0) ? 1 : 0;
if (target < 0) return table[d][0] + 1;
return (target > 9 * d) ? 0 : table[d][target];
}
function decimalDigits(n) {
var array = [];
do {array.push(n % 10);
n = Math.floor(n / 10);
} while (n);
return array;
}
}
document.write("0 → 100,000,000 = " + countHeavy(0, 100000000, 7) + "<br>");
document.write("782 → 4321 = " + countHeavy(782, 4321, 7) + "<br>");
document.write("782 → 4321 = " + countHeavy(782, 4321, 5) + " (weight: 5)");
averageOfDigits
函数中,我会将结果设为int
类型,然后简单地使用return (float)result / count;
。这样可以一直使用整数运算,直到函数返回。可能不是很大的优化,但仍然有用... - Rudy VelthuisA >= B
始终成立,那么A
应该递减,并且条件应该是while (A >= B)
。 - Rudy Velthuis