【问题标题】:How can you get the first digit in an int (C#)?如何获得 int (C#) 中的第一个数字?
【发布时间】:2010-10-16 14:59:10
【问题描述】:

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

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

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

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

【问题讨论】:

  • 这比递归方法还要慢。 =)
  • 我的错,我忘了重置秒表=),它真的比别人慢
  • 这将在负数上失败。
  • int 的第一个数字始终为 1,抑制为零 (01010101010100101011011011100101) LOL

标签: c#


【解决方案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)

【讨论】:

  • 然后还有10k、100k、1mil、10mil、100mil等等?
  • 我会这样做。比那个递归函数高效得多。
  • 这个答案是最好的。
  • 效率是以非常非常非常丑陋、重复、不可维护的代码为代价的。如果你不小心跳级了怎么办?哎呀。
  • 你真的应该重新排序这些条件,以便最有可能在顶部......
【解决方案2】:
int myNumber = 8383;
char firstDigit = myNumber.ToString()[0];
// char = '8'

【讨论】:

  • 这不是和我发的一样吗?它只是使用 [] 而不是 Substring
  • 否:它返回一个字符而不是一个字符串,并且 [] 查找应该更快
  • 没错,但它仍然是相同的基本方法——解析字符串表示。我认为@Dinah 正在寻找一些东西......你知道......不同的。
  • @Randolpho:完全正确。抱歉,如果我对此不清楚。我更新了问题以澄清。
  • 我也会返回 firstDigit-'0' 来获取 int。 (它适用于 c ......我猜它适用于 c#)
【解决方案3】:

试试这个

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

【讨论】:

  • 这样的递归……真丢脸。在里面放一个循环。
  • @Welbog :),通常我会。但我对这个特定问题有很多怀旧之情。这几乎正​​是我被问到的第一个 CS 作业问题。当时我基本上写了这个解决方案。
  • 这是“nice”=慢的情况
  • 我希望优化器将第一个版本视为可以进行尾调用优化的东西,在这种情况下它不会变慢。
  • 但是,@Jared,这里完全没有必要减去最后一位数字。只需除以 10。
【解决方案4】:

我能想到的最好的是:

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

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

【讨论】:

  • 您可以通过使用 divisor=Convert.ToInt32(Math.Pow(10, Math.Floor(Math.Log10(value)))) 来避免整数和双精度数之间不必要的转换; firstDigit = 值/除数;
  • 不幸的是,当值为 1(返回 0)或值为负时,这不起作用。修复方法是 int numberOfDigits = 1 + Convert.ToInt32(Math.Floor(Math.Log10(Math.Abs​​(value))));
【解决方案5】:

方法如下

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

i 将包含您需要的内容

【讨论】:

    【解决方案6】:
    int temp = i;
    while (temp >= 10)
    {
        temp /= 10;
    }
    

    结果为@​​987654322@

    【讨论】:

    • @ck: 发出什么舍入?在这种情况下除法是整数除法,每个除法都精确地切断最后一位......
    【解决方案7】:

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

    类似这样的:

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

    【讨论】:

    • 使while条件(curr > 10)并且你不需要prevValue
    • >= 是必需的。 10 的第一个数字应该是 1,而不是 10
    【解决方案8】:
    while (i > 10)
    {
       i = (Int32)Math.Floor((Decimal)i / 10);
    }
    // i is now the first int
    

    【讨论】:

    • 为什么要转换为十进制?也不需要地板。
    • 存在潜在的舍入问题,实际上并不需要。
    • 应该是 while(i >= 10) 否则对于 10 的倍数会失败。
    【解决方案9】:
    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);
    }
    

    【讨论】:

    • 现在应该可以工作了。还为 abs 添加了带有陷阱的递归函数。
    【解决方案10】:
    int start = curr;
    while (start >= 10)
      start /= 10;
    

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

    【讨论】:

      【解决方案11】:

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

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

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

      【讨论】:

      • 无法将类型“double”隐式转换为“int”。
      【解决方案12】:

      非迭代公式:

      public static int GetHighestDigit(int num)
      {
          if (num <= 0)
             return 0; 
      
          return (int)((double)num / Math.Pow(10f, Math.Floor(Math.Log10(num))));
      }
      

      【讨论】:

      • Log 的实现是迭代的,因为它需要一个寻根算法
      【解决方案13】:

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

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

      【讨论】:

      • 计时,一点也​​不慢。这将是我的建议,鉴于它是一个没有循环的纯数学解决方案,它可能比这里的许多解决方案更快。请记住,今天销售的每个 x86 处理器都有一个非常、非常快速且功能强大的浮点处理器。
      • 我认为你真的应该在这里使用 Math.Log10
      • 谢谢,Martin - 我很少使用 .NET 数学库,忘记了它是 Log 和 Log10 而不是 Log 和 Ln。
      • 好吧,Adam,至少它需要取 10^(N-1)(n 是位数)加上它需要取对数。与循环答案(您将 N - 1 次相除)相比,在我看来,它必须至少慢一点。但我经常对基准感到惊讶,所以谁知道呢。
      • 此外,OP 要求更有意义的表达方式,而不考虑性能,这是表达所寻求内容的最直接(数学)方式。
      【解决方案14】:

      和Lennaert有同样的想法

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

      这也适用于负数。

      【讨论】:

        【解决方案15】:

        安东回答的变体:

         // 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 创意 :)
        • 让我想起了某人发现的 bcl 中的一个函数,该方法读起来非常难看,但它基本上是在进行循环比较,并且将比较分成十个左右的组,知道现代 cpus 会能够一次更好地处理一组比较。
        • 这只是非常丑陋,因为 C# 没有办法在编译时生成这些 const(在运行时生成这些常量会比你试图阻止的更昂贵)。在像 Lisp 这样您控制评估时间的语言中,快速方式和优雅方式是相同的。 :-)
        • 展开循环肯定比一堆废话 if/else 语句要好。它既优雅又性能。
        • 如果您将第一个“if”设置为“while”,它也适用于 64 位(但甚至更“丑陋”)。 +1
        【解决方案16】:

        基准测试

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

        我对这些示例中的每一个运行了 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
        

        展开和优化的循环

        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

        【讨论】:

        • 在哪组整数上运行它们?
        • 这太琐碎了。我很欣赏你所做的事情,它提供了很多信息,但对于 99% 的应用程序来说,这太过分了。恐怕一些新手程序员可能会留下不好的印象。 (硬核,过早的可读性优化)不过,它仍然很有趣。 :)
        • +1 用于基准测试,但我同意这完全是矫枉过正。如果您担心性能,那么您可能一开始就不应该使用 C# :)
        • +1 - 好东西。这是一种镀金的、豪华型的矫枉过正,只是让你很明显你喜欢编码。给你很多赞。不,斯图尔特,我不认为这会污染毫无戒心的新手的思想。希望它能教会他们培养对软件的热爱。
        • @Vikash - 将char 转换为int 计算为字符的代码点,因此将字符0 计算为48。减去48 有效地转换为整数.再举一个例子,将字符 5 转换为 int 计算结果为 53,然后从中减去 48 得到 5。
        【解决方案17】:

        如果您认为 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;
          }
        }
        

        附: MartinStettner 也有同样的想法。

        【讨论】:

        • @Rasch:啊,狂喜!它应该被隐藏起来,只在特殊场合才拿出来,用安静的语调阅读……谁说编程没有灵魂?
        • i=(...moreugly...)i&gt;=100?i&gt;=1000?i/1000:i/100:i&gt;=10?i/10:i;怎么样
        【解决方案18】:

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

        【讨论】:

        • 但它会将其转换回整数吗?
        • 哎呀,现在说得通了-.-
        • 他可以说是 IronPython 让这个话题稍微少了些
        【解决方案19】:

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

        【讨论】:

          【解决方案20】:

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

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

          【讨论】:

          • 这很好,但问题是获取第一个数字,而不是最后一个数字。
          • @Daniel 因为int lastDigigt = myInt % 10; 太明显了?
          【解决方案21】:
          int i = 4567789;
          int digit1 = int.Parse(i.ToString()[0].ToString());
          

          【讨论】:

            【解决方案22】:

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

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

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

            【讨论】:

              【解决方案23】:

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

              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 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 八核 (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的命令处理器中的分支预测在新处理器上更智能,但我不确定。

              我没有任何 Intel CPU,也许有人可以为我们测试一下?

              【讨论】:

                【解决方案24】:

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

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

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

                【讨论】:

                  【解决方案25】:

                  也检查一下这个:

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

                  【讨论】:

                  • 您可以为您的答案添加一些解释吗?这个问题已经有 24 个其他答案 - 很高兴知道为什么我们应该使用您的答案而不是其他答案。
                  • 使用原始解决方案时,我遇到了一些使用大数字的错误。我的解决方案更清洁。不客气。
                  • -1 的解决方案失败。
                  • :) 然后使用它: if (myVal.ToString()[0]!='-') {....} 和 - 符号不是数字!
                  • 为什么不检查数字是不是负数而不是检查字符串?
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-05-05
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-07-10
                  • 2020-06-25
                  • 1970-01-01
                  相关资源
                  最近更新 更多