热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何计算一个整数范围内的每个数字?-Howtocounteachdigitinarangeofintegers?

Imagineyousellthosemetallicdigitsusedtonumberhouses,lockerdoors,hotelrooms,etc.Youne

Imagine you sell those metallic digits used to number houses, locker doors, hotel rooms, etc. You need to find how many of each digit to ship when your customer needs to number doors/houses:

想象一下,你把这些金属数字卖给房子、橱柜门、酒店房间等等。你需要知道,当你的客户需要数个门/房子时,你需要多少个数字。

  • 1 to 100
  • 1到100
  • 51 to 300
  • 51 - 300
  • 1 to 2,000 with zeros to the left
  • 1到2000,左边是0。

The obvious solution is to do a loop from the first to the last number, convert the counter to a string with or without zeros to the left, extract each digit and use it as an index to increment an array of 10 integers.

最明显的解决方案是,从第一个到最后一个数字循环,将计数器转换为带有或没有0的字符串,提取每个数字,并将其作为一个索引来增加10个整数的数组。

I wonder if there is a better way to solve this, without having to loop through the entire integers range.

我想知道是否有更好的方法来解决这个问题,而不需要遍历整个整数范围。

Solutions in any language or pseudocode are welcome.

任何语言或伪代码的解决方案都是受欢迎的。


Edit:

Answers review
John at CashCommons and Wayne Conrad comment that my current approach is good and fast enough. Let me use a silly analogy: If you were given the task of counting the squares in a chess board in less than 1 minute, you could finish the task by counting the squares one by one, but a better solution is to count the sides and do a multiplication, because you later may be asked to count the tiles in a building.
Alex Reisner points to a very interesting mathematical law that, unfortunately, doesn’t seem to be relevant to this problem.
Andres suggests the same algorithm I’m using, but extracting digits with %10 operations instead of substrings.
John at CashCommons and phord propose pre-calculating the digits required and storing them in a lookup table or, for raw speed, an array. This could be a good solution if we had an absolute, unmovable, set in stone, maximum integer value. I’ve never seen one of those.
High-Performance Mark and strainer computed the needed digits for various ranges. The result for one millon seems to indicate there is a proportion, but the results for other number show different proportions.
strainer found some formulas that may be used to count digit for number which are a power of ten. Robert Harvey had a very interesting experience posting the question at MathOverflow. One of the math guys wrote a solution using mathematical notation.
Aaronaught developed and tested a solution using mathematics. After posting it he reviewed the formulas originated from Math Overflow and found a flaw in it (point to Stackoverflow :).
noahlavine developed an algorithm and presented it in pseudocode.

在《现金共享》和韦恩·康拉德的评论中,我的回答是:我目前的方法既好又快。让我用一个愚蠢的类比:如果你被给予的任务数方格的棋盘在不到1分钟,可以完成这项任务通过计算方块一个接一个,但是一个更好的解决方案是计算,做乘法,因为你以后可能会被要求计算建筑物的瓷砖。Alex Reisner指出了一个非常有趣的数学定律,不幸的是,它似乎与这个问题无关。Andres提出了我正在使用的相同算法,但是使用%10操作而不是子字符串来提取数字。John在CashCommons和phord建议预先计算所需的数字,并将它们存储在一个查找表中,或者,以原始速度,一个数组。这是一个很好的解决方案,如果我们有一个绝对的,不可移动的,设置在石头上,最大的整数值。我从来没见过。高性能标记和过滤器计算各种范围所需的数字。一个米隆的结果似乎表明有一个比例,但其他数字的结果显示不同的比例。strainer发现了一些公式,可以用来计算数字的数字,这是10的幂。罗伯特·哈维在MathOverflow上发布了一个非常有趣的问题。其中一个数学老师用数学符号写了一个解。Aaronaught开发并测试了一个使用数学的解决方案。在发布后,他回顾了源自数学溢出的公式并发现了它的一个缺陷(指向Stackoverflow:)。noahlavine开发了一种算法并将其呈现在伪代码中。

A new solution
After reading all the answers, and doing some experiments, I found that for a range of integer from 1 to 10n-1:

在读完所有的答案后,我找到了一个新的解决方案,并做了一些实验,我发现从1到10n-1的整数范围:

  • For digits 1 to 9, n*10(n-1) pieces are needed
  • 对于数字1到9,需要n*10(n-1)块。
  • For digit 0, if not using leading zeros, n*10n-1 - ((10n-1) / 9) are needed
  • 对于数字0,如果不使用前导零,则需要n*10n-1 - ((10n-1) / 9)。
  • For digit 0, if using leading zeros, n*10n-1 - n are needed
  • 对于数字0,如果使用前导零,则需要n*10n-1 - n。

The first formula was found by strainer (and probably by others), and I found the other two by trial and error (but they may be included in other answers).

第一个公式是由strainer(可能是其他人)发现的,我通过尝试和错误找到了另外两个公式(但它们可能包含在其他答案中)。

For example, if n = 6, range is 1 to 999,999:

例如,如果n = 6,范围是1到999,999:

  • For digits 1 to 9 we need 6*105 = 600,000 of each one
  • 对于数字1到9,我们需要6*105 = 60万。
  • For digit 0, without leading zeros, we need 6*105 – (106-1)/9 = 600,000 - 111,111 = 488,889
  • 对于数字0,没有前导零,我们需要6*105 -(106-1)/9 = 600,000 - 111,111 = 488,889。
  • For digit 0, with leading zeros, we need 6*105 – 6 = 599,994
  • 对于数字0,用前导零,我们需要6*105 - 6 = 599,994。

These numbers can be checked using High-Performance Mark results.

可以使用高性能的标记结果来检查这些数字。

Using these formulas, I improved the original algorithm. It still loops from the first to the last number in the range of integers, but, if it finds a number which is a power of ten, it uses the formulas to add to the digits count the quantity for a full range of 1 to 9 or 1 to 99 or 1 to 999 etc. Here's the algorithm in pseudocode:

利用这些公式,改进了原算法。还是从第一个到最后一个号码循环范围内的整数,但是,如果它发现一个数字是十的力量,它使用公式添加数字计数1到9的数量为全系列1到99或1到999等算法的伪代码:

integer First,Last //First and last number in the range
integer Number     //Current number in the loop
integer Power      //Power is the n in 10^n in the formulas
integer Nines      //Nines is the resut of 10^n - 1, 10^5 - 1 = 99999
integer Prefix     //First digits in a number. For 14,200, prefix is 142
array 0..9  Digits //Will hold the count for all the digits

FOR Number = First TO Last
  CALL TallyDigitsForOneNumber WITH Number,1  //Tally the count of each digit 
                                              //in the number, increment by 1
  //Start of optimization. Comments are for Number = 1,000 and Last = 8,000.
  Power = Zeros at the end of number //For 1,000, Power = 3
  IF Power > 0                       //The number ends in 0 00 000 etc 
    Nines = 10^Power-1                 //Nines = 10^3 - 1 = 1000 - 1 = 999
    IF Number+Nines <= Last            //If 1,000+999 <8,000, add a full set
      Digits[0-9] += Power*10^(Power-1)  //Add 3*10^(3-1) = 300 to digits 0 to 9
      Digits[0]   -= -Power              //Adjust digit 0 (leading zeros formula)
      Prefix = First digits of Number    //For 1000, prefix is 1
      CALL TallyDigitsForOneNumber WITH Prefix,Nines //Tally the count of each 
                                                     //digit in prefix,
                                                     //increment by 999
      Number += Nines                    //Increment the loop counter 999 cycles
    ENDIF
  ENDIF 
  //End of optimization
ENDFOR  

SUBROUTINE TallyDigitsForOneNumber PARAMS Number,Count
  REPEAT
    Digits [ Number % 10 ] += Count
    Number = Number / 10
  UNTIL Number = 0

For example, for range 786 to 3,021, the counter will be incremented:

例如,对于786到3021的范围,计数器将增加:

  • By 1 from 786 to 790 (5 cycles)
  • 从786到790(5个周期)
  • By 9 from 790 to 799 (1 cycle)
  • 从790到799(1个周期)
  • By 1 from 799 to 800
  • 从799到800。
  • By 99 from 800 to 899
  • 从800到899。
  • By 1 from 899 to 900
  • 从899到900。
  • By 99 from 900 to 999
  • 从900到999。
  • By 1 from 999 to 1000
  • 从999到1000。
  • By 999 from 1000 to 1999
  • 从1000到1999的999。
  • By 1 from 1999 to 2000
  • 从1999年到2000年。
  • By 999 from 2000 to 2999
  • 从2000年到2999年。
  • By 1 from 2999 to 3000
  • 从2999到3000。
  • By 1 from 3000 to 3010 (10 cycles)
  • 从3000到3010(10个周期)
  • By 9 from 3010 to 3019 (1 cycle)
  • 由3010至3019(1个循环)
  • By 1 from 3019 to 3021 (2 cycles)
  • 由3019至3021(2个周期)

Total: 28 cycles Without optimization: 2,235 cycles

总:28个周期无优化:2,235个周期。

Note that this algorithm solves the problem without leading zeros. To use it with leading zeros, I used a hack:

注意,这个算法解决了问题,没有前导零。用前导零来使用它,我用了一个hack:

If range 700 to 1,000 with leading zeros is needed, use the algorithm for 10,700 to 11,000 and then substract 1,000 - 700 = 300 from the count of digit 1.

如果需要使用前导零的范围为700到1000,则使用算法为10700到11000,然后从数字1的计数中减去1000 - 700 = 300。

Benchmark and Source code

基准和源代码

I tested the original approach, the same approach using %10 and the new solution for some large ranges, with these results:

我测试了原始的方法,同样的方法使用%10和一些大范围的新解决方案,这些结果:

Original             104.78 seconds
With %10              83.66
With Powers of Ten     0.07

A screenshot of the benchmark application:
alt text http://clarion.sca.mx/images/stories/digitsbench.png

该基准应用程序的屏幕截图:alt文本http://clari.sca.mx/images/stories/digitsbench.png。

If you would like to see the full source code or run the benchmark, use these links:

如果您想查看完整的源代码或运行基准测试,请使用以下链接:

  • Complete Source code (in Clarion): http://sca.mx/ftp/countdigits.txt
  • 完整的源代码(在Clarion中):http://sca.mx/ftp/countdigits.txt。
  • Compilable project and win32 exe: http://sca.mx/ftp/countdigits.zip
  • 可编译的项目和win32 exe: http://sca.mx/ftp/countdigits.zip。

Accepted answer

接受的答案

noahlavine solution may be correct, but l just couldn’t follow the pseudo code, I think there are some details missing or not completely explained.

noahlavine解决方案可能是正确的,但是我只是不能遵循pseudo代码,我认为有些细节缺失或者没有完全解释。

Aaronaught solution seems to be correct, but the code is just too complex for my taste.

Aaronaught的解决方案似乎是正确的,但是代码对我的口味来说太复杂了。

I accepted strainer’s answer, because his line of thought guided me to develop this new solution.

我接受了斯特莱纳的回答,因为他的思路引导我去开发这个新的解决方案。

11 个解决方案

#1


5  

To reel of the digits from a number, we'd only ever need to do a costly string conversion if we couldnt do a mod, digits can most quickly be pushed of a number like this:

要从一个数字中记录数字,我们只需要做一个代价昂贵的字符串转换,如果我们不能做一个mod,数字可以最快的被这样的数字推动:

feed=number;
do
{ digit=feed%10;
  feed/=10; 
  //use digit... eg. digitTally[digit]++;
  }
while(feed>0)

that loop should be very fast and can just be placed inside a loop of the start to end numbers for the simplest way to tally the digits.

这个循环应该是非常快的,并且可以被放置在一个循环中,以最简单的方式来计算数字。

To go faster, for larger range of numbers, im looking for an optimised method of tallying all digits from 0 to number*10^significance (from a start to end bazzogles me)

更快,更大范围的数字,我寻找一个优化的方法计算所有数字从0到数量* 10 ^意义(从开始到结束bazzogles我)

here is a table showing digit tallies of some single significant digits.. these are inclusive of 0, but not the top value itself, -that was an oversight but its maybe a bit easier to see patterns (having the top values digits absent here) These tallies dont include trailing zeros,

这里有一张表显示了一些个位数的数字。这些都包含0,但不是最大值本身,这是一个疏忽,但它可能更容易看到模式(这里不包括上面的值)这些数据不包括尾随零,

  1 10 100 1000 10000 2 20 30 40 60 90 200 600 2000  6000

0 1 1  10  190  2890  1  2  3  4  6  9  30 110  490  1690
1 0 1  20  300  4000  1 12 13 14 16 19 140 220 1600  2800
2 0 1  20  300  4000  0  2 13 14 16 19  40 220  600  2800
3 0 1  20  300  4000  0  2  3 14 16 19  40 220  600  2800
4 0 1  20  300  4000  0  2  3  4 16 19  40 220  600  2800
5 0 1  20  300  4000  0  2  3  4 16 19  40 220  600  2800
6 0 1  20  300  4000  0  2  3  4  6 19  40 120  600  1800
7 0 1  20  300  4000  0  2  3  4  6 19  40 120  600  1800
8 0 1  20  300  4000  0  2  3  4  6 19  40 120  600  1800
9 0 1  20  300  4000  0  2  3  4  6  9  40 120  600  1800

edit: clearing up my origonal thoughts:

编辑:整理我的原始想法:

from the brute force table showing tallies from 0 (included) to poweroTen(notinc) it is visible that a majordigit of tenpower:

从蛮力表中可以看出,从0(包括)到poweroTen(notinc),可以看到一个主要的tenpower数字:

increments tally[0 to 9] by md*tp*10^(tp-1)
increments tally[1 to md-1] by 10^tp
decrements tally[0] by (10^tp - 10) 
(to remove leading 0s if tp>leadingzeros)
can increment tally[moresignificantdigits] by self(md*10^tp) 
(to complete an effect)

if these tally adjustments were applied for each significant digit, the tally should be modified as though counted from 0 to end-1

如果对每一个有效数字都应用了这些计数调整,则应该将计数从0到end 1进行修改。

the adjustments can be inverted to remove preceeding range (start number)

调整可以倒转以移走之前的范围(开始数)

Thanks Aaronaught for your complete and tested answer.

感谢Aaronaught的完整和测试的答案。

#2


10  

There's a clear mathematical solution to a problem like this. Let's assume the value is zero-padded to the maximum number of digits (it's not, but we'll compensate for that later), and reason through it:

对于这样的问题,有一个清晰的数学解决方案。让我们假设这个值是零填充到最大位数(它不是,但是我们稍后会补偿),以及通过它的原因:

  • From 0-9, each digit occurs once
  • 从0到9,每个数字出现一次。
  • From 0-99, each digit occurs 20 times (10x in position 1 and 10x in position 2)
  • 从0-99,每一个数字出现20次(10x在位置1和10x在位置2)
  • From 0-999, each digit occurs 300 times (100x in P1, 100x in P2, 100x in P3)
  • 从0-999,每一个数字出现300次(100x在P1, 100x在P2, 100x在P3)

The obvious pattern for any given digit, if the range is from 0 to a power of 10, is N * 10N-1, where N is the power of 10.

任何给定数字的明显模式,如果取值范围是从0到10的幂,就是N * 10N-1,其中N是10的幂。

What if the range is not a power of 10? Start with the lowest power of 10, then work up. The easiest case to deal with is a maximum like 399. We know that for each multiple of 100, each digit occurs at least 20 times, but we have to compensate for the number of times it appears in the most-significant-digit position, which is going to be exactly 100 for digits 0-3, and exactly zero for all other digits. Specifically, the extra amount to add is 10N for the relevant digits.

如果范围不是10的幂呢?从10的最低能量开始,然后向上运动。最简单的例子是399。我们知道,对于每100个倍数,每一个数字至少出现20次,但我们必须补偿它出现在最重要的位置上的次数,这个位置恰好是0-3的100,而所有其他的数字正好是0。具体来说,添加的额外数量是10N的相关数字。

Putting this into a formula, for upper bounds that are 1 less than some multiple of a power of 10 (i.e. 399, 6999, etc.) it becomes: M * N * 10N-1 + iif(d <= M, 10N, 0)

将其放入公式中,上界小于10的倍数(即399,6999,等等)它变成:M * N * 10N-1 + iif(d <= M, 10N, 0)

Now you just have to deal with the remainder (which we'll call R). Take 445 as an example. This is whatever the result is for 399, plus the range 400-445. In this range, the MSD occurs R more times, and all digits (including the MSD) also occur at the same frequencies they would from range [0 - R].

现在你只需要处理剩下的部分(我们称之为R),以445为例。这是399的结果,加上400-445的范围。在这个范围内,MSD发生R次,所有的数字(包括MSD)也发生在相同频率范围内[0 - R]。

Now we just have to compensate for the leading zeros. This pattern is easy - it's just:

现在我们只需要补偿前导零。这个模式很简单——只是:

10N + 10N-1 + 10N-2 + ... + **100

10N + 10N-1 + 10N-2 +…+ * * 100

Update: This version correctly takes into account "padding zeros", i.e. the zeros in middle positions when dealing with the remainder ([400, 401, 402, ...]). Figuring out the padding zeros is a bit ugly, but the revised code (C-style pseudocode) handles it:

更新:这个版本正确地考虑了“填充零”,即在处理剩余部分时中间位置的零([400,401,402,…])。计算填充0是有点难看,但是修改后的代码(C-style伪代码)处理它:

function countdigits(int d, int low, int high) {
    return countdigits(d, low, high, false);
}

function countdigits(int d, int low, int high, bool inner) {
    if (high == 0)
        return (d == 0) ? 1 : 0;

    if (low > 0)
        return countdigits(d, 0, high) - countdigits(d, 0, low);

    int n = floor(log10(high));
    int m = floor((high + 1) / pow(10, n));
    int r = high - m * pow(10, n);
    return
        (max(m, 1) * n * pow(10, n-1)) +                             // (1)
        ((d = 0) && (n > 0)) ? countdigits(d, 0, r, true) : 0) +   // (3)
        (((r >= 0) && (d == m)) ? (r + 1) : 0) +                     // (4)
        (((r >= 0) && (d == 0)) ? countpaddingzeros(n, r) : 0) -     // (5)
        (((d == 0) && !inner) ? countleadingzeros(n) : 0);           // (6)
}

function countleadingzeros(int n) {
      int tmp= 0;
      do{
         tmp= pow(10, n)+tmp;
         --n;
         }while(n>0);
         return tmp;
         }

function countpaddingzeros(int n, int r) {
    return (r + 1) * max(0, n - max(0, floor(log10(r))) - 1);
}

As you can see, it's gotten a bit uglier but it still runs in O(log n) time, so if you need to handle numbers in the billions, this will still give you instant results. :-) And if you run it on the range [0 - 1000000], you get the exact same distribution as the one posted by High-Performance Mark, so I'm almost positive that it's correct.

正如你所看到的,它变得更难看了,但是它仍然运行在O(log n)的时间里,所以如果你需要处理数十亿的数字,这仍然会给你即时的结果。如果你在[0 - 1000000]范围内运行它,你得到的分布和高性能标记的分布完全相同,所以我几乎肯定它是正确的。

FYI, the reason for the inner variable is that the leading-zero function is already recursive, so it can only be counted in the first execution of countdigits.

FYI,内部变量的原因是导零函数已经是递归的,所以只能在第一次执行count位数时进行计数。

Update 2: In case the code is hard to read, here's a reference for what each line of the countdigits return statement means (I tried inline comments but they made the code even harder to read):

更新2:如果代码很难读,这里是对count位数返回语句的每一行的引用(我尝试了内联注释,但是它们使代码更难读):

  1. Frequency of any digit up to highest power of 10 (0-99, etc.)
  2. 最高功率为10(0-99,等等)的频率。
  3. Frequency of MSD above any multiple of highest power of 10 (100-399)
  4. MSD频率高于10(100-399)的最高倍数
  5. Frequency of any digits in remainder (400-445, R = 45)
  6. 其余数字的频率(400-445,R = 45)
  7. Additional frequency of MSD in remainder
  8. 剩余的MSD频率。
  9. Count zeros in middle position for remainder range (404, 405...)
  10. 在剩余范围(404,405…)的中间位置数0。
  11. Subtract leading zeros only once (on outermost loop)
  12. 只将前导零减去一次(在最外层循环)

#3


8  

I'm assuming you want a solution where the numbers are in a range, and you have the starting and ending number. Imagine starting with the start number and counting up until you reach the end number - it would work, but it would be slow. I think the trick to a fast algorithm is to realize that in order to go up one digit in the 10^x place and keep everything else the same, you need to use all of the digits before it 10^x times plus all digits 0-9 10^(x-1) times. (Except that your counting may have involved a carry past the x-th digit - I correct for this below.)

我假设你想要一个解,其中的数字在一个范围内,你有开始和结束的数字。想象一下从开始数开始,然后数到最后的数字——它会起作用,但它会很慢。我认为快速算法的技巧是意识到为了上一位在10 ^ x的地方,保持一切一样,您需要使用所有的数字之前10 ^ x * +数字0 - 9 10 ^(x - 1)。(除了你的计数可能涉及到超过第x位的数字-我在下面是正确的。)

Here's an example. Say you're counting from 523 to 1004.

这是一个例子。比如从523到1004。

  • First, you count from 523 to 524. This uses the digits 5, 2, and 4 once each.
  • 首先,从523到524。它使用数字5、2和4。
  • Second, count from 524 to 604. The rightmost digit does 6 cycles through all of the digits, so you need 6 copies of each digit. The second digit goes through digits 2 through 0, 10 times each. The third digit is 6 5 times and 5 100-24 times.
  • 第二,从524到604。最右边的数字做了6个周期的所有数字,所以你需要6个数字的拷贝。第二个数字通过数字2到0,每一个10次。第三位是6 5乘以5 100-24。
  • Third, count from 604 to 1004. The rightmost digit does 40 cycles, so add 40 copies of each digit. The second from right digit doers 4 cycles, so add 4 copies of each digit. The leftmost digit does 100 each of 7, 8, and 9, plus 5 of 0 and 100 - 5 of 6. The last digit is 1 5 times.
  • 第三,从604到1004。最右边的数字是40个周期,所以每个数字加40个。第二个来自右数字的实施者4个周期,所以每个数字加4个副本。最左边的数字是每7个,8个,9个,加上5个0和100 - 5个6。最后一个数字是1 5乘以。

To speed up the last bit, look at the part about the rightmost two places. It uses each digit 10 + 1 times. In general, 1 + 10 + ... + 10^n = (10^(n+1) - 1)/9, which we can use to speed up counting even more.

为了加快最后一点,看看最右边两个地方的部分。它使用每个数字10 + 1次。一般来说,1 + 10 +…+ 10 n = (10 (n+1) - 1)/9,我们可以用它来加快计数。

My algorithm is to count up from the start number to the end number (using base-10 counting), but use the fact above to do it quickly. You iterate through the digits of the starting number from least to most significant, and at each place you count up so that that digit is the same as the one in the ending number. At each point, n is the number of up-counts you need to do before you get to a carry, and m the number you need to do afterwards.

我的算法是从开始数到最终数(使用base-10计数),但是使用上面的事实来快速完成它。你遍历起始数的数字,从最小到最重要的,在每一个地方都加起来,使这个数字与结尾数字中的数字相同。在每一个点上,n是在你到达一个进位之前需要做的向上计数的数量,而m是你以后需要做的数字。

Now let's assume pseudocode counts as a language. Here, then, is what I would do:

现在让我们假设伪代码是一种语言。在这里,我要做的是:

convert start and end numbers to digit arrays start[] and end[]
create an array counts[] with 10 elements which stores the number of copies of
     each digit that you need

iterate through start number from right to left. at the i-th digit,
    let d be the number of digits you must count up to get from this digit
        to the i-th digit in the ending number. (i.e. subtract the equivalent
        digits mod 10)
    add d * (10^i - 1)/9 to each entry in count.
    let m be the numerical value of all the digits to the right of this digit,
        n be 10^i - m.
    for each digit e from the left of the starting number up to and including the
        i-th digit, add n to the count for that digit.
    for j in 1 to d
        increment the i-th digit by one, including doing any carries
        for each digit e from the left of the starting number up to and including
            the i-th digit, add 10^i to the count for that digit
    for each digit e from the left of the starting number up to and including the
        i-th digit, add m to the count for that digit.
    set the i-th digit of the starting number to be the i-th digit of the ending
        number.

Oh, and since the value of i increases by one each time, keep track of your old 10^i and just multiply it by 10 to get the new one, instead of exponentiating each time.

哦,因为我每次都加1,所以要跟踪你的老10,我把它乘以10得到新的,而不是每次取幂。

#4


6  

Here's a very bad answer, I'm ashamed to post it. I asked Mathematica to tally the digits used in all numbers from 1 to 1,000,000, no leading 0s. Here's what I got:

这是一个很糟糕的回答,我很不好意思张贴出来。我要求Mathematica将所有数字中使用的数字从1到100万不等,而不是0。这是我得到的答案:

0   488895
1   600001
2   600000
3   600000
4   600000
5   600000
6   600000
7   600000
8   600000
9   600000

Next time you're ordering sticky digits for selling in your hardware store, order in these proportions, you won't be far wrong.

下次当你在你的五金店订购粘性数字时,按这些比例排序,你就不会错了。

#5


5  

I asked this question on Math Overflow, and got spanked for asking such a simple question. One of the users took pity on me and said if I posted it to The Art of Problem Solving, he would answer it; so I did.

我问了这个关于数学溢出的问题,并因为问了这么简单的问题而被打了屁股。有一个用户很同情我,说如果我把它贴在解决问题的艺术上,他会回答的;所以我所做的。

Here is the answer he posted:
http://www.artofproblemsolving.com/Forum/viewtopic.php?p=1741600#1741600

以下是他发布的答案:http://www.artofproblem solving.com/forum/viewtopic.php?

Embarrassingly, my math-fu is inadequate to understand what he posted (the guy is 19 years old...that is so depressing). I really need to take some math classes.

令人尴尬的是,我的数学老师不太懂他的帖子(那家伙19岁了……)这是如此令人沮丧)。我真的需要上一些数学课。

On the bright side, the equation is recursive, so it should be a simple matter to turn it into a recursive function with a few lines of code, by someone who understands the math.

在好的方面,这个等式是递归的,所以应该是一个简单的问题,用几行代码把它变成递归函数,由理解数学的人来做。

#6


3  

Your approach is fine. I'm not sure why you would ever need anything faster than what you've described.

你的方法很好。我不知道为什么你需要比你描述的更快的东西。

Or, this would give you an instantaneous solution: Before you actually need it, calculate what you would need from 1 to some maximum number. You can store the numbers needed at each step. If you have a range like your second example, it would be what's needed for 1 to 300, minus what's needed for 1 to 50.

或者,这将给你一个瞬时解:在你实际需要它之前,计算你需要从1到某个最大值。您可以存储每个步骤所需的数字。如果你有一个像你的第二个例子的范围,它将是需要的1到300,减去需要的1到50。

Now you have a lookup table that can be called at will. Doing up to 10,000 would only take a few MB and, what, a few minutes to compute, once?

现在您有一个可以随意调用的查找表。达到10,000只需要几MB,几分钟就可以计算一次?

#7


3  

I know this question has an accepted answer but I was tasked with writing this code for a job interview and I think I came up with an alternative solution that is fast, requires no loops and can use or discard leading zeroes as required.

我知道这个问题有一个被接受的答案,但我的任务是写这段代码来进行工作面试,我想我找到了一个快速的替代方案,不需要循环,可以根据需要使用或丢弃前导零。

It is in fact quite simple but not easy to explain.

这其实很简单,但很难解释。

If you list out the first n numbers

如果你列出了第一个n个数字。

     1
     2
     3

     .
     .
     .


     9
    10
    11

It is usual to start counting the digits required from the start room number to the end room number in a left to right fashion, so for the above we have one 1, one 2, one 3 ... one 9, two 1's one zero, four 1's etc. Most solutions I have seen used this approach with some optimisation to speed it up.

通常开始计算从起始房间号到结束房间号所需的数字,从左到右的方式,所以上面我们有一个1,一个2,一个3…一个9,两个1,一个0,四个1等等,我看到的大多数解决方案都用这个方法来优化它。

What I did was to count vertically in columns, as in hundreds, tens, and units. You know the highest room number so we can calculate how many of each digit there are in the hundreds column via a single division, then recurse and calculate how many in the tens column etc. Then we can subtract the leading zeros if we like.

我所做的是垂直地计算列数,如在百位、十位和单位中。你知道最高的房间号,这样我们就可以计算出每一个数字有多少是通过一个除法来计算的,然后递归算出十位上的数,然后我们可以减去前导零。

Easier to visualize if you use Excel to write out the numbers but use a separate column for each digit of the number

如果你用Excel来写数字,但要用一个单独的列来表示数字的每一个数字,就更容易理解了。

     A    B    C
     -    -    -
     0    0    1  (assuming room numbers do not start at zero)
     0    0    2
     0    0    3
     .
     .
     .
     3    6    4
     3    6    5
     .
     .
     .

     6    6    9
     6    7    0
     6    7    1

     ^
     sum in columns not rows

So if the highest room number is 671 the hundreds column will have 100 zeroes vertically, followed by 100 ones and so on up to 71 sixes, ignore 100 of the zeroes if required as we know these are all leading.

所以,如果最高的房间号是671,那么几百列就会垂直地有100个0,然后是100个0,那么就有71个6,如果需要的话,忽略100个0,因为我们知道这些都是领先的。

Then recurse down to the tens and perform the same operation, we know there will be 10 zeroes followed by 10 ones etc, repeated six times, then the final time down to 2 sevens. Again can ignore the first 10 zeroes as we know they are leading. Finally of course do the units, ignoring the first zero as required.

然后递归到十位上,执行相同的操作,我们知道会有10个0,然后是10个,重复6次,然后最后一次降到2个7。同样可以忽略前10个0,因为我们知道它们是领先的。最后当然是单位,忽略第一个0。

So there are no loops everything is calculated with division. I use recursion for travelling "up" the columns until the max one is reached (in this case hundreds) and then back down totalling as it goes.

所以没有循环,所有的都是用除法计算的。我使用递归来“向上”行“向上”的列,直到到达最大的列(在这个例子中是数百),然后按它的方式返回。

I wrote this in C# and can post code if anyone interested, haven't done any benchmark timings but it is essentially instant for values up to 10^18 rooms.

我写这个在c#和邮政编码如果有人感兴趣,没做任何基准时间但它本质上是即时值10 ^ 18个房间。

Could not find this approach mentioned here or elsewhere so thought it might be useful for someone.

在这里或其他地方找不到这种方法,所以认为它可能对某些人有用。

#8


1  

This doesn't answer your exact question, but it's interesting to note the distribution of first digits according to Benford's Law. For example, if you choose a set of numbers at random, 30% of them will start with "1", which is somewhat counter-intuitive.

这并不能回答你的问题,但有趣的是,根据本福德定律,第一个数字的分布是很有趣的。例如,如果你随机选择一组数字,其中的30%将以“1”开头,这有点违反直觉。

I don't know of any distributions describing subsequent digits, but you might be able to determine this empirically and come up with a simple formula for computing an approximate number of digits required for any range of numbers.

我不知道有什么分布描述了后来的数字,但是你可能可以根据经验来确定这一点,并给出一个简单的公式来计算任意数量的数字所需的近似数字。

#9


1  

If "better" means "clearer," then I doubt it. If it means "faster," then yes, but I wouldn't use a faster algorithm in place of a clearer one without a compelling need.

如果“更好”的意思是“更清楚”,那我就怀疑了。如果它的意思是“更快”,那么是的,但是我不会使用一个更快的算法来代替一个更清晰的算法,而不需要一个令人信服的需求。

#!/usr/bin/ruby1.8

def digits_for_range(min, max, leading_zeros)
  bins = [0] * 10
  format = [
    '%',
    ('0' if leading_zeros),
    max.to_s.size,
    'd',
  ].compact.join
  (min..max).each do |i|
    s = format % i
    for digit in s.scan(/./)
      bins[digit.to_i] +=1  unless digit == ' '
    end
  end
  bins
end

p digits_for_range(1, 49, false) 
# => [4, 15, 15, 15, 15, 5, 5, 5, 5, 5]

p digits_for_range(1, 49, true)
# => [13, 15, 15, 15, 15, 5, 5, 5, 5, 5]

p digits_for_range(1, 10000, false)
# => [2893, 4001, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000]

Ruby 1.8, a language known to be "dog slow," runs the above code in 0.135 seconds. That includes loading the interpreter. Don't give up an obvious algorithm unless you need more speed.

Ruby 1.8,一种被称为“dog slow”的语言,在0.135秒内运行上述代码。这包括加载解释器。除非你需要更多的速度,否则不要放弃一个明显的算法。

#10


1  

If you need raw speed over many iterations, try a lookup table:

如果在许多迭代中需要原始速度,可以尝试查找表:

  1. Build an array with 2 dimensions: 10 x max-house-number
  2. 构建一个包含两个维度的数组:10 x max-house-number。

    int nDigits[10000][10] ;   // Don't try this on the stack, kids!
  1. Fill each row with the count of digits required to get to that number from zero.
    Hint: Use the previous row as a start:
  2. 在每一行中填入从0到这个数字所需的位数。提示:使用前一行作为开始:

    n=0..9999:
       if (n>0) nDigits[n] = nDigits[n-1]
       d=0..9:
           nDigits[n][d] += countOccurrencesOf(n,d)   // 
  1. Number of digits "between" two numbers becomes simple subtraction.
  2. “在”两个数字之间的数字的数目变成了简单的减法。

       For range=51 to 300, take the counts for 300 and subtract the counts for 50.
       0's = nDigits[300][0] - nDigits[50][0]
       1's = nDigits[300][1] - nDigits[50][1]
       2's = nDigits[300][2] - nDigits[50][2]
       3's = nDigits[300][3] - nDigits[50][3]
       etc.

#11


0  

You can separate each digit (look here for a example), create a histogram with entries from 0..9 (which will count how many digits appeared in a number) and multiply by the number of 'numbers' asked.

您可以将每个数字分开(看这里的例子),创建一个带有从0到的条目的直方图。9(它会计算一个数字中出现了多少个数字),然后乘以“数字”的个数。

But if isn't what you are looking for, can you give a better example?

但如果不是你想要的,你能举一个更好的例子吗?

Edited:

编辑:

Now I think I got the problem. I think you can reckon this (pseudo C):

现在我想我有问题了。我认为你可以估计这个(伪C):

int histogram[10];
memset(histogram, 0, sizeof(histogram));

for(i = startNumber; i <= endNumber; ++i)
{
    array = separateDigits(i);
    for(j = 0; k 

Separate digits implements the function in the link.

单独的数字在链接中实现函数。

Each position of the histogram will have the amount of each digit. For example

直方图的每个位置都有每个数字的数量。例如

histogram[0] == total of zeros
histogram[1] == total of ones

...

Regards

问候


推荐阅读
  • 网络请求模块选择——axios框架的基本使用和封装
    本文介绍了选择网络请求模块axios的原因,以及axios框架的基本使用和封装方法。包括发送并发请求的演示,全局配置的设置,创建axios实例的方法,拦截器的使用,以及如何封装和请求响应劫持等内容。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • 如何在php中将mysql查询结果赋值给变量
    本文介绍了在php中将mysql查询结果赋值给变量的方法,包括从mysql表中查询count(学号)并赋值给一个变量,以及如何将sql中查询单条结果赋值给php页面的一个变量。同时还讨论了php调用mysql查询结果到变量的方法,并提供了示例代码。 ... [详细]
  • Redis底层数据结构之压缩列表的介绍及实现原理
    本文介绍了Redis底层数据结构之压缩列表的概念、实现原理以及使用场景。压缩列表是Redis为了节约内存而开发的一种顺序数据结构,由特殊编码的连续内存块组成。文章详细解释了压缩列表的构成和各个属性的含义,以及如何通过指针来计算表尾节点的地址。压缩列表适用于列表键和哈希键中只包含少量小整数值和短字符串的情况。通过使用压缩列表,可以有效减少内存占用,提升Redis的性能。 ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
  • Ihaveaworkfolderdirectory.我有一个工作文件夹目录。holderDir.glob(*)>holder[ProjectOne, ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
author-avatar
手机用户2502905615
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有