# 计算2个数字之间的小数位数

16

（6 + 9 + 5 + 9）/ 4 = 7.5

（1 + 2 + 3 + 4）/ 4 = 2.5

`HeftyDecimalCount(a, b)`

``````9480   (9+4+8+0)/4 21/4 = 5.25
9481   (9+4+8+1)/4 22/4 = 5.5
9482   (9+4+8+2)/4 23/4 = 5.75
9483   (9+4+8+3)/4 24/4 = 6
9484   (9+4+8+4)/4 25/4 = 6.25
9485   (9+4+8+5)/4 26/4 = 6.5
9486   (9+4+8+6)/4 27/4 = 6.75
9487   (9+4+8+7)/4 28/4 = 7
9488   (9+4+8+8)/4 29/4 = 7.25   hefty
9489   (9+4+8+9)/4 30/4 = 7.5    hefty
``````

• 假设a或b都不超过200,000,000。
• 一个n平方的解决方案会起作用，但会很慢-我们能解决这个问题的最快方法是什么？

2

11

$h(n,d) = \binom{n+d-1}{d-1} = \binom{(n+1)+(d-1)-1}{d-1}$

``````3-choose-2     fences        number
-----------------------------------
11             ||11          002
12             |1|1          011
13             |11|          020
22             1||1          101
23             1|1|          110
33             11||          200
``````

$g(n,d) = \binom{n+d-1}{d-1} - \binom{d}{1} \binom{n+d-1-10}{d-1}$

``````(10, 0, 1) => (10, 0, 11)
(0, 0, 11) => (10, 0, 11)
``````

$g(n,d) = \binom{n+d-1}{d-1} - \binom{d}{1} \binom{n+d-1-10}{d-1} + \binom{d}{2} \binom{n+d-1-20}{d-1}$

``````1 0 0 0 0 0 . .
1 1 0 0 0 0 . .
1 2 1 0 0 0 . .
1 4 6 4 1 0 . .
. . . . . .
. . . . . .
``````

$g(n,d) = \sum_{i=0}^{d} (-1)^i \binom{d}{i} \binom{n+d-1 - 10i}{d-1}$

$\bar{h}(n,d) = \binom{n+d}{d} = \binom{n+d-1}{d-1} + \binom{n+d-1}{d} = h(n,d) + \bar{h}(n-1,d)$

``````1240..1249:  10 - f(1, 28 - (1+2+4))
1250..1259:  10 - f(1, 28 - (1+2+5))
1260..1269:  10 - f(1, 28 - (1+2+6))
1270..1279:  10 - f(1, 28 - (1+2+7))
1280..1289:  10 - f(1, 28 - (1+2+8))
1290..1299:  10 - f(1, 28 - (1+2+9))
``````

``````1300..1399:  100 - f(2, 28 - (1+3))
1400..1499:  100 - f(2, 28 - (1+4))
1500..1599:  100 - f(2, 28 - (1+5))
1600..1699:  100 - f(2, 28 - (1+6))
1700..1799:  100 - f(2, 28 - (1+7))
1800..1899:  100 - f(2, 28 - (1+8))
1900..1999:  100 - f(2, 28 - (1+9))
``````

``````2000..2999:  1000 - f(3, 28 - 2)
3000..3999:  1000 - f(3, 28 - 3)
4000..4999:  1000 - f(3, 28 - 4)
``````

``````def binomial(n, k):
if k < 0 or k > n:
return 0
result = 1
for i in range(k):
result *= n - i
result //= i + 1
return result

binomial_lut = [
[1],
[1, -1],
[1, -2, 1],
[1, -3, 3, -1],
[1, -4, 6, -4, 1],
[1, -5, 10, -10, 5, -1],
[1, -6, 15, -20, 15, -6, 1],
[1, -7, 21, -35, 35, -21, 7, -1],
[1, -8, 28, -56, 70, -56, 28, -8, 1],
[1, -9, 36, -84, 126, -126, 84, -36, 9, -1]]

def f(d, n):
return sum(binomial_lut[d][i] * binomial(n + d - 10*i, d)
for i in range(d + 1))

def digits(i):
d = map(int, str(i))
d.reverse()
return d

def heavy(a, b):
b += 1
a_digits = digits(a)
b_digits = digits(b)
a_digits = a_digits + [0] * (len(b_digits) - len(a_digits))
max_digits = next(i for i in range(len(a_digits) - 1, -1, -1)
if a_digits[i] != b_digits[i])
a_digits = digits(a)
count = 0
digit = 0
while digit < max_digits:
while a_digits[digit] == 0:
digit += 1
inc = 10 ** digit
for i in range(10 - a_digits[digit]):
if a + inc > b:
break
count += inc - f(digit, 7 * len(a_digits) - sum(a_digits))
a += inc
a_digits = digits(a)
while a < b:
while digit and a_digits[digit] == b_digits[digit]:
digit -= 1
inc = 10 ** digit
for i in range(b_digits[digit] - a_digits[digit]):
count += inc - f(digit, 7 * len(a_digits) - sum(a_digits))
a += inc
a_digits = digits(a)
return count
``````

1
@Bob：代码是用Python编写的，根本没有优化。如果您希望它快一些，请用C编写。但是在纯Python中，还有很多改进的余地。需要优化的第一件事是`binomial()`功能。还有其他一些可以轻松改进的地方。我将在几分钟后发布更新。
Sven Marnach 2011年

@Moron：那肯定是最好的选择-我会尝试的。
Sven Marnach 2011年

@Moron：我需要在源代码中包含查找表。通常`f(d, n)`，在一次程序运行期间，不会使用相同的参数调用两次。
Sven Marnach 2011年

5

``````heavy_decimal_count(a,b,x)
``````

``````heavy_decimal_count(8675,8689,7)
``````

``````heavy_decimal_count(75,89,7)
``````

``````7: 5-9
8: 0-9
``````

2

@Moron：不，因为前两个8更改了重量阈值。在示例中，前两个是86，平均为7，因此不会更改阈值。如果（8 + 8 + x）/ 3> 7，则x> 5。因此，重型（886,887,7.0）==重型（6,7,5.0）。

@Phil H，我认为这种想法不会奏效：如果您采用9900和9999，则会将其更改为可能介于0和99之间的较重值，例如考虑到8而9908并不是一个很大的数字（ @Aryabhatta）。

3

1

1

``````int count_heavy(int digits,int min_sum) {
if (digits * 9 < min_sum)//impossible (ie, 2 digits and min_sum=19)
return 0; //this pruning is what makes it fast

if (min_sum <= 0)
return pow(10,digits);//any digit will do,
// (ie, 2 digits gives 10*10 possibilities)

if (digits == 1)
//recursion base
return 10-min_sum;//only the highest digits

//recursion step
int count = 0;
for (i = 0; i <= 9; i++)
{
//let the first digit be i, then
count += count_heavy(digits - 1, min_sum - i);
}
return count;
}

count_heavy(9,7*9+1); //average of 7,thus sum is 7*9, the +1 is 'exceeds'.
``````

0

``````public int heavy_decimal_count(int A, int B)
{
int count = 0;
for (int i = A; i <= B; i++)
{
char[] chrArray = i.ToString().ToCharArray();
float sum = 0f;
double average = 0.0f;
for (int j = 0; j < chrArray.Length; j++)
{
sum = sum + (chrArray[j] - '0');
}
average = sum / chrArray.Length;
if (average > 7)
count++;
}
return count;
}
``````

1

ugoren 2012年

0

# C，间隔[a，b]为O（ba）

``````c(n,s,j){return n?c(n/10,s+n%10,j+1):s>7*j;}

HeftyDecimalCount(a,b){int r; for(r=0;a<=b;++a)r+=c(a,0,0); return r;}
``````

//练习

``````main()
{
printf("[9480,9489]=%d\n", HeftyDecimalCount(9480,9489));
printf("[0,9489000]=%d\n", HeftyDecimalCount(9480,9489000));
return 0;
}
``````

//结果

``````//[9480,9489]=2
//[0,9489000]=66575
``````

RosLuP

1
@Riker这里的标签不是<codegolf>，而是<fast algorithm>
RosLuP