有多种解决方案可以找到给定数字中的数字位数。
例如:
方法-1:
int findn(int num)
{
char snum[100];
sprintf(snum, "%d", num);
return strlen(snum);
}
方法-2:
int findn(int num)
{
if (num == 0) return 1;
int n = 0;
while(num) {
num /= 10;
n++;
}
return n;
}
方法-3:
int findn(int num)
{
/* math.h included */
return (int) log10(num) + 1;
}
问题是 - 什么是最有效的方法?我知道方法-2是O(n)
但是方法1和方法3呢?如何找到库函数的运行时复杂性?
答案 0 :(得分:21)
以下效率更高:
int findn(int num)
{
if ( num < 10 )
return 1;
if ( num < 100 )
return 2;
//continue until max int
}
您可以通过二进制搜索进一步优化这一点,但这样做会有点过分。
答案 1 :(得分:11)
目前看来,接受且得到最高度认可的答案(仍然)对于否定数字不正确。如果回答者需要花时间来测试它发现它已被打破了负数,他可能会浪费更多的时间而不是机器只需使用snprintf
,即
int count_digits(int arg) {
return snprintf(NULL, 0, "%d", arg) - (arg < 0);
}
我们不再是20世纪80年代了;像我们一样停止编码。我是一个C级标准狂热者,我在这里给出的最喜欢的答案是Tao Feng's answer ......但即使这样也没有进入为什么它是目前为止最有效的答案;在这个答案中,我打算通过考虑以下内容来表明他的答案可以进一步改善:
snprintf
放回缓存中。以下介绍了妨碍您提高工作效率的微观优化。由于您在答案中提供的信息不足,所以没有人回答当前的问题可以提供任何证明做出以下假设:
您可以通过衡量瓶颈来避免微观优化,即通过分析(“基准测试”)系统上的每个解决方案,假设他们甚至可以正确解决您的问题。如果解决方案无法解决问题,那么它不是解决方案,因此不应该考虑......如果正确完成,这应该消除微优化。有些编译器甚至提供了智能的配置文件引导优化,它通常通过重新组织缓存位置的分支和对象,并自动执行来削减20-30%。
我已经涵盖了计数位数,我认为这肯定会回答你的问题,但有些情况下你认为你需要在不时计算数字,并且能够消除计算数字的开销,这可能是非常理想的优化,无论是在工作时间的和工时。
例如,如果要计算要分配的字节数以存储包含这些数字的字符串,则不应使用任何运行时,因为预处理器宏可用于计算最大位数(或您尝试保存的临时存储空间中的任何字符都将被逻辑中添加的机器代码字节数远远超过我,这对我来说似乎是一笔费用。程序员使用预处理器宏也有好处;相同的宏可用于任何整数类型。 有关此问题的解决方案,请参阅my answer至this question ;毕竟,没有必要重复自己...
答案 2 :(得分:10)
GCC / Clang __builtin_clz()
或Microsoft Visual C _BitScanReverse()
内在函数在许多机器上编译为单个机器指令。您可以将其用作O(1)解决方案的基础。这是一个32位的实现:
#include <limits.h>
#include <stdint.h>
/* Return the number of digits in the decimal representation of n. */
unsigned digits(uint32_t n) {
static uint32_t powers[10] = {
0, 10, 100, 1000, 10000, 100000, 1000000,
10000000, 100000000, 1000000000,
};
static unsigned maxdigits[33] = {
1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5,
5, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
};
unsigned bits = sizeof(n) * CHAR_BIT - __builtin_clz(n);
unsigned digits = maxdigits[bits];
if (n < powers[digits - 1]) {
-- digits;
}
return digits;
}
答案 3 :(得分:7)
我想也许你可以把第一种方法写成
int findn(int num)
{
char snum[100];
return sprintf(snum, "%d", num);
}
因为sprintf将返回写入的字符数,您可以将调用保存到strlen。
至于效率,我认为这取决于sprintf的实现,你可能需要找到sprintf的来源,看看它是否有效。
答案 4 :(得分:3)
尝试二分搜索。为了清楚起见,我们假设有符号的32位整数。首先,检查是否x < 10000
。接下来,取决于答案,x < 100
或x < 1000000
,依此类推。
那是O(log n)
,其中n
是位数。
答案 5 :(得分:3)
这些函数对非正数给出截然不同的结果(最差的是方法3),因此比较它们的时间复杂度具有可疑的价值。我会使用能够在所有情况下提供答案的那个;没有上下文,我们无法知道它是什么(它可能不是方法3)。
方法1,findn(0) == 1
和findn(-n) == digits in n + 1
(由于负号)。
对于方法2,findn(0) == 0
和findn(-n) == digits in n
。
对于方法3,findn(0) == INT_MIN
和findn(-n) == INT_MIN
也是如此。
答案 6 :(得分:3)
一个班轮:for(digits = 0; num > 0; digits++) num /= 10;
答案 7 :(得分:2)
我认为sprintf()
将使用您的方法2 来打印数字(以确定要打印的字符串的长度,然后打印字符串的每个字符),所以它本来就会慢一些。
3号可能涉及ln()
的一些多项式近似,它将涉及更多的1个除法,所以我猜它也会慢一些(here's a fast ln ()实现,仍然涉及浮动划分......所以WAY慢一些。)
所以我的初步猜测是,方法2是要走的路。
请注意,这是解决此问题的一种非常自由的方式。我想用每个函数测试一个很好的旧定时百万次迭代会告诉你结果。但它太过于暴力,不是吗?
请注意,只有方法2会给你真实的结果,其他方法的缺陷必须调整到正确(参见亚伦的回答)。所以只需选择方法2.
答案 8 :(得分:1)
这是我的解决方案...... 它会计数到100位数,或者你知道你想要它的数量。
max_digits = 100
int numdigits(int x) {
for (int i = 1; i < max_digits; i++) {
if (x < pow(10, i)) {
return i;
}
}
return 0;
}
答案 9 :(得分:1)
printf函数将返回成功打印的位数。
int digits,n=898392;
digits=printf("%d",n);
printf("Number of digits:%d",digits);
<强>输出:强>
898392
位数:6
答案 10 :(得分:0)
使用日志可能是个不错的选择......
int
可以投放到double
并且不会丢失精确度。示例实施......
int num_digits(int arg) {
if (arg == 0) {
return 1;
}
arg = abs(arg);
return (int)log10(arg)+1;
}
答案 11 :(得分:0)
log10(x) + 1
将计算x > 0
的位数,但可能需要另一种方法:
log2(x) = size_of(x) - 1 - clz(x)
将计算log2为整数。log_b(x) = log2(x) / log2(b)
将进行基本转换。log2(10)
除数变为以10为底的3
。log2(x)
的分红变为31 - clz(x)
。您甚至可以以此来计算十六进制数字或任何其他基数。
uint32_t count_dec_digits(uint32_t n) {
return 1 + (31 - __builtin_clz(n)) / 3;
}
uint32_t count_hex_digits(uint32_t n) {
return 1 + (31 - __builtin_clz(n)) / 4;
}
int main() {
printf("%d", count_dec_digits(1234567)); // 7
printf("%d", count_hex_digits(0x12345)); // 5
}
答案 12 :(得分:0)
@AShelly 建议的函数变体,用于有符号和无符号 64 位数字,并检查 0(https://godbolt.org/z/cqeKEj4rj,还包含一些测试代码)。
uint8_t digits_u64(uint64_t number) {
static uint8_t maxdigits[] = {
1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6,
7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11,
12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, 15,
16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20,
};
static uint64_t powers[] = {
0U, 1U, 10U, 100U, 1000U, 10000U, 100000U, 1000000U, 10000000U,
100000000U, 1000000000U, 10000000000U, 100000000000U,
1000000000000U, 10000000000000U, 100000000000000U,
1000000000000000U, 10000000000000000U, 100000000000000000U,
1000000000000000000U, 10000000000000000000U,
};
if (number == 0) {
return 1;
}
unsigned bits = sizeof(number) * CHAR_BIT - __builtin_clzll(number);
unsigned digits = maxdigits[bits];
if (number < powers[digits]) {
--digits;
}
return digits;
}
uint8_t digits_i64(int64_t number) {
int add_minus_sign = number < 0U ? 1 : 0;
if (add_minus_sign) {
number *= -1;
}
return digits_u64(number) + add_minus_sign;
}