Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm 有没有有效的方法把一元数转换成二进制数?_Algorithm_Haskell_Functional Programming_Lambda Calculus - Fatal编程技术网

Algorithm 有没有有效的方法把一元数转换成二进制数?

Algorithm 有没有有效的方法把一元数转换成二进制数?,algorithm,haskell,functional-programming,lambda-calculus,Algorithm,Haskell,Functional Programming,Lambda Calculus,让这些数据类型分别表示一元和二元自然数: data UNat = Succ UNat | Zero data BNat = One BNat | Zero BNat | End u0 = Zero u1 = Succ Zero u2 = Succ (Succ Zero) u3 = Succ (Succ (Succ Zero)) u4 = Succ (Succ (Succ (Succ Zero))) b0 = End // 0 b1 = One End

让这些数据类型分别表示一元和二元自然数:

data UNat = Succ UNat | Zero
data BNat = One BNat | Zero BNat | End

u0 = Zero
u1 = Succ Zero
u2 = Succ (Succ Zero)
u3 = Succ (Succ (Succ Zero))
u4 = Succ (Succ (Succ (Succ Zero)))

b0 = End                   //   0
b1 = One End               //   1
b2 = One (Zero End)        //  10
b3 = One (One End)         //  11
b4 = One (Zero (Zero End)) // 100

(Alternatively, one could use `Zero End` as b1, `One End` as b2, `Zero (Zero End)` as b3...)
我的问题是:有没有办法实现该功能:

toBNat :: UNat -> BNat

O(N)
中工作,只通过一次UNat

如果我们有一个函数来增加一个
BNat
,我们可以通过沿着
UNat
运行,在每一步增加一个
BNat
来非常容易地做到这一点:

toBNat :: UNat -> BNat
toBNat = toBNat' End
    where
    toBNat' :: BNat -> UNat -> BNat
    toBNat' c Zero     = c
    toBNat' c (Succ n) = toBNat' (increment c) n
现在,这是
O(NM)
其中
M
increment
的最坏情况。因此,如果我们可以在O(1)中增加,那么答案是肯定的

下面是我在实现增量方面的尝试:

increment :: BNat -> BNat
increment = (reverse End) . inc' . (reverse End)
    where
    inc' :: BNat -> BNat
    inc' End      = One End
    inc' (Zero n) = One n
    inc' (One n)  = Zero (inc' n)

    reverse :: BNat -> BNat -> BNat
    reverse c End = c
    reverse c (One n) = reverse (One c) n

此实现是
O(N)
,因为您必须
反转
BNat
以查看最低有效位,这将为您提供
O(N)
整体。如果我们考虑<代码> BNat < /代码>类型来表示反向二进制数,我们不需要颠倒<代码> BNat <代码>,并且,正如@ AugStuSS所说,我们有O(1),它给了你O(n)的整体。

< P>增加二进制数字,你必须在数字的末尾翻转第一个零和前面的所有数字。此操作的成本与输入末尾的1的数量成正比(为此,您应将数字表示为从右到左的列表,例如13的列表[1;0;1;1]代码)

设a(n)为n结尾处1的数字:

a(n) = 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, ...

s(k) = a(2^k) + a(2^k+1) + ... + a(2^(k+1)-1) 
是2的两次幂之间的元素之和。你应该能够通过注意到这一点来说服自己s(k+1)=2*s(k)+1(s(0)=1)

    a(2^(k+1)) ..., a(2^(k+2) - 1) 
是通过连接获得的

    a(2^k) + 1, ..., a(2^(k+1) - 1) and   a(2^k), ..., a(2^(k+1) - 1)
因此,作为一个几何级数,s(k)=2^k-1

现在,将一个数字增加N倍的成本应该与

    a(0) + a(1) + ... + a(N)
  = s(0) + s(1) + s(2)  + ... + s(log(N)) 
  = 2^0 - 1 + 2^1 -1 + 2^2-1 + ... + 2^log(N) - 1
  = 2^0 + 2^1 + 2^2 + ... + 2^log(N) - log(N) - 1
  = 2^(log(N) + 1) - 1 - log(N) - 1 = 2N - log(N) - 2

因此,如果你注意从右到左表示数字,那么朴素的算法是线性的(注意,如果你真的需要相反的数字,你可以执行列表反转并保持线性)。

我喜欢其他答案,但我发现它们的渐近分析很复杂。因此,我提出另一个答案,有一个非常简单的渐近分析。基本思想是为一元数实现
divMod 2
。因此:

data UNat = Succ UNat | Zero
data Bit = I | O

divMod2 :: UNat -> (UNat, Bit)
divMod2 Zero = (Zero, O)
divMod2 (Succ Zero) = (Zero, I)
divMod2 (Succ (Succ n)) = case divMod2 n of
    ~(div, mod) -> (Succ div, mod)
现在我们可以通过迭代
divMod
将其转换为二进制

toBinary :: UNat -> [Bit]
toBinary Zero = []
toBinary n = case divMod2 n of
    ~(div, mod) -> mod : toBinary div
渐近分析现在非常简单。给定一个一元表示法中的数字
n
divMod2
需要O(n)个时间来产生一个一半大的数字,也就是说,如果
n
足够大,则最多需要
c*n
时间。因此,迭代此过程需要花费大量时间:

c*(n + n/2 + n/4 + n/8 + ...)

众所周知,这个级数收敛到
c*(2*n)
,所以
toBinary
也在O(n)中对于witness常量
2*c

,从一元数到任何其他基数的转换是复杂的,因为如果不先读取整数,就无法确定任何目标数字。为了让非Haskeller能够访问该问题,我添加了这个等价的问题。如果我们有一个很长的链表,我们可以在时间
O(N)
中计算它的长度
N
?注意,加法在这里不被认为是一个常数时间操作,因此简单的方法只能提供
O(nlogn)
。(最后一部分对这个问题至关重要,IMHO。)增加1等于摊销复杂性O(1),不是吗?(每个增量平均改变2位数字。)因此,只要对每个一元数字重复简单的ripple进位增量,就应该具有摊销复杂性O(N)。@Carsten只有在你愚蠢到使用大端表示时才这样做。:)当然,LSB应该是第一位的。如果你愿意的话,你可以在数位数的最后把它倒过来。我喜欢这种方法,因为直到现在才明白为什么常数是
2c
。不过,你能不能别再对我大喊大叫了?@Viclib对你大喊大叫?你什么意思?@Viclib哦,哈哈哈!在这种情况下,你可以通过在我的图片上滑动鼠标来让我闭嘴=多凯。。。不知道我对此有何感想。“看起来你在吃我的老鼠,是吗?”丹尼尔·瓦格纳也有一个非常简单的解释,解释为什么复杂性可以是线性的。