Преобразование двоичного представления в троичное

кто-нибудь знает (или может указать на какой-то источник для чтения) метод или алгоритм преобразования числа, представленного в двоичной системе счисления в троичную (мой частный случай), или универсальный алгоритм для таких преобразований?

решение, которое я уже реализовал, - сначала преобразовать число в десятичное, а затем преобразовать его в требуемую систему цифр. Это работает, но есть два шага. Интересно, можно ли это сделать за один шаг легко без реализации сначала троичная арифметика? Есть какой-то трюк, ребята?

UPD: кажется, мне не удалось четко описать, какой способ преобразования я ищу. Я не прошу некоторые способ преобразования базы-2 в базу-3, я знаю, как это сделать. Вы можете считать, что у меня есть алгебраические структуры данных для троичных и двоичных чисел, в Haskell это выглядит так:

data BDigit = B0 | B1
type BNumber = [BDigit]

data TDigit = T0 | T1 | T2
type TNumber = [TDigit]

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

поэтому мне интересно, есть ли другой метод, чем эти два.

8 ответов


вы можете использовать некоторые умные аббревиатуры для преобразования. Следующий код является "неправильным" направлением, это преобразование из троичного в двоичный, основанное на том, что 3^2 = 2^3 + 1 с использованием только двоичного сложения. В основном я преобразовываю две тройные цифры в три двоичные цифры. От двоичного к троичному будет немного сложнее, так как потребуется троичное сложение (и, вероятно, вычитание) (работа над этим). Я предполагаю, что наименее значимая цифра в голове списка (которая единственный способ, который имеет смысл), поэтому вы должны прочитать цифры "назад".

addB :: BNumber → BNumber → BNumber
addB a [] = a
addB [] b = b
addB (B0:as) (B0:bs) = B0 : (addB as bs) 
addB (B0:as) (B1:bs) = B1 : (addB as bs)
addB (B1:as) (B0:bs) = B1 : (addB as bs)
addB (B1:as) (B1:bs) = B0 : (addB (addB as bs) [B1])

t2b :: TNumber → BNumber
t2b [] = []
t2b [T0] = [B0]
t2b [T1] = [B1]
t2b [T2] = [B0,B1]
t2b (T2:T2:ts) = let bs = t2b ts in addB bs (B0:B0:B0:(addB bs [B1]))
t2b (t0:t1:ts) = 
   let bs = t2b ts
       (b0,b1,b2) = conv t0 t1
   in addB bs (b0:b1:b2:bs) 
   where conv T0 T0 = (B0,B0,B0)
         conv T1 T0 = (B1,B0,B0)
         conv T2 T0 = (B0,B1,B0)
         conv T0 T1 = (B1,B1,B0)
         conv T1 T1 = (B0,B0,B1)
         conv T2 T1 = (B1,B0,B1)
         conv T0 T2 = (B0,B1,B1)
         conv T1 T2 = (B1,B1,B1)

[Edit] вот двоичное к троичному направлению, как и ожидалось, немного длиннее:

addT :: TNumber → TNumber → TNumber
addT a [] = a
addT [] b = b
addT (T0:as) (T0:bs) = T0 : (addT as bs) 
addT (T1:as) (T0:bs) = T1 : (addT as bs)
addT (T2:as) (T0:bs) = T2 : (addT as bs)
addT (T0:as) (T1:bs) = T1 : (addT as bs) 
addT (T1:as) (T1:bs) = T2 : (addT as bs)
addT (T2:as) (T1:bs) = T0 : (addT (addT as bs) [T1])
addT (T0:as) (T2:bs) = T2 : (addT as bs)
addT (T1:as) (T2:bs) = T0 : (addT (addT as bs) [T1])
addT (T2:as) (T2:bs) = T1 : (addT (addT as bs) [T1])

subT :: TNumber → TNumber → TNumber
subT a [] = a
subT [] b = error "negative numbers supported"
subT (T0:as) (T0:bs) = T0 : (subT as bs) 
subT (T1:as) (T0:bs) = T1 : (subT as bs)
subT (T2:as) (T0:bs) = T2 : (subT as bs)
subT (T0:as) (T1:bs) = T2 : (subT as (addT bs [T1])) 
subT (T1:as) (T1:bs) = T0 : (subT as bs)
subT (T2:as) (T1:bs) = T1 : (subT as bs)
subT (T0:as) (T2:bs) = T1 : (subT as (addT bs [T1]))
subT (T1:as) (T2:bs) = T2 : (subT as (addT bs [T1]))
subT (T2:as) (T2:bs) = T0 : (subT as bs)

b2t :: BNumber → TNumber
b2t [] = []
b2t [B0] = [T0]
b2t [B1] = [T1]
b2t [B0,B1] = [T2]
b2t [B1,B1] = [T0,T1]
b2t (b0:b1:b2:bs) = 
   let ts = b2t bs
       (t0,t1) = conv b0 b1 b2
   in subT (t0:t1:ts) ts
   where conv B0 B0 B0 = (T0,T0)
         conv B1 B0 B0 = (T1,T0)
         conv B0 B1 B0 = (T2,T0)
         conv B1 B1 B0 = (T0,T1)
         conv B0 B0 B1 = (T1,T1)
         conv B1 B0 B1 = (T2,T1)
         conv B0 B1 B1 = (T0,T2)
         conv B1 B1 B1 = (T1,T2)

[Edit2] немного улучшенная версия subT, которая не нуждается в addT

subT :: TNumber →  TNumber →  TNumber
subT a [] = a
subT [] b = error "negative numbers supported"
subT (a:as) (b:bs) 
  | b ≡ T0 = a : (subT as bs)
  | a ≡ b =  T0 : (subT as bs)
  | a ≡ T2 ∧ b ≡ T1 =  T1 : (subT as bs)
  | otherwise = let td = if a ≡ T0 ∧ b ≡ T2 then T1 else T2 
                in td : (subT as $ addTDigit bs T1)  
    where addTDigit [] d = [d]
          addTDigit ts T0 =  ts
          addTDigit (T0:ts) d = d:ts 
          addTDigit (T1:ts) T1 = T2:ts
          addTDigit (t:ts) d = let td = if t ≡ T2 ∧ d ≡ T2 then T1 else T0
                               in td : (addTDigit ts T1)

Если вы делаете это с компьютером, вещи уже находятся в двоичном формате, поэтому просто многократное деление на 3 и взятие остатков примерно так же просто, как и все.

Если вы делаете это вручную, длинное деление в двоичном формате работает так же, как длинное деление в десятичном. просто разделите на три и возьмите остатки. если мы начнем с 16

   ___101
11 |10000
     11
      100
       11
        1   

100000 / 11 = 101 + 1/11 so the least significnnt digit is 1

101/ 11 = 1 + 10/11  the next digit is 2

1  and the msd is 1

Так в троичном 121


Я думаю, что все упускают что-то важное. Во-первых, вычислите таблицу заранее, для каждого двоичного бита нам нужно представление в троичном виде. В MATLAB я построил его так, хотя каждый второй шаг после этого будет выполняться исключительно вручную, вычисление так просто.

dec2base(2.^(0:10),3)
ans =
0000001
0000002
0000011
0000022
0000121
0001012
0002101
0011202
0100111
0200222
1101221

теперь рассмотрим двоичное число 011000101 (которое является десятичным числом 197, как мы узнаем позже.) Извлеките троичное представление для каждого двоичного бита из таблица. Я напишу соответствующие строки.

0000001 
0000011
0002101
0011202

теперь просто сумма. Мы получаем это представление в незамужней Троице.

0013315

Да, это не троичные числа, но они почти в действительном представлении базы 3. Теперь все, что вам нужно сделать, это сделать перенос. Начните с цифры единиц.

5 больше 2, поэтому вычтите число кратных 3 и увеличьте вторую цифру результата как соответствующий.

0013322

вторая цифра теперь 2, законная тройная цифра, поэтому перейдите к третьей цифре. Сделай это тоже,

0014022

наконец, давая теперь полностью действительное тройное число...

0021022

были ли мои вычисления правильными? Я позволю MATLAB сделать окончательное решение за нас:

base2dec('011000101',2)
ans =
   197

base2dec('0021022',3)
ans =
   197

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


боюсь, я недостаточно знаю Haskell, чтобы выразить это в коде, но мне интересно, может ли использование правила Хорнера для оценки многочленов дать метод.

например ax^2 + bx + c можно оценить как c+x*(b+x*a).

преобразовать, скажем, троичное число a * 9+b*3+c в двоичное, начинается с двоичного представления a, а затем умножается на 3 (i.e shift и add), затем добавляет двоичное представление b, умножает результат на 3 и добавляет c.

Мне кажется, это должно быть выполнимо с картой (чтобы получить двоичное представление троичных цифр) и складкой (из a,b -> a+3*b)


в случае, если это домашнее задание, псевдокод писать x базовый b назад:

while (x != 0) {
    q <-- x/b
    r <-- x - q*b
    print r
    x <-- q
}

Я уверен, что вы можете понять, как написать результат вперед, а не назад. Обратите внимание, что / должно быть целочисленное деление в стиле C (результат-целое число, усеченное до нуля).

обратите внимание, что это не зависит на всех на основании того, что выполняется арифметика. Арифметика определяется на целых числах, а не на представлении целые числа в определенной базе.


Edit: основываясь на вашем обновленном вопросе, я бы захлопнул представление цифр в целое число (через ors и shifts) и использовал алгоритм, описанный выше, с целочисленной арифметикой.

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


Я не думаю, что есть супер-эффективный способ.

" решение, которое я уже реализовал преобразовать число в десятичное первый."

Я предполагаю, что вы на самом деле сначала конвертируете в какой-то встроенный целочисленный тип. Я не думаю, что встроенное целое число имеет какое-либо отношение к базе 10. (Хотя, когда вы его печатаете, будет преобразование base 10).

возможно, вы ожидаете, что будет какой-то алгоритм, который смотрит на вход по одной цифре за раз и выдает результат.

но, скажем, вы хотите конвертировать 3486784400 (base 10) в base 3. Вам нужно будет изучить каждую цифру, прежде чем производить вывод, потому что

3486784401 (base 10) = 100000000000000000000 (base 3)
3486784400 (base 10) =  22222222222222222222 (base 3)

..также

"вычислить результат умножения цифр значения соответствующей мощности двух"

явно вычислять мощность не требуется, см. преобразовать из базы 60 в базу 10


Я думаю, что могут быть разные разные "взгляды" на проблему, хотя я не уверен, что любой из них быстрее или лучше. Например, нижняя 3-значная цифра N-это просто N mod 3. Допустим, у вас уже есть двоичное представление n. Затем рассмотрим, как силы 2 работают mod 3. 2^0 = 1 mod 3, 2^1 = 2 mod 3, 2^2 = 1 mod 3, 2^3 = 2 mod 3,... Другими словами, силы чередуются между бытием 1 mod 3 и бытием 2 mod 3. Теперь у вас есть простой способ получить low-order базовая цифра 3 путем сканирования двоичного представления n и обычно только сложение 1 или 2 в каждой битовой позиции, где происходит 1.


нет, вы не можете преобразовать число base2 в число base3 без загрузки его в целое число. Причина в том, что 2 и 3 взаимно просты - у них нет общих факторов.

Если бы вы работали с base2 и base4, или даже base6 и base9, то множество целых чисел до наименьшего общего кратного из двух оснований было бы представлено двумя изоморфными множествами. Например 13 (base4) = 0111 (base2) , поэтому преобразование 1313 (base4) = 01110111 ( base2) - это поиск и замена операция.

по крайней мере, решение, которое у вас работает и является довольно простым. Если вам нужно улучшить производительность, преобразуйте все представление base2 в целое число перед началом преобразования base3; это означает меньше операций модуля. Альтернативой будет обрабатывать каждый символ в base2 номер один за другим, и в этом случае вы будете делить на все степени 3 для каждой цифры в представлении base2.