Как преобразовать строку, представляющую десятичное число, в строку, представляющую его двоичную форму?

#string #binary #decimal #base-conversion

#строка #двоичный #десятичное #базовое преобразование

Вопрос:

Возьмем, к примеру, строку s="556852144786" , представляющую десятичное число n=556852144786 . Существует ли прямой алгоритм для ее преобразования в s1="1000000110100110..." 1000000110100110... двоичное представление where n ?

Ответ №1:

Я предполагаю, что вы ищете алгоритм, который работает непосредственно со строками, вместо преобразования в любые целые числа, поддерживаемые целевым языком, и их использования?

Для этого существуют различные способы. Вот два, хотя, если вы прищуритесь достаточно сильно, они представляют собой почти один и тот же алгоритм.

Метод 1. повторно разделите десятичную строку на два

В этом методе мы повторно делим исходную десятичную строку на 2 (используя десятичную арифметику) и отслеживаем остатки: эти остатки дают вам биты результата в обратном порядке. Вот как выглядит этот алгоритм в Python. В нем отсутствуют определения is_nonzero и divide_by_two . Мы предоставим их позже.

 def dec_to_bin(s):
    """
    Convert decimal string s to binary, using only string operations.
    """
    bits = ""
    while is_nonzero(s):
        s, bit = divide_by_two(s)
        bits  = bit
    # The above builds up the binary string in reverse.
    return bits[::-1]
  

Алгоритм генерирует биты в обратном порядке, поэтому нам нужен окончательный реверс, чтобы получить результирующую двоичную строку.

divide_by_two Функция принимает десятичную строку s и возвращает новую десятичную строку, представляющую частное s / 2 вместе с остатком. Остаток представляет собой один бит, снова представленный в виде строки — или "0" или "1" . Это следует обычному методу деления слева направо, которому учат в школе: каждый шаг включает в себя деление одной цифры вместе с переносом, полученным из предыдущего шага, на два. Вот эта функция:

 def divide_by_two(s):
    """
    Divide the number represented by the decimal string s by 2,
    giving a new decimal string quotient and a remainder bit.
    """
    quotient = ""
    bit = "0"
    for digit in s:
        quotient_digit, bit = divide_digit_by_two(bit, digit)
        quotient  = quotient_digit
    return quotient, bit
  

Нам осталось определить, который принимает одну цифру плюс десятичный бит переноса и делит на два, чтобы получить цифру и остаток в один бит. divide_digit_by_two На этом этапе все входные и выходные данные представляют собой строки длиной один. Здесь мы обманываем и используем целочисленную арифметику, но существует только 20 возможных различных комбинаций входных данных, поэтому вместо этого мы могли бы легко использовать таблицу поиска.

 def divide_digit_by_two(bit, digit):
    """
    Divide a single digit (with tens carry) by two, giving
    a digit quotient and a single bit remainder.
    """
    digit, bit = divmod(int(bit) * 10   int(digit), 2)
    return str(digit), str(bit)
  

Вы можете представить divide_digit_by_two себе примитивную арифметическую операцию, которая меняет местами две цифры в разных базисах: она преобразует неотрицательное целое число, меньшее, чем 20 представлено в форме 10 * bit digit , в то же значение, представленное в форме 2 * digit bit .

Нам все еще не хватает одного определения: определения is_nonzero . Десятичная строка представляет ноль тогда и только тогда, когда она полностью состоит из нулей. Вот быстрый тест на Python для этого.

 def is_nonzero(s):
    """
    Determine whether the decimal string s represents zero or not.
    """
    return s.strip('0') != ''
  

И теперь, когда у нас есть все биты на месте, мы можем протестировать:

 >>> dec_to_bin("18")
'10010'
>>> dec_to_bin("556852144786")
'1000000110100110111110010110101010010010'
>>> format(556852144786, 'b')  # For comparison
'1000000110100110111110010110101010010010'
  

Способ 2. многократно умножьте двоичную строку на 10

Вот вариант первого метода: вместо повторяющихся делений мы обрабатываем входящую десятичную строку цифра за цифрой (слева направо). Мы начинаем с пустой двоичной строки, представляющей значение 0 , и каждый раз, когда мы обрабатываем цифру, мы умножаем на 10 (в двоичном представлении) и добавляем значение, представленное этой цифрой. Как и раньше, удобнее всего создавать двоичную строку в порядке младшего конца (сначала младший значащий бит), а затем в обратном порядке в конце, чтобы получить традиционное представление в формате big-endian. Вот функция верхнего уровня:

 def dec_to_bin2(s):
    """
    Convert decimal string s to binary, using only string operations.

    Digit-by-digit variant.
    """
    bits = ""
    for digit in s:
        bits = times_10_plus(bits, digit)
    return bits[::-1]
  

Задача times_10_plus функции состоит в том, чтобы взять двоичную строку и цифру и создать новую двоичную строку, представляющую результат умножения значения оригинала на десять и добавления значения двоичной цифры. Это выглядит так:

 def times_10_plus(bits, digit):
    """
    Multiply the value represented by a binary string by 10, add a digit,
    and return the result as a binary string.
    """
    result_bits = ""
    for bit in bits:
        digit, result_bit = divide_digit_by_two(bit, digit)
        result_bits  = result_bit
    while digit != "0":
        digit, result_bit = divide_digit_by_two("0", digit)
        result_bits  = result_bit
    return result_bits
  

Обратите внимание, что мы используем точно такой же арифметический примитив divide_digit_by_two , как и раньше, но теперь мы думаем о нем немного по-другому: он умножает один бит на десять, добавляет переносную цифру и превращает ее в новый бит (младший значащий бит результата) вместе с новой переносной цифрой.

Примечания по эффективности

В интересах ясности я оставил некоторые недостатки на уровне Python. В частности, при создании строк было бы более эффективно создать список битов или цифр, а затем выполнить конкатенацию с str.join помощью операции в самом конце. Также обратите внимание, что в зависимости от языка реализации может иметь смысл изменять либо строку цифр, либо строку битов на месте, вместо того, чтобы генерировать новую строку на каждом шаге. Я оставляю необходимые изменения на ваше усмотрение.