如何有效地检索数字的第一个十进制数字

时间:2013-06-30 18:53:59

标签: c++ c algorithm

一个明显的解决方案是:

int n = 2134;
while(n > 9)
    n /= 10;

需要线性时间。我们可以做得更快吗?

这是否比线性时间更快:

char s[100];
sprintf(s, "%d", n);
n = s[0]-'0';

其他方式(效率是主要关注点)是什么? 我见过this,但我只需找到第一个数字。 (另外,我不明白答案)。

13 个答案:

答案 0 :(得分:21)

某些处理器有指令来计算"有多大"一个数字非常快(见http://en.wikipedia.org/wiki/Leading_zero_count)。这可以用来快速选择10的幂,除以它,而不是重复除以10。

假设您有一个函数clz,它计算数字二进制表示(0 ... 32)中前导零位的数量。然后,您可以使用一个查找表,为每个前导零数提供10的正确功率。

uint32_t powers_of_10[33] = {
    1000000000, 1000000000,
    100000000, 100000000, 100000000,
    10000000, 10000000, 10000000,
    1000000, 1000000, 1000000, 1000000,
    100000, 100000, 100000,
    10000, 10000, 10000,
    1000, 1000, 1000, 1000,
    100, 100, 100,
    10, 10, 10,
    1, 1, 1, 1, 1
};

int CalcFirstDecimalDigit(uint32_t x)
{
    int leading_zeros = clz(x);
    x /= powers_of_10[leading_zeros];
    if (x >= 10)
        return 1;
    else
        return x;
}

答案 1 :(得分:13)

e.g。 for 32 bit unsigned:

步骤1:确定(通过二分搜索)以下哪个时间间隔值:

0 .. 9
10 .. 99
100 .. 999
1000 .. 9999
10000 .. 99999
100000 .. 999999
1000000 .. 9999999
10000000 .. 99999999
100000000 .. 999999999
1000000000 .. 4294967295

需要最多4次比较

第2步:

比一个师计算前导数字。

答案 2 :(得分:5)

您的第二个示例应使用sprintf。无论如何,由于打印整个数字,它不能更快​​,因此搜索所有数字。

链接的问题/答案使用对数属性:对于多个x数字,它的基数10对数介于xx+1之间。但是,由于浮点错误,此方法在某些情况下无法正常工作。另外,考虑到浮点数比进行整数运算要慢。

因此,最简单的解决方案也更快。

答案 3 :(得分:5)

我很确定sprintf(我认为是)将会非常慢。您可以进行一些优化以减少除法运算的数量(这是几乎所有处理器上最慢的指令之一)。

所以可以这样做:

 while(n > 10000)
   n /= 1000;

 while(n >= 9)
   n /= 10;

当然,如果速度非常重要的话。

答案 4 :(得分:5)

你可以在O(1)恒定时间内完成它,但代价是非常大的内存使用量。这是相同的旧时间/记忆权衡。

您可以创建一个包含2 ^ 31个条目(signed int)的查找表,每个条目4位(4位可以编码十进制表示中数字的第一个数字1-9)。

然后您可以使用int来访问查找表并获取O(1)中的第一个数字。 查找表将采用2 ^ 31 * 4位 - > 1024 MB

这是我能想到的最快的方式...

答案 5 :(得分:3)

这是二元搜索的一种变体。像二进制搜索一样,它是O(log n)。它是否更快将取决于你可以多快地进行整数除法。

if (n >= 100000000)
    n /= 100000000
if (n >= 10000)
    n /= 10000
if (n >= 100)
    n /= 100
if (n >= 10)
    n /= 10

对于范围较大的整数,该方法很容易扩展。

答案 6 :(得分:2)

你可以做到这一点:

//Shashank Jain
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
    int num,fdigit;
    cin>>num;
    if(num<0)
        num*=-1;
    int l=log10(num); // l = (length of number -1)

    fdigit=num/pow(10,l);

    cout<<fdigit<<endl;
    return 0;
}

答案 7 :(得分:1)

int FirstDigit ( int Number ) {

    // to obtain the <number of digits -1> use this math formula:

    int DigitsInNumber = (int) lg(Number);           

    long TenExpon = pow(10, DigitsInNumber);

    return (Number / TenExpon);                //first digit
}

还:lg(n) = ln(n) / ln(10);

答案 8 :(得分:0)

你的第一个解决方案(假设n已知为> = 0)几乎是最优的,我认为只能通过使用内联汇编语言来大幅提高。但是,如果您处理数百万这样的数字,那将是值得的。

你的第二个解决方案是 - 我怎么能很好地把这个? - 更多的Java-ish方法:性能? La-di-da,谁在乎...

答案 9 :(得分:0)

    for(int i=0; i<n; i++)
    {  
        e=5; //Specify the number of digits in the number OR Exponential value of 10
        while(e>=0)
        {   
            tenp=pow(10,e); //#include <math.h>
            if(arr[i]/tenp!=0)
            {
                q[i][e]=arr[i]/tenp%10;
            }
            e--;
        }
    }

但是,此代码的复杂度应为O(n ^ 2),这是不希望的。

答案 10 :(得分:0)

另一种解决方案: 将所有值以BCD格式存储(大端)。访问第一个半字节以获取第一个数字

ES。

Decimal value: 265
BCD format: 0010-0110-0101

答案 11 :(得分:-1)

如果您的数字是x9x8x7x6x5x4x3x2x1那么你只需要除以10 ^ 8 所以你需要:找到有多少位数的最佳方法。 您可以使用二进制搜索/

答案 12 :(得分:-1)

首先创建一个用于保存数字的double变量。然后将该数字除以10并循环循环,以使该数字连续丢失一位数字,直到它是一位数字。将double变量强制转换为int以将其舍入。结果将是先前的第一个十进制数字。

代码将在O(log(n))中运行。

#include <iostream>

using namespace std;

int main() {
    double a;
    cin >> a;
    while (true) {
        a /= 10;
        if (a < 10) {
            a = int(a);
            cout << a;
            break;
        }
    }
    return 0; 
}