En C #, ¿cuál es la mejor manera de obtener el primer dígito en un int? El método que se me ocurrió es convertir el int en una cadena, encontrar el primer carácter de la cadena y luego volver a convertirlo en un int.
int start = Convert.ToInt32(curr.ToString().Substring(0, 1));
Si bien esto funciona, parece que probablemente haya una solución buena, simple y basada en matemáticas para tal problema. La manipulación de cuerdas se siente torpe.
Editar: independientemente de las diferencias de velocidad, mystring [0] en lugar de Substring () sigue siendo solo manipulación de cadenas
Respuestas:
Así es cómo
int i = Math.Abs(386792); while(i >= 10) i /= 10;
y
i
contendrá lo que necesitasfuente
Benchmarks
En primer lugar, debe decidir qué quiere decir con "mejor" solución, por supuesto que tenga en cuenta la eficiencia del algoritmo, su legibilidad / mantenibilidad y la probabilidad de que aparezcan errores en el futuro. Sin embargo, las pruebas unitarias cuidadosas generalmente pueden evitar esos problemas.
Ejecuté cada uno de estos ejemplos 10 millones de veces y el valor de los resultados es el número de los
ElapsedTicks
que han pasado.Sin más preámbulos, del más lento al más rápido, los algoritmos son:
Convirtiendo a una cadena, tome el primer carácter
int firstDigit = (int)(Value.ToString()[0]) - 48;
Resultados:
12,552,893 ticks
Usando un logaritmo
int firstDigit = (int)(Value / Math.Pow(10, (int)Math.Floor(Math.Log10(Value))));
Resultados:
9,165,089 ticks
Bucle
while (number >= 10) number /= 10;
Resultados:
6,001,570 ticks
Condicionales
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;
Resultados:
1,421,659 ticks
Bucle desenrollado y optimizado
if (i >= 100000000) i /= 100000000; if (i >= 10000) i /= 10000; if (i >= 100) i /= 100; if (i >= 10) i /= 10;
Resultados:
1,399,788 ticks
Nota:
cada prueba llama
Random.Next()
para obtener la siguienteint
fuente
char
a en anint
evalúa el punto de código del carácter, por lo que la conversión del carácter se0
evalúa como 48. Restar 48 efectivamente hace una conversión a un número entero. Como otro ejemplo, convertir el personaje5
en anint
evalúa a 53, y restar 48 de eso da como resultado 5.Prueba esto
public int GetFirstDigit(int number) { if ( number < 10 ) { return number; } return GetFirstDigit ( (number - (number % 10)) / 10); }
EDITAR
Varias personas han solicitado la versión loop
public static int GetFirstDigitLoop(int number) { while (number >= 10) { number = (number - (number % 10)) / 10; } return number; }
fuente
Lo mejor que se me ocurre es:
int numberOfDigits = Convert.ToInt32(Math.Floor( Math.Log10( value ) ) ); int firstDigit = value / Math.Pow( 10, numberOfDigits );
fuente
variación de la respuesta de Anton:
// 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;
fuente
/10
y/1000
. Aún tiene la ventaja de dividir de manera más agresiva, pero con un poco menos de desorden. +1 para la creatividad :)int myNumber = 8383; char firstDigit = myNumber.ToString()[0]; // char = '8'
fuente
Tenía la misma idea que Lennaert
int start = number == 0 ? 0 : number / (int) Math.Pow(10,Math.Floor(Math.Log10(Math.Abs(number))));
Esto también funciona con números negativos.
fuente
Si cree que la respuesta de Keltex es fea, pruebe esta, es REALMENTE fea e incluso más rápida. Realiza una búsqueda binaria desenrollada para determinar la longitud.
... 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 tuvo la misma idea.
fuente
i=(...moreugly...)i>=100?i>=1000?i/1000:i/100:i>=10?i/10:i;
Un enfoque matemático obvio, pero lento, es:
int firstDigit = (int)(i / Math.Pow(10, (int)Math.Log10(i))));
fuente
int temp = i; while (temp >= 10) { temp /= 10; }
Resulta en
temp
fuente
Sé que no es C #, pero es sorprendentemente curioso que en Python "obtener el primer carácter de la representación de cadena del número" sea más rápido.
EDITAR : no, cometí un error, olvidé volver a construir el int, lo siento. La versión desenrollada es la más rápida.
$ 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 $
fuente
Me encontré con esta vieja pregunta y me sentí inclinado a proponer otra sugerencia, ya que ninguna de las otras respuestas hasta ahora devuelve el resultado correcto para todos los valores de entrada posibles y aún se puede hacer más rápido:
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; }
Esto funciona para todos los valores enteros con signo inclusive,
-2147483648
que es el entero con signo más pequeño y no tiene una contraparte positiva.Math.Abs( -2147483648 )
disparadores unaSystem.OverflowException
y- -2147483648
computa a-2147483648
.La implementación puede verse como una combinación de las ventajas de las dos implementaciones más rápidas hasta ahora. Utiliza una búsqueda binaria y evita divisiones superfluas. Un punto de referencia rápido con el índice de un bucle con 100.000.000 de iteraciones muestra que es dos veces más rápido que la implementación más rápida actualmente.
Termina después de 2.829.581 tics.
A modo de comparación, también medí una variante corregida de la implementación más rápida actualmente que tomó 5,664,627 ticks.
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; }
La respuesta aceptada con la misma corrección necesitó 16,561,929 ticks para esta prueba en mi computadora.
public static int GetFirstDigitY( int i ) { if( i < 0 && ( i = -i ) < 0 ) return 2; while( i >= 10 ) i /= 10; return i; }
Se puede probar fácilmente que funciones simples como estas son correctas, ya que la iteración de todos los valores enteros posibles no toma mucho más que unos pocos segundos en el hardware actual. Esto significa que es menos importante implementarlos de una manera excepcionalmente legible, ya que simplemente nunca será necesario corregir un error dentro de ellos más adelante.
fuente
Muy simple (y probablemente bastante rápido porque solo involucra comparaciones y una división):
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)
fuente
Hice algunas pruebas con uno de mis compañeros de trabajo aquí y descubrí que la mayoría de las soluciones no funcionan para números inferiores a 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); }
fuente
Usando todos los ejemplos a continuación para obtener este código:
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(); } } }
Obtengo estos resultados en un 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
Pero consígalos en un AMD FX 8350 de ocho núcleos (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
Entonces, si el método 5 o 6 es más rápido o no depende de la CPU, solo puedo suponer que esto se debe a que la predicción de rama en el procesador de comandos de la CPU es más inteligente en el nuevo procesador, pero no estoy realmente seguro.
No tengo ninguna CPU Intel, ¿tal vez alguien pueda probarlo por nosotros?
fuente
Mira este también:
int get1digit(Int64 myVal) { string q12 = myVal.ToString()[0].ToString(); int i = int.Parse(q12); return i; }
También es bueno si quieres varios números:
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; }
fuente
-1
.while (i > 10) { i = (Int32)Math.Floor((Decimal)i / 10); } // i is now the first int
fuente
Fórmula no iterativa:
public static int GetHighestDigit(int num) { if (num <= 0) return 0; return (int)((double)num / Math.Pow(10f, Math.Floor(Math.Log10(num)))); }
fuente
Solo para darle una alternativa, puede dividir repetidamente el número entero entre 10 y luego revertir un valor una vez que llegue a cero. Dado que las operaciones de cadenas son generalmente lentas, esto puede ser más rápido que la manipulación de cadenas, pero de ninguna manera es elegante.
Algo como esto:
while(curr>=10) curr /= 10;
fuente
start = getFirstDigit(start); public int getFirstDigit(final int start){ int number = Math.abs(start); while(number > 10){ number /= 10; } return number; }
o
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); }
fuente
int start = curr; while (start >= 10) start /= 10;
Esto es más eficiente que un enfoque ToString () que internamente debe implementar un bucle similar y tiene que construir (y analizar) un objeto de cadena en el camino ...
fuente
Método muy fácil para obtener el último dígito:
int myInt = 1821; int lastDigit = myInt - ((myInt/10)*10); // 1821 - 1820 = 1
fuente
Esto es lo que suelo hacer, consulte mi función a continuación:
Esta función puede extraer la aparición del primer número de cualquier cadena que pueda modificar y usar esta función de acuerdo con su uso
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; }
fuente
Aquí hay una forma más simple que no implica bucles
int number = 1234 int firstDigit = Math.Floor(number/(Math.Pow(10, number.ToString().length - 1))
Eso nos daría 1234 / Math.Pow (10, 4 - 1) = 1234/1000 = 1
fuente
int i = 4567789; int digit1 = int.Parse(i.ToString()[0].ToString());
fuente