В C #, как лучше всего получить 1-ю цифру в int? Метод, который я придумал, состоит в том, чтобы превратить int в строку, найти 1-й символ строки, а затем вернуть его в int.
int start = Convert.ToInt32(curr.ToString().Substring(0, 1));
Хотя это работает, похоже, что существует хорошее простое математическое решение такой проблемы. Манипуляции со строками кажутся неуклюжими.
Изменить: независимо от разницы в скорости mystring [0] вместо Substring () по-прежнему является просто манипуляцией со строкой
Ответы:
Вот как
int i = Math.Abs(386792); while(i >= 10) i /= 10;
и
i
будет содержать то, что вам нужноисточник
Контрольные точки
Во-первых, вы должны решить, что вы подразумеваете под «лучшим» решением, разумеется, с учетом эффективности алгоритма, его удобочитаемости / ремонтопригодности и вероятности появления ошибок в будущем. Однако тщательные модульные тесты обычно позволяют избежать этих проблем.
Я запускал каждый из этих примеров 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
Условные
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
источник
char
кint
оценивается к кодовой точке символа, поэтому приведение символа0
оценивается к 48. Вычитание 48 эффективно выполняет преобразование в целое число. Другой пример: приведение персонажа5
кint
Попробуй это
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; }
источник
Лучшее, что я могу придумать, это:
int numberOfDigits = Convert.ToInt32(Math.Floor( Math.Log10( value ) ) ); int firstDigit = value / Math.Pow( 10, numberOfDigits );
источник
вариант ответа Антона:
// 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;
источник
/10
и/1000
. Вы по-прежнему получаете преимущество более агрессивного разделения, но с меньшим беспорядком. +1 за творчество :)int myNumber = 8383; char firstDigit = myNumber.ToString()[0]; // char = '8'
источник
Была такая же идея, как у Леннарта
int start = number == 0 ? 0 : number / (int) Math.Pow(10,Math.Floor(Math.Log10(Math.Abs(number))));
Это также работает с отрицательными числами.
источник
Если вы думаете, что ответ 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; } }
PS У Мартина Штеттнера была такая же идея.
источник
i=(...moreugly...)i>=100?i>=1000?i/1000:i/100:i>=10?i/10:i;
Очевидный, но медленный математический подход:
int firstDigit = (int)(i / Math.Pow(10, (int)Math.Log10(i))));
источник
int temp = i; while (temp >= 10) { temp /= 10; }
Результат в
temp
источник
Я знаю, что это не 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 $
источник
Я только что наткнулся на этот старый вопрос и почувствовал себя склонным предложить другое предложение, поскольку ни один из других ответов пока не возвращает правильный результат для всех возможных входных значений, и его все еще можно сделать быстрее:
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
.Реализацию можно рассматривать как комбинацию преимуществ двух самых быстрых на данный момент реализаций. Он использует двоичный поиск и избегает лишних делений. Быстрый тест с индексом цикла с 100000000 итераций показывает, что он в два раза быстрее, чем самая быстрая реализация на данный момент.
Он заканчивается после 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; }
Правильность таких простых функций легко проверить, так как итерация всех возможных целочисленных значений на текущем оборудовании занимает не более нескольких секунд. Это означает, что менее важно реализовать их в исключительно удобочитаемом виде, поскольку в дальнейшем просто никогда не будет необходимости исправлять ошибку внутри них.
источник
Очень просто (и, вероятно, довольно быстро, потому что включает только сравнения и одно деление):
if(i<10) firstdigit = i; else if (i<100) firstdigit = i/10; else if (i<1000) firstdigit = i/100; else if (i<10000) firstdigit = i/1000; else if (i<100000) firstdigit = i/10000; else (etc... all the way up to 1000000000)
источник
Провел несколько тестов с одним из моих коллег и обнаружил, что большинство решений не работают для чисел меньше 0.
public int GetFirstDigit(int number) { number = Math.Abs(number); <- makes sure you really get the digit! if (number < 10) { return number; } return GetFirstDigit((number - (number % 10)) / 10); }
источник
Используя все приведенные ниже примеры, чтобы получить этот код:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; namespace Benfords { class Program { static int FirstDigit1(int value) { return Convert.ToInt32(value.ToString().Substring(0, 1)); } static int FirstDigit2(int value) { while (value >= 10) value /= 10; return value; } static int FirstDigit3(int value) { return (int)(value.ToString()[0]) - 48; } static int FirstDigit4(int value) { return (int)(value / Math.Pow(10, (int)Math.Floor(Math.Log10(value)))); } static int FirstDigit5(int value) { if (value < 10) return value; if (value < 100) return value / 10; if (value < 1000) return value / 100; if (value < 10000) return value / 1000; if (value < 100000) return value / 10000; if (value < 1000000) return value / 100000; if (value < 10000000) return value / 1000000; if (value < 100000000) return value / 10000000; if (value < 1000000000) return value / 100000000; return value / 1000000000; } static int FirstDigit6(int value) { if (value >= 100000000) value /= 100000000; if (value >= 10000) value /= 10000; if (value >= 100) value /= 100; if (value >= 10) value /= 10; return value; } const int mcTests = 1000000; static void Main(string[] args) { Stopwatch lswWatch = new Stopwatch(); Random lrRandom = new Random(); int liCounter; lswWatch.Start(); for (liCounter = 0; liCounter < mcTests; liCounter++) FirstDigit1(lrRandom.Next()); lswWatch.Stop(); Console.WriteLine("Test {0} = {1} ticks", 1, lswWatch.ElapsedTicks); lswWatch.Reset(); lswWatch.Start(); for (liCounter = 0; liCounter < mcTests; liCounter++) FirstDigit2(lrRandom.Next()); lswWatch.Stop(); Console.WriteLine("Test {0} = {1} ticks", 2, lswWatch.ElapsedTicks); lswWatch.Reset(); lswWatch.Start(); for (liCounter = 0; liCounter < mcTests; liCounter++) FirstDigit3(lrRandom.Next()); lswWatch.Stop(); Console.WriteLine("Test {0} = {1} ticks", 3, lswWatch.ElapsedTicks); lswWatch.Reset(); lswWatch.Start(); for (liCounter = 0; liCounter < mcTests; liCounter++) FirstDigit4(lrRandom.Next()); lswWatch.Stop(); Console.WriteLine("Test {0} = {1} ticks", 4, lswWatch.ElapsedTicks); lswWatch.Reset(); lswWatch.Start(); for (liCounter = 0; liCounter < mcTests; liCounter++) FirstDigit5(lrRandom.Next()); lswWatch.Stop(); Console.WriteLine("Test {0} = {1} ticks", 5, lswWatch.ElapsedTicks); lswWatch.Reset(); lswWatch.Start(); for (liCounter = 0; liCounter < mcTests; liCounter++) FirstDigit6(lrRandom.Next()); lswWatch.Stop(); Console.WriteLine("Test {0} = {1} ticks", 6, lswWatch.ElapsedTicks); Console.ReadLine(); } } }
Я получаю эти результаты на AMD Ahtlon 64 X2 Dual Core 4200+ (2,2 ГГц):
Test 1 = 2352048 ticks Test 2 = 614550 ticks Test 3 = 1354784 ticks Test 4 = 844519 ticks Test 5 = 150021 ticks Test 6 = 192303 ticks
Но получите их на восьмиядерном процессоре AMD FX 8350 (4,00 ГГц).
Test 1 = 3917354 ticks Test 2 = 811727 ticks Test 3 = 2187388 ticks Test 4 = 1790292 ticks Test 5 = 241150 ticks Test 6 = 227738 ticks
Так что, будет ли метод 5 или 6 быстрее, зависит от ЦП, я могу только предположить, что это связано с тем, что предсказание ветвлений в командном процессоре ЦП более умное на новом процессоре, но я не совсем уверен.
У меня нет процессоров Intel, может, кто-нибудь нам поможет?
источник
Проверьте это тоже:
int get1digit(Int64 myVal) { string q12 = myVal.ToString()[0].ToString(); int i = int.Parse(q12); return i; }
Также хорошо, если вам нужно несколько номеров:
int get3digit(Int64 myVal) //Int64 or whatever numerical data you have { char mg1 = myVal.ToString()[0]; char mg2 = myVal.ToString()[1]; char mg3 = myVal.ToString()[2]; char[] chars = { mg1, mg2, mg3 }; string q12= new string(chars); int i = int.Parse(q12); return i; }
источник
-1
.while (i > 10) { i = (Int32)Math.Floor((Decimal)i / 10); } // i is now the first int
источник
Неитерационная формула:
public static int GetHighestDigit(int num) { if (num <= 0) return 0; return (int)((double)num / Math.Pow(10f, Math.Floor(Math.Log10(num)))); }
источник
Чтобы дать вам альтернативу, вы можете несколько раз разделить целое число на 10, а затем откатить одно значение, когда достигнете нуля. Поскольку строковые операции обычно выполняются медленно, это может быть быстрее, чем манипуляции со строками, но отнюдь не элегантно.
Что-то вроде этого:
while(curr>=10) curr /= 10;
источник
start = getFirstDigit(start); public int getFirstDigit(final int start){ int number = Math.abs(start); while(number > 10){ number /= 10; } return number; }
или же
public int getFirstDigit(final int start){ return getFirstDigit(Math.abs(start), true); } private int getFirstDigit(final int start, final boolean recurse){ if(start < 10){ return start; } return getFirstDigit(start / 10, recurse); }
источник
int start = curr; while (start >= 10) start /= 10;
Это более эффективно, чем подход ToString (), который внутренне должен реализовывать аналогичный цикл и должен конструировать (и анализировать) строковый объект по пути ...
источник
Очень простой способ получить последнюю цифру:
int myInt = 1821; int lastDigit = myInt - ((myInt/10)*10); // 1821 - 1820 = 1
источник
Это то, что я обычно делаю, пожалуйста, обратитесь к моей функции ниже:
Эта функция может извлекать первое число из любой строки, которую вы можете изменить, и использовать эту функцию в соответствии с вашим использованием.
public static int GetFirstNumber(this string strInsput) { int number = 0; string strNumber = ""; bool bIsContNo = true; bool bNoOccued = false; try { var arry = strInsput.ToCharArray(0, strInsput.Length - 1); foreach (char item in arry) { if (char.IsNumber(item)) { strNumber = strNumber + item.ToString(); bIsContNo = true; bNoOccued = true; } else { bIsContNo = false; } if (bNoOccued && !bIsContNo) { break; } } number = Convert.ToInt32(strNumber); } catch (Exception ex) { return 0; } return number; }
источник
Вот более простой способ, не требующий цикла
int number = 1234 int firstDigit = Math.Floor(number/(Math.Pow(10, number.ToString().length - 1))
Это даст нам 1234 / Math.Pow (10, 4-1) = 1234/1000 = 1
источник
int i = 4567789; int digit1 = int.Parse(i.ToString()[0].ToString());
источник