Количество цифр - какой метод наиболее эффективен?
Существует более одного решения для поиска числа цифр в заданном числе.
Например:
Способ-1:
int findn(int num)
{
char snum[100];
sprintf(snum, "%d", num);
return strlen(snum);
}
Способ-2:
int findn(int num)
{
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? Как найти сложность библиотечных функций во время выполнения?
Ответы
Ответ 1
Ниже приведено более эффективное:
int findn(int num)
{
if ( num < 10 )
return 1;
if ( num < 100 )
return 2;
//continue until max int
}
Вы можете оптимизировать это еще дальше, выполнив двоичный поиск, но это будет излишним.
Ответ 2
Встроенные функции 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
Я думаю, может быть, вы можете написать первый метод как
int findn(int num)
{
char snum[100];
return sprintf(snum, "%d", num);
}
потому что sprintf вернет количество написанных символов, и вы можете сохранить вызов strlen.
Что касается эффективности, я думаю, что это зависит от реализации sprintf, вам может понадобиться найти источник sprintf и посмотреть, насколько эффективно это делать.
Ответ 4
Как и в настоящее время, принятый и наиболее одобренный ответ (по-прежнему) неверен для отрицательных чисел.. Если ответчик должен был потратить время на его проверку и выяснить, чисел, он, вероятно, потратил бы больше времени, чем машина, когда-либо используя просто snprintf
, т.е.
int count_digits(int arg) {
return snprintf(NULL, 0, "%d", arg) - (arg < 0);
}
Мы больше не в 1980-х годах; прекратите кодирование, как если бы мы были. Я фанат C-стандарта, и мой любимый ответ здесь был Дао Фэн ответил... но даже это не входило в то, почему это самый эффективный ответ, далеко; в этом ответе я намерен показать, что его ответ можно улучшить, рассмотрев следующее:
- Производительность программного обеспечения важнее эффективности кода,, потому что почти наверняка потребуется больше времени для написания и тестирования новых функций, чем это будет в течение нескольких микросекунд времени выполнения.
- Повторное использование тех же стандартных функций библиотеки, которые обычно используют (возможно) другие стандартные библиотеки в кэше ЦП. Пропустить кеш (например, когда ваш код необходимо скопировать из ОЗУ в CPU) может стоить до 50 инструкций процессора, не говоря уже о том, что другой код может привести к тому, что другой кеш-код не сможет поместить
snprintf
обратно в кеш в любом случае.
- Устранение требований к хранению может вызвать дополнительные оптимизации.
Ниже описывается микро-оптимизация, которая препятствует вашей производительности. Из-за отсутствия информации, которую вы предоставили в своем ответе, никто, кто отвечает на вопрос, который сейчас стоит, может предоставить любые доказательства без делая предположения о:
- Когда мы оптимизируем, нам нужно найти наиболее существенное узкое место в полном решении (проблема, которую ваша программа должна решить). Здесь есть две возможности: A) Вы хотите рассчитать количество байтов для размещения, чтобы сохранить строку, содержащую эти цифры; B) Вы просто хотите подсчитать количество цифр или что угодно для пинков. Подробнее об этом позже. На данный момент важно понять, что вы, вероятно, говорите о части решения, и эта часть может быть не самым значительным узким местом.
- Используемый вами компилятор, используемая вами ОС и используемая вами машина (включая скорость RAM, поскольку некоторые из нас представляют потенциальные промахи в кэше, на которые больше влияет медленная память, чем на быструю память) влияют на наиболее значительное узкое место. Некоторые компиляторы отличаются от других, и оптимизируют некоторые части кода лучше для некоторых ОС, процессоров и т.д., Чем другие.
Вы можете избежать микрооптимизации путем измерения узких мест, то есть путем профилирования ( "бенчмаркинга" ) каждого из этих решений в вашей системе, предполагая, что они даже правильно решают ваши проблемы. Если решение не решает проблему, это не решение, поэтому его не следует рассматривать... Когда это делается правильно, это должно устранить микро-оптимизацию. Некоторые компиляторы даже обеспечивают интеллектуальную оптимизацию с учетом профиля, которая обычно бреет 20-30% путем реорганизации ветвей и объектов для локализации кэша, и делает это автоматически.
Я уже рассмотрел вариант B, который, я думаю, наиболее точно отвечает на ваш вопрос, но бывают случаи, когда опция A представляет собой очень желательную оптимизацию, как в человеко-часы, так и в машинные часы.
Если вы хотите вычислить количество байтов для размещения, чтобы сохранить строку, содержащую эти цифры, вы не должны использовать какое-либо время выполнения, потому что макрос препроцессора может использоваться для вычисления максимального количества цифр (или символов, включая знак), и любые драгоценные байты временного хранилища, которые вы пытаетесь сохранить, будут значительно превосходить численные байты машинного кода, добавленные в логику, что кажется мне крутой ценой. Также для программиста полезно использовать макрос препроцессора; один и тот же макрос можно использовать для любого целочисленного типа. Посмотрите мой ответ на этот вопрос для решения этой проблемы; в конце концов, нет смысла повторять себя...
Ответ 5
Попробуйте двоичный поиск. Для большей ясности предположим, что мы подписали 32-битные целые числа. Сначала проверьте, есть ли x < 10000
. Далее, в зависимости от ответа, если x < 100
или x < 1000000
и т.д.
Это O(log n)
, где n
- количество цифр.
Ответ 6
Я думаю, что sprintf()
будет использовать ваш метод 2 для печати номера (для определения длины строки для печати, а затем для печати каждого символа строки), поэтому она будет по своей сути медленнее.
Число 3, вероятно, будет включать в себя некоторую полиномиальную аппроксимацию ln()
, в которой будет задействовано более 1 деление, поэтому я думаю, что он будет медленнее (здесь быстрая реализация ln(), по-прежнему включающая float-деление... так что WAY медленнее).
Итак, моя предварительная догадка заключается в том, что метод 2 - это путь.
Обратите внимание, что это довольно либеральный подход к решению этой проблемы. Я думаю, что тестирование хорошей старой истребительной миллионной итерации с каждой функцией скажет вам результат. Но это было бы слишком грубо, не так ли?
Обратите внимание, что только метод 2 даст вам реальные результаты, другие имеют недостатки, которые необходимо скорректировать, чтобы быть правильными (см. ответ Аарона). Поэтому просто выберите метод 2.
Ответ 7
Эти функции дают совершенно разные результаты для неположительных чисел (худшим является метод 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
.
Ответ 8
Один вкладыш: for(digits = 0; num > 0; digits++) num /= 10;
Ответ 9
Это мое решение...
он будет считать до 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;
}
Ответ 10
Обратите внимание:
int findn(int num)
{
int n = 0;
while(num) {
num /= 10;
n++;
}
return n;
}
и
for(digits = 0; num > 0; digits++) num /= 10;
не будет работать для числа ноль, потому что ноль имеет длину в одну цифру.
Ответ 11
Функция printf вернет число успешно напечатанных цифр.
int digits,n=898392;
digits=printf("%d",n);
printf("Number of digits:%d",digits);
Вывод:
898392
Число цифр: 6
Ответ 12
Использование журнала может быть хорошим вариантом...
- Если целевая машина поддерживает аппаратное обеспечение
- Если вы уверены, что
int
может быть переведен в double
и обратно без потери точности.
Пример реализации...
int num_digits(int arg) {
if (arg == 0) {
return 1;
}
arg = abs(arg);
return (int)log10(arg)+1;
}