Найти количество цифр целого числа

каков наилучший способ найти количество цифр положительного целого числа?

Я нашел это 3 основных метода:

  • преобразование в строку

    String s = new Integer(t).toString(); 
    int len = s.length();
    
  • цикл

    for(long long int temp = number; temp >= 1;)
    {
        temp/=10;
        decimalPlaces++;
    } 
    
  • расчет logaritmic

    digits = floor( log10( number ) ) + 1;
    

где вы можете вычислить log10(x) = ln(x) / ln (10) на большинстве языков.

сначала я думал, что метод string грязной, но чем больше я думаю об этом, тем больше я думаю, что это самый быстрый способ. Или нет?

16 ответов


всегда есть такой метод:

n = 1;
if ( i >= 100000000 ) { n += 8; i /= 100000000; }
if ( i >= 10000     ) { n += 4; i /= 10000; }
if ( i >= 100       ) { n += 2; i /= 100; }
if ( i >= 10        ) { n += 1; }

Я не знаю, и ответ может быть разным в зависимости от того, как реализуется ваш индивидуальный язык.

итак, стресс-тест это! Реализуйте все три решения. Запустите их от 1 до 1 000 000 (или какой-либо другой огромный набор чисел, который представляет числа, с которыми будет работать решение) и время, сколько времени каждый из них занимает.

Pit ваши решения друг против друга,и пусть они бороться. Как интеллектуальные гладиаторы. Три алгоритмы вводятся! Один алгоритм уходит!


ну правильный ответ был бы измерить его - но вы должны быть в состоянии сделать предположение о количестве шагов процессора, участвующих в преобразовании строк и проходя через них в поисках конечного маркера

затем подумайте, сколько операций FPU / S может сделать ваш процессор и как легко вычислить один журнал.

edit: тратить больше времени на утро понедельника: -)

String s = new Integer(t).toString(); 
int len = s.length();

одна из проблем с языками высокого уровня гадать, сколько работы система делает за кулисами, по-видимому, простое утверждение. Обязательно Джоул ссылке

этот оператор включает выделение памяти для строки и, возможно, нескольких временных копий строки. Он должен проанализировать целое число и скопировать его цифры в строку, возможно, придется перераспределить и переместить существующую память, если число большое. Возможно, придется проверить кучу настроек локали, чтобы решить, использует ли ваша страна", " или ".", возможно, сделайте кучу преобразований unicode.
Затем поиск длины должен сканировать всю строку, снова учитывая unicode и любые локальные конкретные настройки, такие как - вы находитесь на правом->левом языке?.

кроме того:

digits = floor( log10( number ) ) + 1;

только потому, что это было бы сложнее для вас сделать на бумаге, не означает, что это трудно для компьютера! На самом деле хорошим правилом в высокопроизводительных вычислениях, похоже, было - если что-то трудно для человека (fluid dynamics, 3d рендеринг) это легко для компьютера, и если это легко для человека (распознавание лиц, обнаружение голоса в шумной комнате), это трудно для компьютера!

вы можете вообще предположить, что встроенные математические функции log/sin / cos и т. д. - были важной частью компьютерного дизайна в течение 50 лет. Поэтому, даже если они не сопоставляются непосредственно с аппаратной функцией в FPU, вы можете поспорить, что альтернативная реализация довольно эффективна.


этот алгоритм также может быть хорош, если предположить, что:

  • число целочисленно и двоично закодировано (
  • мы не знаем числовых границ

    var num = 123456789L;
    var len = 0;
    var tmp = 1L;
    while(tmp < num)
    {
        len++;
        tmp = (tmp << 3) + (tmp << 1);
    }
    

этот алгоритм должен иметь скорость, сопоставимую с for-loop (2), но немного быстрее из-за (2 бит-сдвига, сложение и вычитание вместо деления).

что касается алгоритма Log10, он даст вам только приблизительный ответ (то есть близко к реальному, но все же), так как аналитическая формула для вычисления Лог-функции имеет бесконечный цикл и не может быть вычислена точно Wiki.


условия испытаний

  • десятичная система счисления
  • положительные целые числа
  • до 10 цифр
  • Язык: ActionScript 3

результаты

цифр: [1,10],

нет. работает: 1,000,000

случайная выборка: 8777509,40442298,477894,329950,513,91751410,313,3159,131309,2

результат: 7,8,6,6,3,8,3,4,6,1

преобразование в строку: 724ms

LOGARITMIC расчета: 349ms

div 10 итерация: 229ms

ручное кондиционирование: 136ms

Примечание: автор воздерживается от каких-либо выводов для числа с более чем 10 цифр.


скрипт

package {
    import flash.display.MovieClip;
    import flash.utils.getTimer;
    /**
     * @author Daniel
     */
    public class Digits extends MovieClip {
        private const NUMBERS : uint = 1000000;
        private const DIGITS : uint = 10;

        private var numbers : Array;
        private var digits : Array;

        public function Digits() {
            // ************* NUMBERS *************
            numbers = [];
            for (var i : int = 0; i < NUMBERS; i++) {
                var number : Number = Math.floor(Math.pow(10, Math.random()*DIGITS));
                numbers.push(number);
            }   
            trace('Max digits: ' + DIGITS + ', count of numbers: ' + NUMBERS);
            trace('sample: ' + numbers.slice(0, 10));

            // ************* CONVERSION TO STRING *************
            digits = [];
            var time : Number = getTimer();

            for (var i : int = 0; i < numbers.length; i++) {
                digits.push(String(numbers[i]).length);
            }

            trace('\nCONVERSION TO STRING - time: ' + (getTimer() - time));
            trace('sample: ' + digits.slice(0, 10));

            // ************* LOGARITMIC CALCULATION *************
            digits = [];
            time = getTimer();

            for (var i : int = 0; i < numbers.length; i++) {
                digits.push(Math.floor( Math.log( numbers[i] ) / Math.log(10) ) + 1);
            }

            trace('\nLOGARITMIC CALCULATION - time: ' + (getTimer() - time));
            trace('sample: ' + digits.slice(0, 10));

            // ************* DIV 10 ITERATION *************
            digits = [];
            time = getTimer();

            var digit : uint = 0;
            for (var i : int = 0; i < numbers.length; i++) {
                digit = 0;
                for(var temp : Number = numbers[i]; temp >= 1;)
                {
                    temp/=10;
                    digit++;
                } 
                digits.push(digit);
            }

            trace('\nDIV 10 ITERATION - time: ' + (getTimer() - time));
            trace('sample: ' + digits.slice(0, 10));

            // ************* MANUAL CONDITIONING *************
            digits = [];
            time = getTimer();

            var digit : uint;
            for (var i : int = 0; i < numbers.length; i++) {
                var number : Number = numbers[i];
                if (number < 10) digit = 1;
                else if (number < 100) digit = 2;  
                else if (number < 1000) digit = 3;  
                else if (number < 10000) digit = 4;  
                else if (number < 100000) digit = 5;  
                else if (number < 1000000) digit = 6;  
                else if (number < 10000000) digit = 7;  
                else if (number < 100000000) digit = 8;  
                else if (number < 1000000000) digit = 9;  
                else if (number < 10000000000) digit = 10;  
                digits.push(digit);
            }

            trace('\nMANUAL CONDITIONING: ' + (getTimer() - time));
            trace('sample: ' + digits.slice(0, 10));
        }
    }
}

  • преобразование в строку: это придется перебирать каждую цифру, найти символ, который сопоставляется с текущей цифрой, добавить символ в коллекцию символов. Затем получите длину результирующего объекта String. Будет работать в O (n) для n=#цифр.

  • for-loop: выполнит 2 математические операции: деление числа на 10 и увеличение счетчика. Будет работать в O (n) для n=#цифр.

  • логарифмическая: будет вызовите log10 и floor и добавьте 1. Похоже на O (1), но я не совсем уверен, насколько быстры функции log10 или floor. Мои знания о такого рода вещах атрофировались из-за отсутствия использования, так что могло быть скрытый сложность в этих функциях.

поэтому я думаю, что это сводится к: ищет цифровые отображения быстрее, чем несколько математических операций или что-то происходит в log10? Ответ, вероятно, будет различным. Там могут быть платформы, где отображение символов происходит быстрее, а другие, где выполнение вычислений происходит быстрее. Также следует иметь в виду, что первый метод создаст новый строковый объект, который существует только для получения длины. Это, вероятно, будет использовать больше памяти, чем два других метода, но это может или не имеет значения.


вы, очевидно, можете исключить метод 1 из конкурса, потому что алгоритм atoi/toString, который он использует, будет похож на Метод 2.

скорость метода 3 зависит от того, компилируется ли код для системы, набор инструкций которой включает базу журнала 10.


используйте самое простое решение на любом языке программирования, который вы используете. Я не могу придумать случай, когда подсчет цифр в целочисленном будет узким местом в любой (полезной) программе.

C, C++:

char buffer[32];
int length = sprintf(buffer, "%ld", (long)123456789);

Хаскелл:

len = (length . show) 123456789

JavaScript:

length = String(123456789).length;

PHP:

$length = strlen(123456789);

Visual Basic (непроверенный):

length = Len(str(123456789)) - 1

import math
def numdigits(n):
    return ( int(math.floor(math.log10(n))) + 1 )

сохранить его простым:

long long int a = 223452355415634664;

int x;
for (x = 1; a >= 10; x++)
{
   a = a / 10;
}

printf("%d", x);

для очень больших целых чисел метод log работает намного быстрее. Например, с 2491327-значным числом (11920928-е число Фибоначчи, если вам интересно) Python занимает несколько минут, чтобы выполнить алгоритм деления на 10, и миллисекунды для выполнения 1+floor(log(n,10)).


вы можете использовать рекурсивное решение вместо цикла, но как-то похоже:

@tailrec
def digits (i: Long, carry: Int=1) : Int =  if (i < 10) carry else digits (i/10, carry+1)

digits (8345012978643L)

С длинными, изображение может измениться-измерение малых и длинных чисел независимо от различных алгоритмов, и выбрать соответствующий, в зависимости от вашего типичного ввода. :)

конечно, ничто не бьет переключатель:

switch (x) {
  case 0:  case 1:  case 2:  case 3:  case 4:  case 5:  case 6:  case 7:  case 8:  case 9: return 1;
  case 10: case 11: // ...
  case 99: return 2;
  case 100: // you get the point :) 
  default: return 10; // switch only over int
}

кроме массива plain-o:

   int [] size = {1,1,1,1,1,1,1,1,1,2,2,2,2,2,... };
   int x = 234561798;
   return size [x];

некоторые люди скажут вам оптимизировать размер кода, но yaknow, преждевременный оптимизация...


Что касается трех методов, которые вы предлагаете для "определения количества цифр, необходимых для представления данного числа в данной базе", мне не нравится ни один из них; я предпочитаю метод, который я даю ниже.

Re ваш метод #1 (строки): Все, что связано с преобразованием назад и вперед между строками и числами, обычно очень медленно.

Re ваш метод #2 (temp/=10): это фатально неправильно, потому что он предполагает, что x/10 всегда означает "X, разделенный на 10". Но во многих языках программирования (например, C, C++), если "x" является целочисленным типом, то "x/10" означает "целочисленное деление", что не то же самое, что деление с плавающей запятой, и оно вводит ошибки округления на каждой итерации, и они накапливаются в рекурсивной формуле, такой как ваше решение #2.

Re ваш метод #3 (журналы): он глючит Для больших чисел (по крайней мере, в C, и, вероятно, других языках), потому что типы данных с плавающей запятой, как правило, не так точны, как 64-разрядные целое число.

поэтому мне не нравятся все 3 этих метода: #1 работает, но медленно, #2 сломан, а #3 глючит Для больших чисел. Вместо этого я предпочитаю следующее:

unsigned NumberOfDigits (uint64_t Number, unsigned Base)
{
   unsigned Digits = 1;
   uint64_t Power  = 1;
   while ( Number / Power >=  Base )
   {
      ++Digits;
      Power *= Base;
   }
   return Digits;
}

мне было любопытно увидеть @daniel.sedlacek результаты поэтому я сделал некоторые испытания с помощью Swift для чисел, имеющих более 10 цифр. Я запустил следующий сценарий на игровой площадке.

let base = [Double(100090000000), Double(100050000), Double(100050000), Double(100000200)]
var rar = [Double]()
for i in 1...10 {
    for d in base {
        let v = d*Double(arc4random_uniform(UInt32(1000000000)))
        rar.append(v*Double(arc4random_uniform(UInt32(1000000000))))
        rar.append(Double(1)*pow(1,Double(i)))
    }
}

print(rar)

var timeInterval = NSDate().timeIntervalSince1970

for d in rar {
    floor(log10(d))
}

var newTimeInterval = NSDate().timeIntervalSince1970

print(newTimeInterval-timeInterval)


timeInterval = NSDate().timeIntervalSince1970
for d in rar {
    var c = d
    while c > 10 {
        c = c/10
    }
}

newTimeInterval = NSDate().timeIntervalSince1970

print(newTimeInterval-timeInterval)

результаты 80 элементов

0.105069875717163 для пола (log10 (x))
0.867973804473877 для div 10 итераций


log(x,n)-mod(log(x,n),1)+1

где x-основание, а n-число.


вот измерение в Swift 4.

машинный код:

extension Int {
    var numberOfDigits0: Int {
        var currentNumber = self
        var n = 1
        if (currentNumber >= 100000000) {
            n += 8
            currentNumber /= 100000000
        }
        if (currentNumber >= 10000) {
            n += 4
            currentNumber /= 10000
        }
        if (currentNumber >= 100) {
            n += 2
            currentNumber /= 100
        }
        if (currentNumber >= 10) {
            n += 1
        }
        return n
    }

    var numberOfDigits1: Int {
        return String(self).count
    }

    var numberOfDigits2: Int {
        var n = 1
        var currentNumber = self
        while currentNumber > 9 {
            n += 1
            currentNumber /= 10
        }
        return n
    }

}

код измерений:

var timeInterval0 = Date()
for i in 0...10000 {
    i.numberOfDigits0
}
print("timeInterval0: \(Date().timeIntervalSince(timeInterval0))")

var timeInterval1 = Date()
for i in 0...10000 {
    i.numberOfDigits1
}
print("timeInterval1: \(Date().timeIntervalSince(timeInterval1))")

var timeInterval2 = Date()
for i in 0...10000 {
    i.numberOfDigits2
}
print("timeInterval2: \(Date().timeIntervalSince(timeInterval2))")

выход

timeInterval0: 1.92149806022644

timeInterval1: 0,557608008384705

timeInterval2: 2.83262193202972

на основе этого измерения преобразование строк является лучшим вариантом для языка Swift.