一个明显的解决方案是:
int n = 2134;
while(n > 9)
n /= 10;
这需要线性时间。我们能做得更快吗?
这是否比线性时间快:
char s[100];
sprintf(s, "%d", n);
n = s[0]-'0';
还有哪些其他方式(效率是主要关注点)?
我见过这个,除了我只需要找到第一个数字。(另外,我不明白答案)。
一些处理器具有计算“有多大”数字的指令,非常快(参见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;
}
例如对于 32 位无符号:
第 1 步:确定(通过二分查找)该值位于以下哪个区间:
0 .. 9
10 .. 99
100 .. 999
1000 .. 9999
10000 .. 99999
100000 .. 999999
1000000 .. 9999999
10000000 .. 99999999
100000000 .. 999999999
1000000000 .. 4294967295
最多进行 4 次比较
第2步:
比以一个除法计算前导数。
我很确定sprintf
(正如我假设的那样)会明显变慢。您可以进行一些优化以减少除法操作的数量(这是几乎所有处理器上最慢的指令之一)。
所以可以做这样的事情:
while(n > 10000)
n /= 1000;
while(n >= 9)
n /= 10;
当然,如果速度真的很重要的话。
你的第二个例子应该使用sprintf
. 无论如何,由于打印了整个数字,因此它不能更快,因此搜索了所有数字。
链接的问题/答案使用对数的属性:对于多个x
数字,它的以 10 为底的对数介于x
和之间x+1
。但是,由于浮点错误,这种方法在某些情况下并不能真正正常工作。另外,考虑到浮点运算比整数运算要慢的事实。
因此,最简单的解决方案也更快。
您可以在 O(1) 恒定时间内完成此操作,但会消耗大量内存。这是相同的旧时间/内存权衡。
您可以创建一个包含 2^31 个条目(有符号整数)的查找表,每个条目 4 位(您可以使用 4 位以十进制表示形式对数字的第一个数字 1-9 进行编码)。
然后您可以使用 int 访问查找表并获取 O(1) 中的第一个数字。查找表将占用 2^31 * 4 位 -> 1024 MB
这是我能想到的最快的方法...
这是二分搜索的一种变体。就像二进制搜索一样,它是 O(log n)。是否更快取决于您进行整数除法的速度。
if (n >= 100000000)
n /= 100000000
if (n >= 10000)
n /= 10000
if (n >= 100)
n /= 100
if (n >= 10)
n /= 10
该方法很容易扩展到具有更大范围的整数。
你可以这样做:
//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;
}
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);
您的第一个解决方案(假设已知 n >= 0)几乎是最优的,我认为它只能通过使用内联汇编语言来大幅改进。但这只有在处理数百万个这样的数字时才值得。
你的第二个解决方案是——我怎样才能把这个很好地表达出来?——更像是一种 Java 风格的方法:性能?拉迪达,谁在乎...
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),这是不可取的。
另一种解决方案:以 BCD 格式存储所有值,大端。访问第一个半字节以获得第一个数字
埃斯。
Decimal value: 265
BCD format: 0010-0110-0101
首先制作一个保存数字的双变量。然后在循环中将该数字除以10,使该数字连续丢失一位,直到它是一个1位数字。将 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;
}
如果您的号码是 x9x8x7x6x5x4x3x2x1,那么您只需除以 10^8,因此您需要: 找出有多少位数的最佳方法。您可以使用二进制搜索/