Convertir cadena a entero sin usar la multiplicación C #

9

¿Hay alguna manera de convertir cadenas a enteros sin usar Multiplicación? La implementación de int.Parse () también usa la multiplicación. Tengo otras preguntas similares en las que puede convertir manualmente la cadena a int, pero eso también requiere multiplicar el número por su base 10. Esta fue una pregunta de entrevista que tuve en una de las entrevistas y parece que no puedo encontrar ninguna respuesta al respecto.

chaudrhy
fuente
3
con (texto) o sin (título)?
d4zed
2
¿Podrías aclarar? Su título dice 'sin' usar mientras que su texto dice 'con' usando ...
vonludi
1
Teniendo en cuenta el resto del contenido de la pregunta (por ejemplo, "pero eso también requiere multiplicar el número por su base 10"), el título es correcto. Además, si se permite la multiplicación, entonces esto es trivial, por lo que no tendría sentido preguntarlo.
John
77
este artículo sugiere reemplazar la multiplicación por diez con cambios de bits y suma ( x<<1 + x<<3), pero sigue siendo una forma de usar la multiplicación, por lo que es difícil decir si eso está "en el espíritu" de la pregunta ...
Simon MᶜKenzie
3
No for (int i = 0; i < 10; i++) result += number;cuentan?
canton7

Respuestas:

12

Si asume un sistema de números de base 10 y sustituye la multiplicación por cambios de bits ( vea aquí ), esta puede ser una solución para enteros positivos .

public int StringToInteger(string value)
{
    int number = 0;
    foreach (var character in value)
        number = (number << 1) + (number << 3) + (character - '0');

    return number;
}

Ver el ejemplo de ideone .

La única suposición es que los personajes '0'que '9'se encuentran directamente junto a la otra en el conjunto de caracteres. Los caracteres de dígitos se convierten a su valor entero usando character - '0'.

Editar:

Para enteros negativos, esta versión ( ver aquí ) funciona.

public static int StringToInteger(string value)
{
    bool negative = false;
    int i = 0;

    if (value[0] == '-')
    {
        negative = true;
        ++i;
    }

    int number = 0;
    for (; i < value.Length; ++i)
    {
        var character = value[i];
        number = (number << 1) + (number << 3) + (character - '0');
    }

    if (negative)
        number = -number;
    return number;
}

En general, debe tener en cuenta los errores, como las verificaciones nulas, los problemas con otros caracteres no numéricos, etc.

Andre Kampling
fuente
6

Depende. ¿Estamos hablando de la operación lógica de la multiplicación, o cómo se hace realmente en hardware?

Por ejemplo, puede convertir una cadena hexadecimal (u octal, o cualquier otra base dos multiplicador) en un entero "sin multiplicación". Puede ir carácter por carácter y seguir oring ( |) y bitshifting ( <<). Esto evita usar el *operador.

Hacer lo mismo con las cadenas decimales es más complicado, pero todavía tenemos una suma simple. Puede usar bucles con adición para hacer lo mismo. Bastante simple de hacer. O puede hacer su propia "tabla de multiplicar", ojalá haya aprendido a multiplicar números en la escuela; Puedes hacer lo mismo con una computadora. Y, por supuesto, si está en una computadora decimal (en lugar de binaria), puede hacer el "desplazamiento de bits", al igual que con la cadena hexadecimal anterior. Incluso con una computadora binaria, puede usar una serie de cambios de bits, (a << 1) + (a << 3)es lo mismo que a * 2 + a * 8 == a * 10. Cuidado con los números negativos. Puedes descubrir muchos trucos para hacer esto interesante.

Por supuesto, ambos son solo multiplicaciones disfrazadas. Esto se debe a que los sistemas numéricos posicionales son inherentemente multiplicativos . Así es como funciona esa representación numérica particular. Puede tener simplificaciones que ocultan este hecho (por ejemplo, los números binarios solo necesitan 0y 1, por lo tanto, en lugar de multiplicar, puede tener una condición simple; por supuesto, lo que realmente está haciendo es multiplicar, solo con dos entradas posibles y dos posibles salidas), pero siempre está ahí, al acecho. <<es lo mismo que * 2, incluso si el hardware que realiza la operación puede ser más simple y / o más rápido.

Para eliminar por completo la multiplicación, debe evitar el uso de un sistema posicional. Por ejemplo, los números romanos son aditivos (nota que los números romanos reales no utilizaron las reglas compactificación que tenemos hoy - cuatro serían IIII, no IV, y los catorce años se podrían escribir en cualquier forma como XIIII, IIIIX, IIXII, VVIIIIetc.). Convertir una cadena de este tipo en entero se vuelve muy fácil: simplemente vaya carácter por carácter y siga agregando. Si el personaje es X, suma diez. Si V, suma cinco. SiI, Agrega uno. Espero que puedan ver por qué los números romanos siguieron siendo populares durante tanto tiempo; los sistemas numéricos posicionales son maravillosos cuando necesitas hacer muchas multiplicaciones y divisiones. Si se trata principalmente de sumas y restas, los números romanos funcionan muy bien y requieren mucha menos educación (¡y un ábaco es mucho más fácil de hacer y usar que una calculadora posicional!).

Con tareas como esta, hay muchos imprevistos sobre lo que el entrevistador realmente espera. Quizás solo quieran ver tus procesos de pensamiento. ¿Adoptas tecnicismos ( <<no es realmente multiplicación)? ¿Conoces la teoría de números y la informática? ¿Simplemente te sumerges con tu código o pides una aclaración? ¿Lo ve como un desafío divertido, o como otra pregunta ridícula y aburrida de la entrevista que no tiene ninguna relevancia para su trabajo? Es imposible para nosotros decirle la respuesta que estaba buscando el entrevistador.

Pero espero al menos darte una idea de las posibles respuestas :)

Luaan
fuente
Si utilizamos el sistema de números romanos, ¿cómo agregaría si tuviéramos caracteres como B, T, S, R, G, A, etc., que no son parte del sistema de números romanos? En otras palabras, ¿cómo obtendría un int equivalente?
Adheesh
@Adheesh No tengo idea de lo que estás tratando de preguntar. ¿Puedes dar un ejemplo de lo que crees que sería un problema?
Luaan
Supongamos que tenemos una cadena "12345" y queremos convertirla en un int. ¿Qué pasa si no quiero usar el sistema numérico posicional y en su lugar uso el sistema de números romanos? ¿Cómo podríamos hacerlo? (No quiero usar la multiplicación en absoluto)
Adheesh
@Adheesh En un sistema de números romanos, la cadena no sería "12345". Ese es todo el punto. Los sistemas numéricos posicionales son inherentemente multiplicativos. El sistema de numeración romana es aditivo. La cadena romana sería algo así como "MMMMMMMMMMMMCCCXXXXIIIII". Ve personaje por personaje y sigue sumando números.
Luaan
@Adheesh Por supuesto, en la realidad práctica real, no sería un desastre tan difícil de manejar. En lugar de "12345 metros", diría algo así como "XII kilo y metros CCCXXXXIIIII" o algo así. Los sistemas de medición antiguos se adaptaron a personas que no podían contar mucho: solo compare el rango de valores que puede representar con unidades imperiales con unidades modernas si solo puede contar hasta doce :)
Luaan
5

Considerando que se trata de una pregunta de entrevista, el desempeño podría no ser una alta prioridad. ¿Por qué no solo:

private int StringToInt(string value)
{
    for (int i = int.MinValue; i <= int.MaxValue; i++)
        if (i.ToString() == value)
            return i;
    return 0; // All code paths must return a value.
}

Si la cadena pasada no es un entero, el método generará una excepción de desbordamiento.

nl-x
fuente
1
Brillante: P Solo asegúrate de no usarlo si no hay comentarios inmediatos del entrevistador: DI imagino que incluso podría haber formas de mejorar el rendimiento con coincidencias parciales, utilizando el mismo enfoque básico. Por lo menos, una simple verificación de números negativos le ahorra la mitad del ciclo; un cheque por impar / incluso otra mitad.
Luaan
@Luaan, quería hacerlo en la menor cantidad de líneas posible :) ...public int StringToInt(string value, int search_from = int.MinValue) => value == search_from.ToString() ? search_from : StringToInt (value, ++search_from);
nl-x
Lamentablemente, eso va a explotar: D Pero es una gran solución para escribir el código en papel: lo hace obviamente muy correcto y es difícil escribir mal. También puedes usar LIINQ - Enumerable.Range(int.MinValue, int.MaxValue).First(i => i.ToString() == stringValue.
Luaan
0

Cualquier multiplicación puede ser reemplazada por la suma repetida. Por lo tanto, puede reemplazar cualquier multiplicación en un algoritmo existente con una versión que solo use la suma:

static int Multiply(int a, int b)
{
    bool isNegative = a > 0 ^ b > 0;
    int aPositive = Math.Abs(a);
    int bPositive = Math.Abs(b);
    int result = 0;
    for(int i = 0; i < aPositive; ++i)
    {
        result += bPositive;
    }
    if (isNegative) {
        result = -result;
    }
    return result;
}

Podría ir más allá y escribir una Cadena especializada para Int usando esta idea que minimiza el número de adiciones (número negativo y manejo de errores omitidos por brevedad):

static int StringToInt(string v)
{
    const int BASE = 10;
    int result = 0;
    int currentBase = 1;
    for (int digitIndex = v.Length - 1; digitIndex >= 0; --digitIndex)
    {
        int digitValue = (int)Char.GetNumericValue(v[digitIndex]);
        int accum = 0;
        for (int i = 0; i < BASE; ++i)
        {
            if (i == digitValue)
            {
                result += accum;
            }
            accum += currentBase;
        }
        currentBase = accum;
    }
    return result;
}

Pero no creo que valga la pena, ya que el rendimiento no parece ser una preocupación aquí.

David Brown
fuente