没有原生类型的数字最有效的表示是什么?

时间:2015-09-04 21:52:39

标签: haskell functional-programming adt

在Haskell上,大多数数字类型都是原生数据 - Int,Float,Word32等。还有一种仅使用ADT的一元自然数的流行表示 - 即Peano编码:

data Nat = Succ Nat | Zero

该数据类型虽然优雅,但效率不高。乘法,取幂,一元数除法是不切实际的。我的问题是:如果我们没有依赖本机类型,那么数字的最有效表示 - 自然,整数,压缩,复杂等等 - 在纯粹的功能中像哈斯克尔这样的语言?数据类型和各自的算法是什么样的?

3 个答案:

答案 0 :(得分:3)

当提到Haskell和ghc时,左派已经评论了关于术语“最有效”的歧义。如果您真正要问的是,“在支持它们的语言中,使用ADT(代数数据类型)的数字编码效率会更高吗?”,那么我会指向Basics chapter in Software Foundations,其中你可以练习定义自然数的二进制表示。

书中的一句话:

  

练习:3星(二进制)

     

考虑一种不同的,更有效的自然数表示   使用二元而不是一元系统。也就是说,而不是说   每个自然数是零或自然的后继   数字,我们可以说每个二进制数都是

     
      
  • 零,
  •   
  • 二进制数的两倍,
  •   
  • 或二进制数的两倍以上。
  •   
     

(a)首先,写一个bin对应类型的归纳定义   对二进制数的描述。

     

(提示:回想一下nat的定义,

Inductive nat : Type :=
  | O : nat
  | S : nat → nat.

答案 1 :(得分:1)

这在很大程度上取决于您想要对数字做什么,以及效率最高的是什么意思。

如果要表示自然数 n ,则需要 log n 位信息。并且由于ADT只能有有限多个不同的构造函数,因此它会编码有限数量的位,因此您需要具有至少 log n 节点的结构。

我非常推荐Chris Okasaki的Functional Data Structures章节数字表示(论文可在线获取here)。它描述了各种树状数据结构,支持不同的操作集,以及它们与自然数的关系。以下所有内容都是我从本书中学到的。

扩展Cirdec的评论:您可以定义

data N = Zero | Positive
data Positive = One | Add Positive Positive

这为您提供 O(1)加法并减去1。另一方面,结构的大小将是 O(n)

您可以使用带有 O(log n)空间的二进制表示,但是添加将是 O(log n)

data N = Zero | Positive
data Positive = One | Twice Positive | TwicePlusOne Positive

增量和减量几乎为amortized O(1)。只有每个 2 ^ d 操作的增量序列才会深入 d ,所以平均来说,每个增量都是 O(1)。同样适用于decerements。我上面说几乎,因为如果你交换增量和减量,那么你可以在 O(log n)递增和递减操作之间切换。解决方案是添加一些冗余:

data N = Zero | Positive
data Positive = One | Twice Positive | TwicePlusOne Positive | TwicePlusTwo Positive

现在每次操作需要更深一层时,它会使当前节点保持在TwicePlusOne,这意味着影响节点的下一个操作将停在它上面,无论它是增量还是减量。

如果你想要恒定时间添加,可以为此扩展数据结构(在书中查找有效连接列表),但是你可以再次结束如果操作顺序不好,则使用 O(n)内存。有一个开放的SO问题How can natural numbers be represented to offer constant time addition?询问是否可以同时获得这两个问题。

答案 2 :(得分:0)

正如@Cirdec已经评论过的那样,这个问题在编程语言方面没有意义。只有特定的编程问题才有意义。

例如,如果您需要确切的数字,您可能希望将有理数表示为连续分数。其中OTOH可能不适合记账。