Ответ 1
Вот как
int i = Math.Abs(386792);
while(i >= 10)
i /= 10;
и i
будут содержать то, что вам нужно
В С#, какой лучший способ получить 1-ю цифру в int? Метод, который я придумал, состоит в том, чтобы превратить int в строку, найти 1-ю строку char строки, а затем вернуть ее обратно к 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
Попробуйте это
public int GetFirstDigit(int number) {
if ( number < 10 ) {
return number;
}
return GetFirstDigit ( (number - (number % 10)) / 10);
}
EDIT
Несколько человек запросили версию цикла
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 );
...
Не очень красиво:)
[Отредактировано: первый ответ был очень плохим:)]
[Редактировать 2: я бы, вероятно, советовал решениям, управляющим строкой)
[Редактировать 3: форматирование кода приятно:)]
вариант ответа Антона:
// 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;
Имел ту же идею, что и Lennaert
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;
}
}
P.S. У MartinStettner была та же идея.
int myNumber = 8383;
char firstDigit = myNumber.ToString()[0];
// char = '8'
int temp = i;
while (temp >= 10)
{
temp /= 10;
}
Результат в temp
Очевидный, но медленный, математический подход:
int firstDigit = (int)(i / Math.Pow(10, (int)Math.Log10(i))));
Я знаю, что это не С#, но удивительно, что в python "получить первый char строкового представления числа" быстрее!
EDIT: нет, я допустил ошибку, я забыл построить снова 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
$
Я просто наткнулся на этот старый вопрос и почувствовал склонность предлагать другое предложение, так как ни один из других ответов до сих пор не возвращает правильный результат для всех возможных входных значений, и он все равно может быть выполнен быстрее:/p >
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;
}
Это работает для всех значащих целочисленных значений inclusive -2147483648
, который является наименьшим знаковым целым и не имеет положительного аналога. Math.Abs( -2147483648 )
запускает a 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;
}
Простые функции, подобные этим, могут быть легко доказаны для правильности, так как итерация всех возможных значений целых чисел занимает не более нескольких секунд на текущем оборудовании. Это означает, что менее важно реализовать их в исключительно читабельном виде, поскольку в дальнейшем их не будет вообще исправлять ошибку.
Очень простой (и, вероятно, довольно быстрый, потому что он включает только сравнения и одно разделение):
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 Eight Core (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, может быть, кто-то может проверить его для нас?
Чтобы дать вам альтернативу, вы можете разделить целое число на 10, а затем отбросить одно значение, как только достигнете нуля. Поскольку операции с строкой, как правило, медленные, это может быть быстрее, чем манипуляция строк, но отнюдь не изящна.
Что-то вроде этого:
while(curr>=10)
curr /= 10;
while (i > 10)
{
i = (Int32)Math.Floor((Decimal)i / 10);
}
// i is now the first int
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(), который внутренне должен реализовывать аналогичный цикл и должен построить (и разобрать) строковый объект на пути...
Нетеративная формула:
public static int GetHighestDigit(int num)
{
if (num <= 0)
return 0;
return (int)((double)num / Math.Pow(10f, Math.Floor(Math.Log10(num))));
}
Очень простой способ получить последнюю цифру:
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 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;
}
Вот более простой способ, который не связан с циклом
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());