查找整数的位数

问题描述 投票:54回答:16

找到正整数位数的最佳方法是什么?

我找到了这3种基本方法:

  • 转换为字符串 String s = new Integer(t).toString(); int len = s.length();
  • for循环 for(long long int temp = number; temp >= 1;) { temp/=10; decimalPlaces++; }
  • 对数计算 qazxsw poi

你可以在大多数语言中计算log10(x)= ln(x)/ ln(10)。

首先我认为字符串方法是最脏的,但我想的越多,我认为这是最快的方法。或者是吗?

algorithm digits counting
16个回答
53
投票

总有这种方法:

digits = floor( log10( number ) ) + 1;

2
投票
1+floor(log(n,10))

2
投票

关于你提出的“确定在给定基数中代表给定数字所需的位数”的三种方法,我实际上并不喜欢它们中的任何一种;我更喜欢下面给出的方法。

你的方法#1(字符串):任何涉及在字符串和数字之间来回转换的东西通常都很慢。

你的方法#2(temp / = 10):这是致命的缺陷,因为它假定x / 10总是意味着“x除以10”。但是在许多编程语言中(例如:C,C ++),如果“x”是整数类型,则“x / 10”表示“整数除法”,这与浮点除法不同,它引入了每次迭代时的舍入误差,它们会累积在递归公式中,例如您的解决方案#2使用。

你的方法#3(日志):它是大数字的错误(至少在C中,也可能是其他语言),因为浮点数据类型往往不如64位整数精确。

因此,我不喜欢所有这三种方法:#1有效,但速度很慢,#2被破坏,而#3对于大数字来说是错误的。相反,我更喜欢这个,适用于从0到大约18.44 quintillion的数字:

import math
def numdigits(n):
    return ( int(math.floor(math.log10(n))) + 1 )

1
投票

把事情简单化:

unsigned NumberOfDigits (uint64_t Number, unsigned Base)
{
   unsigned Digits = 1;
   uint64_t Power  = 1;
   while ( Number / Power >=  Base )
   {
      ++Digits;
      Power *= Base;
   }
   return Digits;
}

1
投票

您可以使用递归解决方案而不是循环,但不知何故类似:

long long int a = 223452355415634664;

int x;
for (x = 1; a >= 10; x++)
{
   a = a / 10;
}

printf("%d", x);

对于长图,图像可能会发生变化 - 根据不同的算法独立测量小数和长数,并根据您的典型输入选择合适的数字。 :)

当然没有什么比开关更好:

@tailrec
def digits (i: Long, carry: Int=1) : Int =  if (i < 10) carry else digits (i/10, carry+1)

digits (8345012978643L)

除了普通的数组:

switch (x) {
  case 0:  case 1:  case 2:  case 3:  case 4:  case 5:  case 6:  case 7:  case 8:  case 9: return 1;
  case 10: case 11: // ...
  case 99: return 2;
  case 100: // you get the point :) 
  default: return 10; // switch only over int
}

有些人会告诉你优化代码大小,但yaknow,过早优化......


1
投票

这是Swift 4中的测量。

算法代码:

   int [] size = {1,1,1,1,1,1,1,1,1,2,2,2,2,2,... };
   int x = 234561798;
   return size [x];

测量代码:

extension Int {
    var numberOfDigits0: Int {
        var currentNumber = self
        var n = 1
        if (currentNumber >= 100000000) {
            n += 8
            currentNumber /= 100000000
        }
        if (currentNumber >= 10000) {
            n += 4
            currentNumber /= 10000
        }
        if (currentNumber >= 100) {
            n += 2
            currentNumber /= 100
        }
        if (currentNumber >= 10) {
            n += 1
        }
        return n
    }

    var numberOfDigits1: Int {
        return String(self).count
    }

    var numberOfDigits2: Int {
        var n = 1
        var currentNumber = self
        while currentNumber > 9 {
            n += 1
            currentNumber /= 10
        }
        return n
    }

}

产量

timeInterval0:1.92149806022644

timeInterval1:0.557608008384705

timeInterval2:2.83262193202972

在此测量基础上,字符串转换是Swift语言的最佳选择。


0
投票

看到@ daniel.sedlacek结果后我很好奇,所以我使用Swift测试了超过10位的数字。我在操场上运行了以下脚本。

var timeInterval0 = Date()
for i in 0...10000 {
    i.numberOfDigits0
}
print("timeInterval0: \(Date().timeIntervalSince(timeInterval0))")

var timeInterval1 = Date()
for i in 0...10000 {
    i.numberOfDigits1
}
print("timeInterval1: \(Date().timeIntervalSince(timeInterval1))")

var timeInterval2 = Date()
for i in 0...10000 {
    i.numberOfDigits2
}
print("timeInterval2: \(Date().timeIntervalSince(timeInterval2))")

Results of 80 elements

楼层0.105069875717163(log10(x)) 对于div 10次迭代,为0.867973804473877


0
投票

let base = [Double(100090000000), Double(100050000), Double(100050000), Double(100000200)] var rar = [Double]() for i in 1...10 { for d in base { let v = d*Double(arc4random_uniform(UInt32(1000000000))) rar.append(v*Double(arc4random_uniform(UInt32(1000000000)))) rar.append(Double(1)*pow(1,Double(i))) } } print(rar) var timeInterval = NSDate().timeIntervalSince1970 for d in rar { floor(log10(d)) } var newTimeInterval = NSDate().timeIntervalSince1970 print(newTimeInterval-timeInterval) timeInterval = NSDate().timeIntervalSince1970 for d in rar { var c = d while c > 10 { c = c/10 } } newTimeInterval = NSDate().timeIntervalSince1970 print(newTimeInterval-timeInterval)

其中x是基数,n是数字。


22
投票

我不知道,答案可能会有所不同,具体取决于您的个人语言的实施方式。

所以,压力测试吧!实施所有三种解决方案在1到1,000,000(或代表解决方案将要运行的数字的其他一些大数字)上运行它们,并计算每个数字所需的时间。

让你的解决方案相互对立,让他们对抗它。像智力角斗士一样。三种算法进入!一种算法离开!


21
投票

那么正确的答案就是测量它 - 但你应该能够猜测转换字符串并通过它们寻找结束标记所涉及的CPU步骤数量

然后想想你的处理器可以做多少FPU操作以及计算单个日志是多么容易。

编辑:在星期一早上浪费更多时间:-)

n = 1;
if ( i >= 100000000 ) { n += 8; i /= 100000000; }
if ( i >= 10000     ) { n += 4; i /= 10000; }
if ( i >= 100       ) { n += 2; i /= 100; }
if ( i >= 10        ) { n += 1; }

高级语言的一个问题是猜测系统在一个看似简单的语句的幕后做了多少工作。强制性的String s = new Integer(t).toString(); int len = s.length();

此语句涉及为字符串分配内存,可能还有一些字符串的临时副本。它必须解析整数并将其数字复制到一个字符串中,如果数字很大,可能需要重新分配和移动现有内存。它可能必须检查一堆区域设置以确定您的国家/地区是使用“,”还是“。”,它可能需要进行一堆unicode转换。 然后找到长度必须扫描整个字符串,再次考虑unicode和任何本地特定设置,如 - 你是右 - >左语言吗?

或者:

Joel link

只是因为这对你在纸上做起来更难,并不意味着它对电脑来说很难!事实上,高性能计算中的一个好规则似乎是 - 如果人类很难(流体动力学,3d渲染)它对计算机来说很容易,而且对于人类来说很容易(面部识别,检测到声音)嘈杂的房间)电脑很难!

你通常可以假设内置数学函数log / sin / cos等 - 已经成为计算机设计50年的重要部分。因此,即使它们没有直接映射到FPU中的硬件功能,您也可以打赌替代实现非常有效。


8
投票

这个算法可能也不错,假设:

  • Number是整数和二进制编码(<<操作便宜)
  • 我们不知道数字边界 digits = floor( log10( number ) ) + 1;

该算法应该具有与提供的for-loop(2)相当的速度,但由于(2位移,加法和减法,而不是除法)而有点快。

对于Log10算法,它只给出你的近似答案(接近真实,但仍然),因为计算Log函数的解析公式具有无限循环而无法精确计算var num = 123456789L; var len = 0; var tmp = 1L; while(tmp < num) { len++; tmp = (tmp << 3) + (tmp << 1); }


6
投票

测试条件

  • 十进制数字系统
  • 正整数
  • 最多10位数
  • 语言:ActionScript 3

结果

数字:[1,10],

没有。运行:1,000,000

随机样本:8777509,40442298,477894,329950,513,91751410,313,3159,131309,2

结果:7,8,6,6,3,8,3,4,6,1

转换为字符串:724ms

对数计算:349毫秒

DIV 10 ITERATION:229ms

手动调节:136ms

注意:作者不会对超过10位数的数字做出任何结论。


脚本

Wiki

2
投票
  • 转换为字符串:这将遍历每个数字,找到映射到当前数字的字符,将字符添加到字符集合。然后获取生成的String对象的长度。将在O(n)中运行n =#digits。
  • for-loop:将执行2个数学运算:将数字除以10并递增计数器。将在O(n)中运行n =#digits。
  • logarithmic:将调用log10和floor,并添加1.看起来像O(1)但我不确定log10或floor功能有多快。我对这类事物的了解因缺乏使用而萎缩,因此这些功能可能存在隐藏的复杂性。

所以我想它归结为:正在查找数字映射的速度比多次数学运算或package { import flash.display.MovieClip; import flash.utils.getTimer; /** * @author Daniel */ public class Digits extends MovieClip { private const NUMBERS : uint = 1000000; private const DIGITS : uint = 10; private var numbers : Array; private var digits : Array; public function Digits() { // ************* NUMBERS ************* numbers = []; for (var i : int = 0; i < NUMBERS; i++) { var number : Number = Math.floor(Math.pow(10, Math.random()*DIGITS)); numbers.push(number); } trace('Max digits: ' + DIGITS + ', count of numbers: ' + NUMBERS); trace('sample: ' + numbers.slice(0, 10)); // ************* CONVERSION TO STRING ************* digits = []; var time : Number = getTimer(); for (var i : int = 0; i < numbers.length; i++) { digits.push(String(numbers[i]).length); } trace('\nCONVERSION TO STRING - time: ' + (getTimer() - time)); trace('sample: ' + digits.slice(0, 10)); // ************* LOGARITMIC CALCULATION ************* digits = []; time = getTimer(); for (var i : int = 0; i < numbers.length; i++) { digits.push(Math.floor( Math.log( numbers[i] ) / Math.log(10) ) + 1); } trace('\nLOGARITMIC CALCULATION - time: ' + (getTimer() - time)); trace('sample: ' + digits.slice(0, 10)); // ************* DIV 10 ITERATION ************* digits = []; time = getTimer(); var digit : uint = 0; for (var i : int = 0; i < numbers.length; i++) { digit = 0; for(var temp : Number = numbers[i]; temp >= 1;) { temp/=10; digit++; } digits.push(digit); } trace('\nDIV 10 ITERATION - time: ' + (getTimer() - time)); trace('sample: ' + digits.slice(0, 10)); // ************* MANUAL CONDITIONING ************* digits = []; time = getTimer(); var digit : uint; for (var i : int = 0; i < numbers.length; i++) { var number : Number = numbers[i]; if (number < 10) digit = 1; else if (number < 100) digit = 2; else if (number < 1000) digit = 3; else if (number < 10000) digit = 4; else if (number < 100000) digit = 5; else if (number < 1000000) digit = 6; else if (number < 10000000) digit = 7; else if (number < 100000000) digit = 8; else if (number < 1000000000) digit = 9; else if (number < 10000000000) digit = 10; digits.push(digit); } trace('\nMANUAL CONDITIONING: ' + (getTimer() - time)); trace('sample: ' + digits.slice(0, 10)); } } } 中发生的任何事情更快?答案可能会有所不同。可能存在字符映射更快的平台,以及进行计算的其他平台更快。另外要记住的是,第一种方法将创建一个新的String对象,该对象仅为获取长度而存在。这可能会比其他两种方法使用更多的内存,但它可能会或可能不重要。


2
投票

显然,你可以从竞争中消除方法1,因为它使用的atoi / toString算法类似于方法2。

方法3的速度取决于是否正在为其指令集包括日志库10的系统编译代码。


2
投票

使用您使用的任何编程语言中最简单的解决方案。我想不出这样一种情况,即整数计数是任何(有用)程序的瓶颈。

C, C++:

log10

Haskell:

char buffer[32];
int length = sprintf(buffer, "%ld", (long)123456789);

JavaScript:

len = (length . show) 123456789

PHP:

length = String(123456789).length;

Visual Basic (untested):

$length = strlen(123456789);

2
投票

对于非常大的整数,log方法要快得多。例如,如果你需要2491327位数(11920928th斐波纳契数,如果你关心的话),Python需要几分钟来执行除以10的算法,并且执行length = Len(str(123456789)) - 1 需要几毫秒。

© www.soinside.com 2019 - 2024. All rights reserved.