Conversion de chaîne en entier sans utiliser la multiplication C #

9

Existe-t-il un moyen de convertir une chaîne en entiers sans utiliser la multiplication. L'implémentation de int.Parse () utilise également la multiplication. J'ai d'autres questions similaires où vous pouvez convertir manuellement une chaîne en int, mais cela nécessite également de multiplier le nombre par sa base 10. C'était une question d'interview que j'avais dans l'une des interviews et je n'arrive pas à trouver de réponse à ce sujet.

chaudrhy
la source
3
avec (texte) ou sans (titre)?
d4zed
2
Pourriez-vous clarifier? Votre titre dit "sans" utiliser tandis que votre texte dit "avec" en utilisant ...
vonludi
1
Compte tenu du reste du contenu de la question (par exemple "mais cela nécessite également de multiplier le nombre par sa base 10"), le titre est correct. De plus, si la multiplication est autorisée, cela est trivial, il ne serait donc pas logique de le demander.
John
7
cet article suggère de remplacer la multiplication par dix par des décalages de bits et addition ( x<<1 + x<<3), mais c'est toujours une façon d'utiliser la multiplication, donc il est difficile de dire si c'est "dans l'esprit" de la question ...
Simon MᶜKenzie
3
Ça for (int i = 0; i < 10; i++) result += number;compte?
canton7

Réponses:

12

Si vous supposez un système numérique en base 10 et que vous remplacez la multiplication par des décalages de bits ( voir ici ), cela peut être une solution pour les entiers positifs .

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

    return number;
}

Voir l'exemple sur ideone .

La seule hypothèse est que les personnages '0'se '9'trouvent directement les uns à côté des autres dans le jeu de caractères. Les caractères numériques sont convertis en leur valeur entière à l'aide de character - '0'.

Éditer:

Pour les entiers négatifs, cette version ( voir ici ) fonctionne.

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 général, vous devez prendre en compte les erreurs comme les vérifications nulles, les problèmes avec d'autres caractères non numériques, etc.

Andre Kampling
la source
6

Ça dépend. Parlons-nous de l'opération logique de la multiplication, ou comment cela se fait-il réellement dans le matériel?

Par exemple, vous pouvez convertir une chaîne hexadécimale (ou octale, ou tout autre multiplicateur de base deux) en un entier "sans multiplication". Vous pouvez parcourir caractère par caractère et conserver oring ( |) et bitshifting ( <<). Cela évite d'utiliser l' *opérateur.

Faire de même avec des chaînes décimales est plus délicat, mais nous avons encore un ajout simple. Vous pouvez utiliser des boucles avec ajout pour faire la même chose. Assez simple à faire. Ou vous pouvez créer votre propre "table de multiplication" - j'espère que vous avez appris à multiplier les nombres à l'école; vous pouvez faire la même chose avec un ordinateur. Et bien sûr, si vous êtes sur un ordinateur décimal (plutôt que binaire), vous pouvez faire le "décalage de bits", tout comme avec la chaîne hexadécimale précédente. Même avec un ordinateur binaire, vous pouvez utiliser une série de décalages de bits - (a << 1) + (a << 3)c'est la même chose que a * 2 + a * 8 == a * 10. Attention aux nombres négatifs. Vous pouvez trouver de nombreuses astuces pour rendre cela intéressant.

Bien sûr, les deux ne sont que des multiplications déguisées. En effet, les systèmes numériques de position sont intrinsèquement multiplicatifs . Voilà comment fonctionne cette représentation numérique particulière. Vous pouvez avoir des simplifications qui cachent ce fait (par exemple, les nombres binaires n'ont besoin 0que 1, et donc au lieu de multiplier, vous pouvez avoir une condition simple - bien sûr, ce que vous faites vraiment est toujours la multiplication, juste avec seulement deux entrées possibles et deux possibles sorties), mais il est toujours là, tapi. <<est identique à * 2, même si le matériel qui effectue l'opération peut être plus simple et / ou plus rapide.

Pour supprimer complètement la multiplication, vous devez éviter d'utiliser un système positionnel. Par exemple, les chiffres romains sont additives (notez que les chiffres romains réels ne pas utiliser les règles de compactification que nous avons aujourd'hui - quatre serait IIIIpas IV, et quatorze ont pu être écrites sous une forme quelconque comme XIIII, IIIIX, IIXII, VVIIIIetc.). Convertir une telle chaîne en entier devient très facile - il suffit de passer caractère par caractère et de continuer à ajouter. Si le personnage est X, ajoutez-en dix. Si V, ajoutez cinq. SiI, ajoute un. J'espère que vous pouvez voir pourquoi les chiffres romains sont restés si populaires pendant si longtemps; les systèmes numériques de position sont merveilleux lorsque vous devez faire beaucoup de multiplication et de division. Si vous avez principalement affaire à l'addition et à la soustraction, les chiffres romains fonctionnent très bien et nécessitent beaucoup moins de scolarité (et un boulier est beaucoup plus facile à fabriquer et à utiliser qu'une calculatrice de position!).

Avec des missions comme celle-ci, il y a beaucoup de aléas sur ce que l'intervieweur attend réellement. Peut-être qu'ils veulent juste voir vos processus de pensée. Adoptez-vous des détails techniques (ce <<n'est pas vraiment de la multiplication)? Connaissez-vous la théorie des nombres et l'informatique? Plongez-vous simplement dans votre code ou demandez-vous des éclaircissements? Le voyez-vous comme un défi amusant ou comme une autre question d'entrevue ennuyeuse et ridicule qui n'a aucune pertinence par rapport à votre travail? Il nous est impossible de vous dire la réponse que l'intervieweur cherchait.

Mais j'espère qu'au moins je vous ai donné un aperçu des réponses possibles :)

Luaan
la source
Si nous utilisons le système de chiffres romains, comment ajouteriez-vous si nous avions des caractères comme B, T, S, R, G, A, etc. qui ne font pas partie du système de chiffres romains. En d'autres termes, comment obtenir un équivalent int?
Adheesh
@Adheesh Je n'ai aucune idée de ce que vous essayez de demander. Pouvez-vous donner un exemple de ce que vous pensez être un problème?
Luaan
Supposons que nous ayons une chaîne "12345" et que nous voulons la convertir en entier. Et si je ne veux pas utiliser le système numérique de position et utiliser à la place le système de chiffres romains. Comment pourrions-nous le faire? (Je ne veux pas du tout utiliser la multiplication)
Adheesh
@Adheesh Dans un système de chiffres romains, la chaîne ne serait pas "12345". Exactement. Les systèmes numériques positionnels sont intrinsèquement multiplicatifs. Le système de chiffres romains est additif. La chaîne romaine serait quelque chose comme "MMMMMMMMMMMMCCCXXXXIIIII". Allez caractère par caractère et continuez à ajouter des chiffres.
Luaan
@Adheesh Bien sûr, dans la réalité pratique, ce ne serait pas un si long gâchis encombrant. Au lieu de "12345 mètres", vous diriez quelque chose comme "XII kilo et CCCXXXXIIIII mètres" ou autre. Les anciens systèmes de mesure ont été adaptés aux personnes qui ne pouvaient pas compter beaucoup - il suffit de comparer la plage de valeurs que vous pouvez représenter avec les unités impériales aux unités modernes si vous ne pouvez compter que jusqu'à douze :)
Luaan
5

Considérant qu'il s'agit d'une question d'entrevue, la performance peut ne pas être une priorité élevée. Pourquoi pas seulement:

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 chaîne transmise n'est pas un entier, la méthode lèvera une exception de débordement.

nl-x
la source
1
Brillant: P Assurez-vous simplement de ne pas l'utiliser s'il n'y a pas de rétroaction immédiate de l'intervieweur, cependant: DI imagine qu'il pourrait même y avoir des moyens d'améliorer les performances avec des correspondances partielles, en utilisant la même approche de base. À tout le moins, une simple vérification des nombres négatifs vous fait économiser la moitié de la boucle; un chèque pour impaire / même une autre moitié.
Luaan
@Luaan Je voulais le faire en aussi peu de lignes que possible :) ...public int StringToInt(string value, int search_from = int.MinValue) => value == search_from.ToString() ? search_from : StringToInt (value, ++search_from);
nl-x
Malheureusement, cela va exploser: D Mais c'est une excellente solution pour écrire le code sur papier - cela le rend très évidemment correct, et il est difficile d'écrire mal. Vous pouvez également utiliser LIINQ - Enumerable.Range(int.MinValue, int.MaxValue).First(i => i.ToString() == stringValue.
Luaan
0

Toute multiplication peut être remplacée par des ajouts répétés. Vous pouvez donc remplacer n'importe quelle multiplication dans un algorithme existant par une version qui utilise uniquement l'addition:

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

Vous pouvez aller plus loin et écrire une chaîne spécialisée dans Int en utilisant cette idée qui minimise le nombre d'ajouts (nombre négatif et gestion des erreurs omis pour plus de brièveté):

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

Mais je ne pense pas que cela en vaille la peine car les performances ne semblent pas être un problème ici.

David Brown
la source