如何获得int(C#)中的第一个数字?

时间:2009-03-31 14:45:06

标签: c#

在C#中,获取int中第一个数字的最佳方法是什么?我想出的方法是将int转换为字符串,找到字符串的第一个字符,然后将其转回int。

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

虽然这可以完成这项工作,但感觉可能是一个很好的,简单的,基于数学的解决方案来解决这个问题。字符串操作感觉笨重。

编辑:无论速度差异如何,mystring [0]而不是Substring()仍然只是字符串操作

25 个答案:

答案 0 :(得分:212)

基准

首先,您必须决定“最佳”解决方案的含义,当然这要考虑算法的效率,可读性/可维护性以及未来出现错误的可能性。但是,仔细的单元测试通常可以避免这些问题。

我运行了这些示例中的每一个1000万次,结果值是已经过去的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

展开&amp;优化循环

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

答案 1 :(得分:122)

以下是

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

i将包含您需要的内容

答案 2 :(得分:31)

试试这个

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;
}

答案 3 :(得分:26)

我能想到的最好的是:

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

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

...

不太漂亮:))

[编辑:第一个答案非常糟糕:)]

[编辑2:我可能会建议使用字符串操作解决方案]

[编辑3:代码格式 很好:)]

答案 4 :(得分:18)

安东回答的变异:

 // 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 :(得分:5)

与Lennaert有同样的想法

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

这也适用于负数。

答案 6 :(得分:4)

如果你认为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有同样的想法。

答案 7 :(得分:3)

int myNumber = 8383;
char firstDigit = myNumber.ToString()[0];
// char = '8'

答案 8 :(得分:3)

我只是偶然发现了这个老问题并且倾向于提出另一个建议,因为到目前为止没有其他答案返回 所有 可能输入值的正确结果仍然可以加快速度:

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;
}

这样的简单函数可以很容易地证明其正确性,因为迭代所有可能的整数值在当前硬件上花费的时间不会超过几秒。这意味着以一种特别易读的方式实现它们并不那么重要,因为以后根本不需要修复它们中的错误。

答案 9 :(得分:3)

我知道这不是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
$

答案 10 :(得分:3)

一种明显但缓慢的数学方法是:

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

答案 11 :(得分:3)

int temp = i;
while (temp >= 10)
{
    temp /= 10;
}

结果temp

答案 12 :(得分:1)

在这里与我的一位同事进行了一些测试,发现大多数解决方案不适用于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);
    }

答案 13 :(得分:1)

非常简单(可能非常快,因为它只涉及比较和一个分裂):

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)

答案 14 :(得分:1)

使用以下所有示例获取此代码:

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双核4200+(2.2 GHz)上获得了这些结果:

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 GHz)上获得这些

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是否更快取决于CPU,我只能推测这是因为CPU的命令处理器中的分支预测在新处理器上更智能,但我并不是真的肯定的。

我没有任何英特尔CPU,也许有人可以为我们测试它?

答案 15 :(得分:0)

非迭代公式:

public static int GetHighestDigit(int num)
{
    if (num <= 0)
       return 0; 

    return (int)((double)num / Math.Pow(10f, Math.Floor(Math.Log10(num))));
}

答案 16 :(得分:0)

int start = curr;
while (start >= 10)
  start /= 10;

这比ToString()方法更有效,ToString()方法在内部必须实现类似的循环,并且必须在路上构造(和解析)字符串对象...

答案 17 :(得分:0)

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);
}

答案 18 :(得分:0)

while (i > 10)
{
   i = (Int32)Math.Floor((Decimal)i / 10);
}
// i is now the first int

答案 19 :(得分:0)

为了给你一个替代方案,你可以重复地将整数除以10,然后在达到零时回滚一个值。由于字符串操作通常很慢,这可能比字符串操作更快,但绝不是优雅的。

这样的事情:

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

答案 20 :(得分:0)

获取最后一位数的非常简单的方法:

int myInt = 1821;

int lastDigit = myInt - ((myInt/10)*10); // 1821 - 1820 = 1

答案 21 :(得分:0)

这是我通常做的,请参考下面的功能:

此功能可以从您可以修改的任何字符串中提取第一个数字,并根据您的使用情况使用此功能

   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;

    }

答案 22 :(得分:0)

也请检查此内容:

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;
}

答案 23 :(得分:-2)

这是一种不涉及循环的简单方法

int number = 1234
int firstDigit = Math.Floor(number/(Math.Pow(10, number.ToString().length - 1))

这会给我们1234 / Math.Pow(10,4-1)= 1234/1000 = 1

答案 24 :(得分:-2)

int i = 4567789;
int digit1 = int.Parse(i.ToString()[0].ToString());