几种计算位数方式的效率对比

发布于 2018-12-25  9 次阅读


平常有些需求是计算一个数的位数,一般在最开始学C语言时也都做过类似的题目。
最近有人问我用循环除十计数好些还是转字符串取长度...不得不说思路清奇,虽然字符串取长度的方式听起来还行,不过毕竟转个字符串,效率太低。
这里整理了三种方式,循环除、转字符串取长度的还有一种取对数的方法。同时把运行效率展现给大家看看,相信大家心里就有个数了。
测试语言用的java,因为正好在做java就顺手写了几段代码:

// 字符串
public static long getLengthByString(long l){
    int len = ("" + l).length();
    return len;
}

// 循环除
public static long getLengthByLoop(long l){
    int len = 1;
    long temp = l;
    while (temp > 10){
        temp /= 10;
        l += 1;
    }
    return len;
}

// 取对数
public static int getLengthByLog(long l){
    int len = (int)Math.log10(l) + 1;
    return len;
}

可以看到取对数和字符串的方式写起来比较简单,而由于需要自己循环,因此循环除十的方法代码量稍稍多一丢丢。
然后是测试代码

int count = 100000000;    // 运行次数
long num = 1234567;       // 测试数值
long start;
long time;

start = System.currentTimeMillis();
for(int i=0; i < count; i++){
    TimeHelper.getLengthByString(num);
}
time = System.currentTimeMillis() - start;
logger.info("运行" + count + "次 字符串 耗时 " + time + "ms");

start = System.currentTimeMillis();
for(int i=0; i< count; i++){
    TimeHelper.getLengthByLog(num);
}
time = System.currentTimeMillis() - start;
logger.info("运行" + count + "次 取对数 耗时 " + time + "ms");

start = System.currentTimeMillis();
for(int i=0; i< count; i++){
    TimeHelper.getLengthByLoop(num);
}
time = System.currentTimeMillis() - start;
logger.info("运行" + count + "次 循环除 耗时 " + time + "ms");

然后来,贴上执行1亿次的效率:
第一次测试数值为 123456789 长度9位
执行效率比较
很明显,循环除是最慢的,因为除法本来就比较慢,然后数值较大会多次循环,复杂度较高。操作字符串的方式稍微快那么一点点,而取对数的方法是最快的,效率非常惊人。这也许就是数学的力量吧...

我们再来看看当数值比较小时的情况,第二次测试数值为 1234 长度4位
执行效率比较
可以看出,由于位数较少,循环除十的方式快过了取字符串长度,然而对数法还是甩了他们几条街,效率比字符串方法快了一倍。

我们再来看看更长些的数值,第三次测试数值为 12345678987654321L 整整17位
执行效率比较
可以看到取对数法还是在2s左右,而另外两种方法就惨不忍睹了2333

由此,我们可以得出结论,由于数学力量的加乘,取以10为底的对数然后保留整数位+1的方法是最快的,而且非常稳定。循环除的方式在位数较少时比取字符串的方式稍稍快些(经过我的测试,这个界限是在位数少于7时,如果位数大于等于7,取字符串的方式就会快过循环除)。
当然了,如果你的执行次数不多,用字符串的方法也是可以的,循环除的方式太老旧了,还是推荐使用对数法。
这里我使用的是JAVA,如果你也正好无聊,同时对此感兴趣的也可以用其他语言来尝试一下233
其实一般来说,像这种问题没有什么人去纠结的,毕竟执行一两次不在乎那点性能,但是作为一个代码强迫症,还是能优化尽量优化吧哈哈哈哈。

Comments


来自像素世界的代码家,创造第九艺术!