一个明显的解决方案是:
int n = 2134;
while(n > 9)
n /= 10;
需要线性时间。我们可以做得更快吗?
这是否比线性时间更快:
char s[100];
sprintf(s, "%d", n);
n = s[0]-'0';
其他方式(效率是主要关注点)是什么? 我见过this,但我只需找到第一个数字。 (另外,我不明白答案)。
答案 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对数介于x
和x+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;
}