非递归方法,用于计算整数范围内数字的出现次数

时间:2015-10-11 21:53:12

标签: java algorithm

我正在研究一个问题来计算十进制表示中0到n之间的2的数量,而不使用递归解决方案。我找到了以下解决方案,我正在调试它,我的问题是:seendigits和语句countof2s += digit * position * pow10_posMinus1seendigits = seendigits + pow10_pos * digit的逻辑含义是什么?如果有人能提供任何见解,那就太棒了。

 public static int count2sI(int num) {
    int countof2s = 0, digit = 0;
    int j = num, seendigits = 0, position = 0, pow10_pos = 1;
    /* maintaining this value instead of calling pow() is an 6x perf
     * gain (48s -> 8s) pow10_posMinus1. maintaining this value
     * instead of calling Numof2s is an 2x perf gain (8s -> 4s).
     * overall > 10x speedup */
    while (j > 0) {
        digit = j % 10;
        int pow10_posMinus1 = pow10_pos / 10;
        countof2s += digit * position * pow10_posMinus1;
        /* we do this if digit <, >, or = 2
         * Digit < 2 implies there are no 2s contributed by this
         * digit.
         * Digit == 2 implies there are 2 * numof2s contributed by
         * the previous position + num of 2s contributed by the
         * presence of this 2 */
        if (digit == 2) {
            countof2s += seendigits + 1;
        }
        /* Digit > 2 implies there are digit * num of 2s by the prev.
         * position + 10^position */
        else if (digit > 2) {
            countof2s += pow10_pos;
        }
        seendigits = seendigits + pow10_pos * digit;
        pow10_pos *= 10;
        position++;
        j = j / 10;
    }
    return (countof2s);
 }

1 个答案:

答案 0 :(得分:1)

数字从右到左处理。对于输入2468,第一次迭代处理数字8,第二次迭代处理4,然后处理4,最后处理2.

变量seendigits跟踪已经处理过的数字的组合值;例如对于输入2468,四次迭代期间的值将为0,8,68和468.当数字等于2时使用该值(见下文)。

重要的是要注意,在处理数字时,例如在示例2468中的4中,已经计算了范围1到68的低位数的二进制数。此计数用于401到468的范围,因此仍需要计算的是1到400范围内较低位数的两位数,以及1到468范围内当前位数的两位数。这个算法分别完成了这两件事。

计算低位数的两位数在此行中完成:

countof2s += digit * position * pow10_posMinus1;  

在第一次迭代中,这会增加0,因为没有较低的数字。在第二次迭代中,这会增加digit * 1 * 1,因为有1个较低的数字,对于当前数字的每个值,它等于2。在第三次迭代中,这会增加digit * 2 * 10,因为有2个较低的数字,它们对于当前数字的每个值都等于2次,依此类推......

对于示例输入2468,此行将添加:
迭代1:数字= 8,加:8 * 0 * 0 = 0
迭代2:数字= 6,加:6 * 1 * 1 = 6
迭代3:数字= 4,加:4 * 2 * 10 = 80
迭代4:数字= 2,加:2 * 3 * 100 = 600

e.g。值80是数字1到400的低位数字中的两个数字:
x02,x12,x22,x32,... x92的最低位数 这些发生4次:2到92次,102到192次,202到292次和302到392次 x20,x21,x22,x23,... x29的第二低位数字 这些发生4次:20到29次,120到129次,220到229次和320到329次。

计算当前数字中的两位数是在以下一行中完成的,具体取决于当前数字是大于还是等于2.

if (digit == 2) { countof2s += seendigits + 1; }
else if (digit > 2) { countof2s += pow10_pos; }

如果当前数字大于2,则从1开始向上计数时数字为2的次数仅取决于数字的位置。对于位置0,这会增加+1,以计算数字2;对于位置1,这增加了+10,对于20到29范围内的最高位数;对于位置2,这会增加+100,对于200到299范围内的最高位,依此类推......

对于示例输入2468,此行将添加:
迭代1:数字= 8,加:1
迭代2:数字= 6,加:10
迭代3:数字= 4,加:100
迭代4:数字= 2,加:0(数字不大于2)

如果当前数字等于2,则数字为2时的数字从1开始向上计数等于低数字+ 1的值。

对于示例输入2468,此行将添加:
迭代1:数字= 8,加:0
迭代2:数字= 6,加:0
迭代3:数字= 4,加:0
迭代4:数字= 2,加:469
这是因为当前数字在2000到2468范围内等于2。

<强>更新

如果您处理从左到右的数字,该算法更容易理解:(javascript中的示例)

&#13;
&#13;
function twosInRange(number) {
    var digits = Math.floor(Math.log(number) / Math.log(10));
    var count = 0;
    for (var pos = digits; pos >= 0; --pos) {     // 3, 2, 1, 0
        var unit = Math.pow(10, pos);             // 1000, 100, 10, 1
        var digit = Math.floor(number / unit);    // 2, 4, 6, 8
        number -= digit * unit;                   // 468, 68, 8, 0
        // COUNT OCCURRENCES IN LOWER DIGITS:
        count += digit * pos * (unit / 10);       // + 2*3*100, 4*2*10, 6*1*1, 8*0*0
        // COUNT OCCURRENCES IN CURRENT DIGIT:
        if (digit > 2) count += unit;             // + (1000), 100, 10, 1
        else if (digit == 2) count += number + 1; // + 469, (69), (9), (1)
    }
    return (count);                               // 600 + 80 + 6 + 100 + 10 + 1 + 469
}

document.write(twosInRange(2468));                // = 1266
&#13;
&#13;
&#13;