[英]How can you get the first digit in an int (C#)?
在 C# 中,獲取 int 中第一個數字的最佳方法是什么? 我想出的方法是將int轉成字符串,找到字符串的第一個char,然后再轉回int。
int start = Convert.ToInt32(curr.ToString().Substring(0, 1));
雖然這可以完成工作,但感覺可能有一個很好的、簡單的、基於數學的解決方案來解決這樣的問題。 字符串操作感覺很笨拙。
編輯:不管速度差異如何, mystring[0] 而不是 Substring() 仍然只是字符串操作
首先,您必須確定“最佳”解決方案的含義,當然這要考慮算法的效率、可讀性/可維護性以及將來出現錯誤的可能性。 然而,仔細的單元測試通常可以避免這些問題。
我將這些示例中的每一個運行了 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
就是這樣
int i = Math.Abs(386792);
while(i >= 10)
i /= 10;
i
會包含你需要的東西
嘗試這個
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;
int myNumber = 8383;
char firstDigit = myNumber.ToString()[0];
// char = '8'
和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;
}
}
PS MartinStettner 也有同樣的想法。
我只是偶然發現了這個老問題,並傾向於提出另一個建議,因為到目前為止,其他答案都沒有為所有可能的輸入值返回正確的結果,並且仍然可以更快:
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;
}
這適用於所有簽名的 integer 值,包括-2147483648
,這是最小的簽名 integer 並且沒有正對應。 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;
}
像這樣的簡單函數可以很容易地證明其正確性,因為在當前硬件上迭代所有可能的 integer 值只需幾秒鍾。 這意味着以非常易讀的方式實現它們並不那么重要,因為以后根本不需要修復它們內部的錯誤。
int temp = i;
while (temp >= 10)
{
temp /= 10;
}
結果在temp
一個明顯但緩慢的數學方法是:
int firstDigit = (int)(i / Math.Pow(10, (int)Math.Log10(i))));
我知道這不是 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
$
非常簡單(而且可能很快,因為它只涉及比較和一個除法):
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)
使用下面的所有示例來獲取此代碼:
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的命令處理器中的分支預測在新處理器上更智能,但我不確定。
我沒有任何英特爾 CPU,也許有人可以為我們測試一下?
也檢查一下:
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;
}
在這里與我的一位同事進行了一些測試,發現大多數解決方案不適用於 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);
}
獲取最后一位數字的非常簡單的方法:
int myInt = 1821;
int lastDigit = myInt - ((myInt/10)*10); // 1821 - 1820 = 1
這是我通常做的,請參考下面我的 function:
此 function 可以從您可以修改的任何字符串中提取第一個數字出現,並根據您的使用情況使用此 function
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;
}
只是為了給您一個替代方案,您可以反復將 integer 除以 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() 方法更有效,后者在內部必須實現類似的循環,並且必須在途中構造(和解析)字符串 object...
非迭代公式:
public static int GetHighestDigit(int num)
{
if (num <= 0)
return 0;
return (int)((double)num / Math.Pow(10f, Math.Floor(Math.Log10(num))));
}
int i = 4567789;
int digit1 = int.Parse(i.ToString()[0].ToString());
這是一種不涉及循環的更簡單的方法
int number = 1234
int firstDigit = Math.Floor(number/(Math.Pow(10, number.ToString().length - 1))
這將給我們 1234/Math.Pow(10, 4 - 1) = 1234/1000 = 1
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.