ejemplos - primera letra de cada palabra en mayuscula c#
¿Cómo se puede obtener el primer dígito en una int(C#)? (24)
En C #, ¿cuál es la mejor manera de obtener el 1er dígito en una int? El método que se me ocurrió es convertir el int en una cadena, encontrar el primer carácter de la cadena, luego volverlo a un int.
int start = Convert.ToInt32(curr.ToString().Substring(0, 1));
Si bien esto hace el trabajo, se siente como si hubiera probablemente una solución buena, simple y matemática 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
Puntos de referencia
En primer lugar, debe decidir qué quiere decir con la "mejor" solución, por supuesto, que tiene en cuenta la eficacia del algoritmo, su legibilidad / mantenibilidad y la probabilidad de que los errores progresen 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 la cantidad de ElapsedTicks
que han pasado.
Sin más preámbulos, de los más lentos a los más rápidos, los algoritmos son:
Convirtiendo en una cadena, toma el primer personaje
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
Looping
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
Lazo 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 a Random.Next()
para obtener la siguiente int
Acabo de tropezar 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 posibles valores de entrada y aún puede hacerse 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, incluido -2147483648
que es el número entero con signo más pequeño y no tiene una contraparte positiva. Math.Abs( -2147483648 )
desencadena una System.OverflowException
y - -2147483648
calcula a -2147483648
.
La implementación se puede ver 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 ciclo con 100,000,000 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.
Para la comparación también medí una variante corregida de la implementación más rápida actualmente que tomó 5.664.627 garrapatas.
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 tics 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;
}
Las funciones simples como estas pueden probarse fácilmente para la corrección ya que iterar todos los valores enteros posibles toma no 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 reparar un error dentro de ellos más adelante.
Aquí hay una forma más simple que no involucra el bucle
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
Así es cómo
int i = Math.Abs(386792);
while(i >= 10)
i /= 10;
y contendré lo que necesitas
Esto es lo que suelo hacer, por favor refiérase a mi función a continuación:
Esta función puede extraer el primer número de ocurrencia 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;
}
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))));
}
Hice algunas pruebas con uno de mis compañeros de trabajo y descubrí que la mayoría de las soluciones no funcionan para números menores de 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);
}
Lo mejor que se me ocurre es:
int numberOfDigits = Convert.ToInt32(Math.Floor( Math.Log10( value ) ) );
int firstDigit = value / Math.Pow( 10, numberOfDigits );
...
No es muy lindo :)
[Editado: la primera respuesta fue realmente mala :)]
[Editar 2: Probablemente aconsejaría las soluciones de manipulación de cuerdas, aunque]
[Editar 3: el formato del código es bueno :)]
Método muy sencillo para obtener el último dígito:
int myInt = 1821;
int lastDigit = myInt - ((myInt/10)*10); // 1821 - 1820 = 1
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)
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 de bucle
public static int GetFirstDigitLoop(int number)
{
while (number >= 10)
{
number = (number - (number % 10)) / 10;
}
return number;
}
Sé que no es C #, pero es sorprendente que en Python el "obtener el primer carácter de la representación de la cadena del número" sea más rápido.
EDITAR : no, cometí un error, me olvidé de construir de nuevo 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
$
Si crees que la respuesta de Keltex es fea, prueba esta, es REALMENTE fea e incluso más rápida. Se desenrolla la búsqueda binaria 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.
Solo para darte una alternativa, puedes dividir el número en repetidas ocasiones por 10, y luego revertir un valor una vez que llegues a cero. Dado que las operaciones de cadena 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;
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.
Un enfoque matemático obvio pero lento es:
int firstDigit = (int)(i / Math.Pow(10, (int)Math.Log10(i))));
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 obtén estos en un 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
Entonces, si el método 5 o 6 es más rápido depende de la CPU, solo puedo suponer que esto se debe a que la predicción de bifurcación en el procesador de comando de la CPU es más inteligente en el nuevo procesador, pero no estoy seguro.
No tengo ninguna CPU Intel, ¿alguien podría probarla por nosotros?
variación en 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;
int i = 4567789;
int digit1 = int.Parse(i.ToString()[0].ToString());
int myNumber = 8383;
char firstDigit = myNumber.ToString()[0];
// char = ''8''
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 ...
int temp = i;
while (temp >= 10)
{
temp /= 10;
}
Resultado en temp
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);
}
while (i > 10)
{
i = (Int32)Math.Floor((Decimal)i / 10);
}
// i is now the first int