二进制转三进制表示转换

8

有没有人知道(或可以指向一些阅读资料)将二进制数字系统表示的数字转换为三进制数字系统(我的特定情况)的方法或算法,或者通用算法用于这种转换?

我已经实现的解决方案是先将数字转换为十进制,然后再将其转换为所需的数字系统。 这有效,但需要两个步骤。 我想知道是否可以轻松地在不首先实现三进制算术的情况下完成一步? 有什么诀窍吗,伙计们?

更新: 看来我没有清楚地描述我正在寻找的转换方式。 我不是要求某些方法将二进制转换为三进制,我知道如何做到这一点。 您可以考虑我在Haskell中具有三进制和二进制数字的代数数据结构,它看起来像这样:

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

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

有两种明显的方法可以将一个转换为另一个:第一种是先将其转换为整数,然后获取结果(不是很有趣的方法),第二种是在三进制中实现自己的乘法和加法,并通过将数字值乘以各自的2次幂来计算结果(直接而繁重)。

所以我想知道是否有其他方法。


你是如何将其转换为十进制的?为什么同样的方法不能用于三进制的转换? - Stephen Canon
@Stephen:他正在寻找直接转换,不需要先转换为十进制。 - Jeff Mercado
1
@Jeff M:没错。他直接从二进制转换到十进制,没有任何中间转换。同样的算法也可以让他在没有任何中间转换的情况下从二进制转换到三进制。 - Stephen Canon
@Stephen 经典的转换方式是:a2^0 + b2^1等,因此我将不得不实现三进制算术中的乘法。为了找到一些巧妙而快捷的方法,我希望避免这种情况,只是出于好玩。 - Vadim Fedorov
@Stephen:啊,我明白你的意思了。 - Jeff Mercado
@Vadim Fedorov:啊,你可以不需要三元算术就能做到这一点。 - Stephen Canon
9个回答

6

如果你使用计算机进行操作,那么事情已经是二进制的了,所以只需不断地除以3并取余数就足够简单了。

如果你手动操作,二进制下的长除法与十进制下的长除法完全相同。只需除以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表示的是什么。

实际上,我需要在程序中实现“手动”算法,因为用于计算的数据是具有数字0/1状态的数据结构,而不是一些整数。 - Vadim Fedorov
经过思考,总是会有一个隐式转换为十进制。在每一步中,始终考虑十进制表示。100 二进制(4 十进制)除以 113)。它仍在被转换。 - Jeff Mercado
@Vadim 你最好将你的数据结构转换成数字并使用计算机算术。 - deinst
6
@Jeff M: 没有到十进制的隐式转换。 除法在任何进制下都是相同的-它是定义在整数本身上的,而不是定义在其在任何特定进制下的表示上。 - Stephen Canon

3
您可以使用一些巧妙的缩写进行转换。以下代码是“错误”的方向,它是基于3^2 = 2^3 + 1这个事实,仅使用二进制加法将三进制转换为二进制。基本上我正在将两个三进制数字转换成三个二进制数字。从二进制到三进制会稍微复杂一些,因为需要三进制加法(可能还需要减法)(正在研究中)。我假设列表中最不重要的数字在头部(这是唯一有意义的方式),所以您必须“反向”读取数字。
addB :: BNumberBNumberBNumber
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 :: TNumberBNumber
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)

[编辑] 这里是从二进制到三进制的转换方法,预计会有更多的步骤:
addT :: TNumberTNumberTNumber
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 :: TNumberTNumberTNumber
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 :: BNumberTNumber
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)

[编辑2] subT的稍微改进版,不需要addT。

subT :: TNumberTNumberTNumber
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)

是的,这就是我自己一直在想的解决方案,但它很繁琐。我相信通过生成转换模板而不是在模式匹配中指定所有内容,可以使其更简短易懂。 - Vadim Fedorov

3

我认为大家都忽略了一些重要的东西。首先,预先计算一个表格,对于每个二进制位,我们需要用三进制表示。在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

是的,这些不是三进制数,但它们几乎是有效的三进制表示。现在你需要做的就是进行进位操作。从个位开始。

5比2大,所以减去3的倍数,并相应地增加结果的第二位数字。

0013322

第二位数字现在是2,这是一个合法的三进制数字,所以继续处理第三位。同样进行进位操作,

0014022

最终产生了完全有效的三进制数...
0021022

我的计算是否正确?我将让MATLAB为我们做出最终判断:

base2dec('011000101',2)
ans =
   197

base2dec('0021022',3)
ans =
   197

我是否已经指出这个操作是多么简单,我可以完全手动进行转换,基本上直接从二进制到三进制,至少在我写下并存储了初始表格后如此?


我要注意的是,只要你先计算出适当的表格,这种方法基本上适用于任何两个数字进制之间的直接转换。因此,如果您希望将基数3转换为基数7,并且需要多次进行转换,那么您可以高效地完成它。 - user85109

1

很抱歉,我不太熟悉 Haskell 语言,无法用代码表达,但我想知道是否可以使用霍纳算法来求解多项式。

例如,ax^2 + bx + c 可以表示为 c+x*(b+x*a)。

为了将三进制数 a*9+b*3+c 转换为二进制数,需要从 a 的二进制表示开始,然后将其乘以 3(即进行位移和加法),再加上 b 的二进制表示,将结果乘以 3 并添加 c。

在我看来,这应该可以通过 map(获取三进制数字的二进制表示)和 fold(a、b -> a+3*b)来完成。


0
如果这是一道作业题,写出将xb为底的反向伪代码:
while (x != 0) {
    q <-- x/b
    r <-- x - q*b
    print r
    x <-- q
}

我相信你可以想出如何将结果正向而非反向地编写。请注意,/ 需要是 C 风格的整数除法(结果是一个整数,向零截断)。

请注意,这与算术运算所执行的基数完全无关。算术运算定义在整数上,而不是在特定基数中表示整数。


编辑:根据您更新的问题,我会将数字表示通过“或”和移位操作转换为整数,并使用上述算法进行整数运算。

当然,您可以按照您描述的方式进行操作,但这似乎是一项非常繁琐的工作。


请查看问题的更新,我澄清了它的内容。 - Vadim Fedorov

0

我认为没有超级高效的方法。

“我已经实现的解决方案是先将数字转换为十进制。”

我猜你实际上是先转换为某个内置整数类型。我不认为这个内置整数与十进制有任何关系。(尽管在打印时会进行十进制转换)。

也许你期望有一些算法,它可以逐位查看输入并生成输出。

但是,假设你想将3486784400(十进制)转换为三进制。你需要在生成输出之前检查每个数字,因为

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

..还有

"计算结果,将数字值乘以二的相应幂次方"

不需要显式地计算幂次方,请参见从60进制转换为10进制


我已经实现的解决方案是先将数字转换为十进制。这是错误的,只需将提到的代数类型(BNumber)转换为整数即可。 - Vadim Fedorov

0

我认为这个问题可能有一些不同的“视角”,尽管我不确定它们中的任何一个是否更快或更好。例如,n的低阶3进制数字就是n mod 3。假设您已经有了n的二进制表示形式。然后考虑2的幂如何在模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之间交替。现在,您可以通过扫描n的二进制表示形式并通常仅在出现1的位位置处添加1或2来轻松获取低阶3进制数字。


0

不,你不能在不将它加载到整数中的情况下将二进制数转换为三进制数。原因是2和3是互质的-它们没有公共因子。

如果你使用的是二进制和四进制,或者甚至是六进制和九进制,那么最低公共倍数之前的整数集将由两个同构集表示。例如13(base4)= 0111(base2),因此将1313(base4)转换为01110111(base2)-这是一项查找和替换操作。

至少你拥有的解决方案有效且相对简单。如果需要提高性能,则在开始进行三进制转换之前将整个二进制表示转换为整数;这意味着减少了模运算次数。另一种选择就是逐个处理二进制数字中的每个字符,在这种情况下,你将为二进制表示中的每个数字除以所有3的幂。


看起来@woodchips能够做到你说不可能做到的事情。 - President James K. Polk
他并未加载成整数,而是加载为非二进制、非三进制表示以进行翻译。这是一个聪明的解决方案,但仍然是一种中间语言,因此我认为它并没有显著区别。例如,在二进制或三进制中如何解决01 + 01 + 01 = 03?你做不到。 - Kirk Broadhurst

0

如果您使用二进制编码的三进制(每个三进制位使用一对比特),则可以使用并行算术进行转换。请参见本教程


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接