【问题标题】:Binary to ternary representation conversion二进制到三进制表示转换
【发布时间】:2011-03-24 22:42:30
【问题描述】:

是否有人知道(或可能指向某些来源以了解)将二进制数字系统表示的数字转换为三进制数字系统的方法或算法(我的特殊情况),或用于此类转换的通用算法?

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

UPD: 看来我没有设法清楚地描述我正在寻找哪种转换方式。我不是要求 some 方法将 base-2 转换为 base-3,我确实知道如何做到这一点。你可能会认为我有三进制和二进制数的代数数据结构,在 Haskell 中它看起来像这样:

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

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

而且有两种很明显的方法可以将一个转换为另一个:第一个是先将其转换为 Integer 并得到结果(不是有趣的方式),第二个是在 base-3 中实现自己的乘法和加法并计算结果乘法数字值分别为 2 的幂(简单和重)。

所以我想知道除了这两个之外是否还有其他方法。

【问题讨论】:

  • 你是如何转换成十进制的?为什么同样的方法不能用于转换为三元?
  • @Stephen:他正在寻找直接转换,没有中间转换为十进制。
  • @Jeff M:对。他在没有任何中间转换的情况下从基数 2 转换为基数 10。相同的算法将允许他从基数 2 转换为基数 3,而无需任何中间转换。
  • @Stephen 经典转换:a*2^0 + b*2^1 等。所以我必须在三元算术中实现乘法。这是我想避免的,只是为了好玩,找到一些棘手而快速的方法。
  • @Stephen:啊,我明白你的意思了。

标签: algorithm math numbers theory ternary-representation


【解决方案1】:

如果你是用计算机做的,事情已经是二进制的了,所以只需反复除以 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 状态的数据结构,而不是某个整数。
  • 想了想,总有隐式转换成十进制。在每一步中,始终考虑十进制表示。 100 二进制(4 十进制)除以 113)。它仍在转换中。
  • @Vadim 几乎可以肯定,将数据结构转换为数字并使用计算机算术会更好。
  • @Jeff M:没有隐式转换为十进制。除法在任何基数中都是相同的——它是在整数本身上定义的,而不是在它们在任何特定基数中的表示上。
【解决方案2】:

您可以使用一些巧妙的缩写进行转换。以下代码是“错误”的方向,它是基于仅使用二进制加法的 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)

[编辑] 这里是二元到三元的方向,和预期的一样长一点:

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】:

我认为每个人都缺少一些重要的东西。首先,预先计算一个表,对于每个二进制位,我们需要以三进制表示。在 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 为基数的表示形式。现在你需要做的就是做carry。以个位数开头。

5 大于 2,因此减去 3 的倍数,并酌情增加结果的第二位。

0013322

第二个数字现在是 2,一个合法的三进制数字,所以继续看第三个数字。也这样做,

0014022

终于产生了现在完全有效的三进制数...

0021022

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

base2dec('011000101',2)
ans =
   197

base2dec('0021022',3)
ans =
   197

我有没有指出这个操作是多么微不足道,我可以完全手动完成转换,基本上直接从二进制到三进制,至少在我写下并存储了初始表之后?

【讨论】:

  • 我会注意到,只要您首先计算适当的表,这种方法基本上适用于任何两个数字基数之间的直接转换。因此,如果您希望将基数 3 转换为基数 7,并且不止一次这样做,那么您可以高效地完成。
【解决方案4】:

恐怕我对 Haskell 了解的不够多,无法在代码中表达这一点,但我想知道使用霍纳规则评估多项式是否会产生一种方法。

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

要转换,比如说, 三进制数 a*9+b*3+c 转为二进制,以 a 的二进制表示开始,然后将其乘以 3(即移位和加法),然后将 b 的二进制表示相加,将结果乘以 3 和添加 c。

在我看来,这应该可以通过映射(以获得三进制数字的二进制表示)和折叠(a,b -> a+3*b)来实现

【讨论】:

    【解决方案5】:

    如果这是家庭作业,请用伪代码将x 倒写在基础b 中:

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

    我相信您可以弄清楚如何将结果向前写入而不是向后写入。注意/需要是C风格的整数除法(结果是一个整数,向零截断)。

    请注意,这根本不依赖于执行算术的基数。算术是基于整数定义的,而不是特定基数的整数表示。


    编辑:根据您更新的问题,我会将数字表示形式转换为整数(通过 ors 和移位)并使用上述算法和整数运算。

    当然你可以按照你描述的那样做,但看起来工作量很大。

    【讨论】:

    • 请看问题的更新,我已经澄清了它是关于什么的。
    【解决方案6】:

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

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

    我假设您实际上是先转换为某些内置整数类型。我不认为内置整数与以 10 为底有任何关系。(虽然,当您打印它时,会有一个以 10 为底的转换)。

    也许您希望有某种算法可以一次查看输入一位数并产生输出。

    但是,假设您要将 3486784400(以 10 为底)转换为以 3 为底。您需要在生成输出之前检查每个数字,因为

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

    ..也

    "计算结果乘数 值分别为 2 的幂”

    不需要显式计算幂,请参阅convert from base 60 to base 10

    【讨论】:

    • “我已经实现的解决方案是先将数字转换为十进制。” - 这是错误的,只是将提到的代数类型(BNumber)转换为整数。
    【解决方案7】:

    我认为对于这个问题可能有一些不同的“观点”,尽管我不确定它们中的任何一个更快或更好。例如,n 的低位 3 位数字只是 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 之间交替。您现在可以通过扫描 n 的二进制表示并且通常只在每个位位置添加 1 或 2 来获得低位基数 3 数字出现 1 的地方。

    【讨论】:

      【解决方案8】:

      不,您不能将 base2 数字转换为 base3 数字而不将其加载到整数中。原因是 2 和 3 互质——它们没有公因数。

      如果您使用的是 base2 和 base4,甚至是 base6 和 base9,那么直到两个基数的最小公倍数的整数集将由两个同构集表示。例如 13 (base4) = 0111 (base2),所以转换 1313 (base4) = 01110111 (base2) - 这是一个查找和替换操作。

      至少您拥有的解决方案有效并且相对简单。如果您需要提高性能,则在开始 base3 转换之前将整个 base2 表示转换为整数;这意味着更少的模运算。另一种方法是逐个处理 base2 数字中的每个字符,在这种情况下,您将对 base2 表示中的每个数字除以 3 的所有幂。

      【讨论】:

      • 看起来@woodchips 能够做到你说做不到的事情。
      • 他没有加载到整数中,但他仍在加载到非二进制、非三进制表示中以用于翻译目的。这是一个聪明的解决方案,但它仍然是一种中介语言,所以我认为它没有显着不同。例如你如何解决二进制或三进制的 01 + 01 + 01 = 03 ?你不能。
      【解决方案9】:

      如果您使用二进制编码三进制(每个三位的一对位),您可以使用并行算术进行转换。见this tutorial

      【讨论】:

        猜你喜欢
        • 2023-04-11
        • 1970-01-01
        • 1970-01-01
        • 2019-07-15
        • 2021-12-07
        • 2016-02-09
        • 2016-08-26
        • 1970-01-01
        相关资源
        最近更新 更多