Перетворення рядка в цілий число без використання множення C #


9

Чи є спосіб перетворити рядок у цілі числа без використання множення. Реалізація int.Parse () також використовує множення. У мене є інші подібні запитання, де ви можете вручну перетворити рядок в int, але це також вимагає mulitiplicing число на його базі 10. Це питання про інтерв'ю, яке я мав в одному з інтерв'ю, і я не можу знайти відповідь щодо цього.


3
з (текст) чи без (заголовок)?
d4zed

2
Не могли б ви уточнити? У вашому заголовку написано "без" використання, а ваш текст пише "з", використовуючи ...
vonludi

1
Враховуючи решту змісту питання (наприклад, "але це також вимагає множення числа на його базу 10"), заголовок правильний. Плюс, якщо дозволено множення, то це банально, тому запитати його не було б сенсу.
Іван

7
Ця стаття пропонує замінити множення на десять з розрядністю біт та додаванням ( x<<1 + x<<3), але це все-таки спосіб використання множення, тому важко сказати, чи «це в дусі» питання ...
Саймон МᶜКензі

3
Чи for (int i = 0; i < 10; i++) result += number;рахує?
кантон7

Відповіді:


12

Якщо ви припускаєте систему чисел базової-10 і замінюєте множення на бітові зсуви ( див. Тут ), це може бути рішенням для натуральних чисел .

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

    return number;
}

Дивіться приклад на ideone .

Єдине припущення , що символи , '0'щоб '9'лежати безпосередньо поруч один з одним в наборі символів. Цифрові символи перетворюються на їх ціле значення за допомогою character - '0'.

Редагувати:

Для негативних цілих чисел ця версія ( див. Тут ) працює.

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

Загалом слід враховувати помилки, такі як нульові перевірки, проблеми з іншими нечисловими символами тощо.


6

Це залежить. Ми говоримо про логічну операцію множення чи про те, як це відбувається насправді в апаратному забезпеченні?

Наприклад, ви можете перетворити шістнадцятковий (або восьмий або будь-який інший базовий два множник) рядок у ціле число "без множення". Ви можете набирати символів за символом і зберігати oring ( |) та bitshifting ( <<). Це уникає використання *оператора.

Зробити те саме з десятковими рядками складніше, але ми все ще маємо просте доповнення. Ви можете використовувати петлі з додаванням, щоб зробити те саме. Дуже просто зробити. Або ви можете скласти власну «таблицю множення» - сподіваємось, ви навчилися множувати числа в школі; те ж саме можна зробити і з комп’ютером. І звичайно, якщо ви перебуваєте на десятковому комп’ютері (а не двійковому), ви можете робити "бітшвидкіс", як і в попередньому шістнадцятковому рядку. Навіть з двійковим комп'ютером можна використовувати серію бітзмінів - (a << 1) + (a << 3)те саме, що a * 2 + a * 8 == a * 10. Обережно ставляться до від’ємних чисел. Ви можете придумати безліч хитрощів, щоб зробити це цікавим.

Звичайно, і те, і інше - це просто множення в маскуванні. Це тому, що позиційні числові системи за своєю суттю мультиплікативні . Ось так працює саме це числове представлення. Ви можете мати спрощень , які приховують цей факт (наприклад , виконавчі числа потрібно тільки 0і 1, таким чином , замість множення, ви можете мати просту умову - звичайно, то , що ви дійсно робите все ще множення, просто тільки два можливих входів і двох можливих виводи), але це завжди є, ховається. <<те саме * 2, що навіть якщо обладнання, яке виконує операцію, може бути простішим та / або швидшим.

Щоб повністю уникнути множення, потрібно уникати використання позиційної системи. Наприклад, римські цифри є адитивними (зверніть увагу , що фактичні римські цифри не використовувати правила компактификации ми маємо сьогодні - чотири б IIII, чи не IV, і чотирнадцять могли бути написані в будь-якій формі , як XIIII, IIIIX, IIXII, і VVIIIIт.д.). Перетворити такий рядок у ціле число стає дуже просто - просто перейдіть по символу та продовжуйте додавати. Якщо символ є X, додайте десять. Якщо V, додайте п'ять. ЯкщоI, додайте один. Я сподіваюся, ви можете зрозуміти, чому римські цифри залишалися популярними так довго; позиційні числові системи чудові, коли потрібно зробити багато множення та ділення. Якщо ви в основному маєте справу з додаванням і відніманням, римські цифри працюють чудово і вимагають набагато менше навчання (а абак набагато простіше зробити і використовувати, ніж позиційний калькулятор!).

З такими завданнями, як багато цього, дуже багато вражень і того, що насправді очікує інтерв'юер. Можливо, вони просто хочуть бачити ваші думки. Чи сприймаєте ви техніку ( насправді<< це не множення)? Чи знаєте ви теорію чисел та інформатику? Ви просто занурюєтесь у свій код або просите роз'яснення? Ви вважаєте це цікавим викликом чи ще одним смішним нудним питанням інтерв'ю, яке не має жодного стосунку до вашої роботи? Нам неможливо сказати вам відповідь, яку шукав інтерв'юер.

Але я сподіваюся, що я принаймні дав вам уявлення про можливі відповіді :)


Якщо ми використовуємо систему римських цифр, як би ви додали, якби у нас були символи типу B, T, S, R, G, A тощо, які не є частиною римської системи числення. Іншими словами, як би отримати цілий еквівалент?
Адхеш

@Adheesh Я поняття не маю, про що ти намагаєшся запитати. Чи можете ви навести приклад того, що, на вашу думку, було б проблемою?
Луань

Припустимо, у нас є рядок "12345", і ми хочемо перетворити його в int. Що робити, якщо я не хочу використовувати позиційну числову систему, а замість цього використовую римську числову систему. Як ми могли це зробити? (Я взагалі не хочу використовувати множення)
Adheesh

@Adheesh У римській системі числення рядок не буде "12345". У цьому вся суть. Позиційні числові системи за своєю суттю мультиплікативні. Римська система числення є адитивною. Римський рядок був би чимось на зразок "MMMMMMMMMMMCCCCXXXXXIIIII". Перейдіть по символу та продовжуйте додавати числа.
Луань

@Adheesh Звичайно, в реальній практичній реальності це не був би такий тривалий безладний безлад. Замість "12345 метрів", ви б сказали щось на кшталт "XII кілограм і CCCXXXXIIIII метрів" або подібне. Старі системи вимірювань були пристосовані для людей, які не могли багато порахувати - просто порівняйте діапазон значень, який ви можете представляти, з імперськими одиницями, з сучасними одиницями, якщо ви можете нарахувати лише дванадцять :)
Луаан,

5

Вважаючи, що це питання інтерв'ю, ефективність може бути не найвищим пріоритетом. Чому б не просто:

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

Якщо передана рядок не є цілим числом, метод видасть виняток переповнення.


1
Блискуче: P Просто переконайтеся, що ви не користуєтесь ним, якщо немає негайного відгуку інтерв'юера: Однак, уявіть собі, може бути навіть способи покращити ефективність за допомогою часткових збігів, використовуючи той самий базовий підхід. Як мінімум, проста перевірка негативних чисел економить половину циклу; чек на непарний / навіть іншу половину.
Луань

@Luaan Я хотів зробити це якомога менше рядків :) ...public int StringToInt(string value, int search_from = int.MinValue) => value == search_from.ToString() ? search_from : StringToInt (value, ++search_from);
nl-x

На жаль, це вибухне: D Але це чудове рішення для написання коду на папері - це робить дуже очевидно правильним, і важко написати неправильно. Ви також можете використовувати LIINQ - Enumerable.Range(int.MinValue, int.MaxValue).First(i => i.ToString() == stringValue.
Луань

0

Будь-яке множення може бути замінено повторним складанням. Таким чином, ви можете замінити будь-яке множення в існуючому алгоритмі версією, яка використовує лише додавання:

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

Ви можете піти далі та написати спеціалізовану строку для Int, використовуючи цю ідею, яка мінімізує кількість доповнень (негативне число та обробка помилок, опущені для стислості):

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

Але я не думаю, що це варте клопоту, оскільки виступ тут, здається, не викликає занепокоєння.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.