Как вы можете получить первую цифру в int (C #)? - PullRequest
74 голосов
/ 31 марта 2009

В C #, как лучше всего получить 1-ю цифру в int? Метод, который я придумал, состоит в том, чтобы превратить int в строку, найти 1-й символ строки, а затем превратить его обратно в int.

int start = Convert.ToInt32(curr.ToString().Substring(0, 1));

В то время как это делает работу, кажется, что есть, вероятно, хорошее, простое, математическое решение такой проблемы. Манипулирование струнами кажется неуклюжим.

Редактировать: независимо от разницы в скорости, mystring [0] вместо Substring () по-прежнему является просто манипулированием строкой

Ответы [ 25 ]

212 голосов
/ 31 марта 2009

Тесты

Во-первых, вы должны решить, что вы подразумеваете под «лучшим» решением, конечно, которое учитывает эффективность алгоритма, его читаемость / ремонтопригодность и вероятность появления ошибок в будущем. Однако тщательные юнит-тесты обычно позволяют избежать этих проблем.

Я выполнил каждый из этих примеров 10 миллионов раз, и значение результата - это число пройденных ElapsedTicks.

Без лишних слов, от самого медленного до самого быстрого, алгоритмы:

Преобразование в строку, взять первый символ

int firstDigit = (int)(Value.ToString()[0]) - 48;

Результаты:

12,552,893 ticks

Использование логарифма

int firstDigit = (int)(Value / Math.Pow(10, (int)Math.Floor(Math.Log10(Value))));

Результаты:

9,165,089 ticks

Циклический

while (number >= 10)
    number /= 10;

Результаты:

6,001,570 ticks

Conditionals

int firstdigit;
if (Value < 10)
     firstdigit = Value;
else if (Value < 100)
     firstdigit = Value / 10;
else if (Value < 1000)
     firstdigit = Value / 100;
else if (Value < 10000)
     firstdigit = Value / 1000;
else if (Value < 100000)
     firstdigit = Value / 10000;
else if (Value < 1000000)
     firstdigit = Value / 100000;
else if (Value < 10000000)
     firstdigit = Value / 1000000;
else if (Value < 100000000)
     firstdigit = Value / 10000000;
else if (Value < 1000000000)
     firstdigit = Value / 100000000;
else
     firstdigit = Value / 1000000000;

Результаты:

1,421,659 ticks

Развернутая и оптимизированная петля

if (i >= 100000000) i /= 100000000;
if (i >= 10000) i /= 10000;
if (i >= 100) i /= 100;
if (i >= 10) i /= 10;

Результаты:

1,399,788 ticks

Примечание:

каждый тест звонит Random.Next(), чтобы получить следующий int

122 голосов
/ 31 марта 2009

Вот как

int i = Math.Abs(386792);
while(i >= 10)
    i /= 10;

и i будут содержать то, что вам нужно

31 голосов
/ 31 марта 2009

Попробуйте это

public int GetFirstDigit(int number) {
  if ( number < 10 ) {
    return number;
  }
  return GetFirstDigit ( (number - (number % 10)) / 10);
}

РЕДАКТИРОВАТЬ

Несколько человек запросили петлевую версию

public static int GetFirstDigitLoop(int number)
{
    while (number >= 10)
    {
        number = (number - (number % 10)) / 10;
    }
    return number;
}
26 голосов
/ 31 марта 2009

Лучшее, что я могу придумать:

int numberOfDigits = Convert.ToInt32(Math.Floor( Math.Log10( value ) ) );

int firstDigit = value / Math.Pow( 10, numberOfDigits );

...

Не очень красиво :) 1006 *

[Отредактировано: первый ответ был действительно плохим :)]

[Правка 2: Я бы, вероятно, посоветовал бы решения для работы со строками]

[Редактировать 3: форматирование кода это приятно :)]

18 голосов
/ 31 марта 2009

вариант ответа Антона:

 // cut down the number of divisions (assuming i is positive & 32 bits)
if (i >= 100000000) i /= 100000000;
if (i >= 10000) i /= 10000;
if (i >= 100) i /= 100;
if (i >= 10) i /= 10;
5 голосов
/ 31 марта 2009

Имел ту же идею, что и Леннаерт

int start = number == 0 ? 0 : number / (int) Math.Pow(10,Math.Floor(Math.Log10(Math.Abs(number))));

Это также работает с отрицательными числами.

4 голосов
/ 31 марта 2009

Если вы считаете, что ответ Keltex уродлив, попробуйте этот, он ДЕЙСТВИТЕЛЬНО уродлив и даже быстрее. Это делает развернутый бинарный поиск, чтобы определить длину.

 ... leading code along the same lines
/* i<10000 */
if (i >= 100){
  if (i >= 1000){
    return i/1000;
  }
  else /* i<1000 */{
    return i/100;
  }
}
else /* i<100*/ {
  if (i >= 10){
    return i/10;
  }
  else /* i<10 */{
    return i;
  }
}

P.S. У MartinStettner была та же идея.

3 голосов
/ 31 марта 2009

Я знаю, что это не C #, но удивительно любопытно, что в python «получить первый символ строкового представления числа» быстрее!

РЕДАКТИРОВАТЬ : нет, я сделал ошибку, я забыл снова создать int, извините. Развернутая версия - самая быстрая.

$ cat first_digit.py
def loop(n):
    while n >= 10:
        n /= 10
    return n

def unrolled(n):
    while n >= 100000000: # yea... unlimited size int supported :)
        n /= 100000000
    if n >= 10000:
        n /= 10000
    if n >= 100:
        n /= 100
    if n >= 10:
        n /= 10
    return n

def string(n):
    return int(str(n)[0])
$ python -mtimeit -s 'from first_digit import loop as test' \
    'for n in xrange(0, 100000000, 1000): test(n)'
10 loops, best of 3: 275 msec per loop
$ python -mtimeit -s 'from first_digit import unrolled as test' \
    'for n in xrange(0, 100000000, 1000): test(n)'
10 loops, best of 3: 149 msec per loop
$ python -mtimeit -s 'from first_digit import string as test' \
    'for n in xrange(0, 100000000, 1000): test(n)'
10 loops, best of 3: 284 msec per loop
$
3 голосов
/ 02 июня 2011

Я просто наткнулся на этот старый вопрос и был склонен предложить другое предложение, поскольку ни один из других ответов пока не возвращает правильный результат для всех возможных входных значений, и он все еще сделать быстрее:

public static int GetFirstDigit( int i )
{
    if( i < 0 && ( i = -i ) < 0 ) return 2;
    return ( i < 100 ) ? ( i < 1 ) ? 0 : ( i < 10 )
            ? i : i / 10 : ( i < 1000000 ) ? ( i < 10000 )
            ? ( i < 1000 ) ? i / 100 : i / 1000 : ( i < 100000 )
            ? i / 10000 : i / 100000 : ( i < 100000000 )
            ? ( i < 10000000 ) ? i / 1000000 : i / 10000000
            : ( i < 1000000000 ) ? i / 100000000 : i / 1000000000;
}

Это работает для всех целочисленных значений со знаком, включая -2147483648, которое является наименьшим целым числом со знаком и не имеет положительного аналога. Math.Abs( -2147483648 ) запускает System.OverflowException, а - -2147483648 вычисляет до -2147483648.

Реализация может рассматриваться как комбинация преимуществ двух самых быстрых реализаций на данный момент. Он использует бинарный поиск и избегает лишних делений. Быстрый тест с индексом цикла с 100 000 000 итераций показывает, что он в два раза быстрее, чем самая быстрая в настоящее время реализация.

Завершается после 2,829,581 тиков.

Для сравнения я также измерил исправленный вариант самой быстрой на данный момент реализации, который занял 5 664 627 тиков.

public static int GetFirstDigitX( int i )
{
    if( i < 0 && ( i = -i ) < 0 ) return 2;
    if( i >= 100000000 ) i /= 100000000;
    if( i >= 10000 ) i /= 10000;
    if( i >= 100 ) i /= 100;
    if( i >= 10 ) i /= 10;
    return i;
}

Принятый ответ с той же коррекцией, необходимой 16 561 929 отметок для этого теста на моем компьютере.

public static int GetFirstDigitY( int i )
{
    if( i < 0 && ( i = -i ) < 0 ) return 2;
    while( i >= 10 )
        i /= 10;
    return i;
}

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

3 голосов
/ 31 марта 2009
int temp = i;
while (temp >= 10)
{
    temp /= 10;
}

Результат в temp

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...