简体   繁体   English

如何表示自然数以提供恒定的时间加法?

[英]How can natural numbers be represented to offer constant time addition?

Cirdec's answer to a largely unrelated question made me wonder how best to represent natural numbers with constant-time addition, subtraction by one, and testing for zero. Cirdec对一个基本无关的问题的回答使我想知道如何最好地表示自然数并进行恒定的加法,减一并测试零。

Why Peano arithmetic isn't good enough: 为什么Peano算术不够好:

Suppose we use 假设我们使用

data Nat = Z | S Nat

Then we can write 然后我们可以写

Z + n = n
S m + n = S(m+n)

We can calculate m+n in O(1) time by placing mr debits (for some constant r ), one on each S constructor added onto n . 我们可以通过放置mr借记项(对于某个常数r ),在O(1)时间内计算m+n在添加到n每个S构造函数上加一个。 To get O(1) isZero , we need to be sure to have at most p debits per S constructor, for some constant p . 为了获得O(1) isZero ,对于某个常数p ,我们需要确保每个S构造函数最多具有p借方。 This works great if we calculate a + (b + (c+...)) , but it falls apart if we calculate ((...+b)+c)+d . 如果我们计算a + (b + (c+...)) ,那么效果很好,但是如果我们计算((...+b)+c)+d ,它就会崩溃。 The trouble is that the debits stack up on the front end. 问题在于借方堆积在前端。

One option 一种选择

The easy way out is to just use catenable lists, such as the ones Okasaki describes, directly. 简单的方法是直接使用可链接的列表,例如Okasaki所描述的列表。 There are two problems: 有两个问题:

  1. O(n) space is not really ideal. O(n)空间不是很理想。

  2. It's not entirely clear (at least to me) that the complexity of bootstrapped queues is necessary when we don't care about order the way we would for lists. 尚不清楚(至少对我来说),当我们不关心列表的顺序时,是否需要引导队列的复杂性。

As far as I know, Idris (a dependently-typed purely functional language which is very close to Haskell) deals with this in a quite straightforward way. 据我所知,Idris(一种依赖类型的纯函数式语言,与Haskell非常接近)以一种非常直接的方式处理该问题。 Compiler is aware of Nat s and Fin s (upper-bounded Nat s) and replaces them with machine integer types and operations whenever possible, so the resulting code is pretty effective. 编译器了解NatFin (上限Nat ),并在可能的情况下将它们替换为机器整数类型和运算,因此生成的代码非常有效。 However, that's not true for custom types (even isomorphic ones) as well as for compilation stage (there were some code samples using Nat s for type checking which resulted in exponential growth in compile-time, I can provide them if needed). 但是,对于自定义类型(甚至是同构类型)以及编译阶段,情况并非如此(有一些代码示例使用Nat进行类型检查,这导致编译时呈指数增长,我可以根据需要提供它们)。

In case of Haskell, I think a similar compiler extension may be implemented. 对于Haskell,我认为可以实现类似的编译器扩展。 Another possibility is to make TH macros which would transform the code. 另一种可能性是制作TH宏来转换代码。 Of course, both of options aren't easy. 当然,这两种选择都不容易。

My understanding is that in basic computer programming terminology the underlying problem is you want to concatenate lists in constant time. 我的理解是,在基本的计算机编程术语中,潜在的问题是您希望在固定时间内连接列表。 The lists don't have cheats like forward references, so you can't jump to the end in O(1) time, for example. 列表没有前向引用之类的作弊手段,因此,例如,您不能在O(1)时间内跳到最后。

You can use rings instead, which you can merge in O(1) time, regardless if a+(b+(c+...)) or ((...+c)+b)+a logic is used. 您可以改用环,无论使用a+(b+(c+...))还是((...+c)+b)+a逻辑,都可以在O(1)时间合并。 The nodes in the rings don't need to be doubly linked, just a link to the next node. 环中的节点不需要双重链接,只需链接到下一个节点即可。

Subtraction is the removal of any node, O(1), and testing for zero (or one) is trivial. 减法是除去任何节点O(1),测试零(或一个)是微不足道的。 Testing for n > 1 is O(n), however. 但是,对于n > 1测试为O(n)。

If you want to reduce space, then at each operation you can merge the nodes at the insertion or deletion points and weight the remaining ones higher. 如果要减少空间,则可以在每次操作时在插入或删除点处合并节点,并对其余节点进行加权。 The more operations you do, the more compact the representation becomes! 您执行的操作越多,表示就越紧凑! I think the worst case will still be O(n), however. 我认为最坏的情况仍然是O(n)。

We know that there are two "extremal" solutions for efficient addition of natural numbers: 我们知道,有两种“极值”解决方案可以有效地相加自然数:

  1. Memory efficient, the standard binary representation of natural numbers that uses O(log n) memory and requires O(log n) time for addition. 内存有效,自然数的标准二进制表示形式,使用O(log n)内存,并且需要O(log n)时间进行加法。 (See also Chapter "Binary Representations" in the Okasaki's book .) (另请参见冈崎书中的 “二进制表示”一章。)
  2. CPU efficient which use just O(1) time. 仅使用O(1)时间的CPU效率。 (See Chapter "Structural Abstraction" in the book.) However, the solution uses O(n) memory as we'd represent natural number n as a list of n copies of () . (请参见本书中的“结构抽象”一章。)但是,该解决方案使用O(n)内存,因为我们将自然数n表示为()n个副本的列表。

    I haven't done the actual calculations, but I believe for the O(1) numerical addition we won't need the full power of O(1) FIFO queues, it'd be enough to bootstrap standard list [] (LIFO) in the same way. 我尚未进行实际计算,但是我相信对于O(1)数值加法,我们将不需要O(1) FIFO队列的全部功能,足以引导标准列表[] (LIFO)以同样的方式。 If you're interested, I could try to elaborate on that. 如果您有兴趣,我可以尝试详细说明。

The problem with the CPU efficient solution is that we need to add some redundancy to the memory representation so that we can spare enough CPU time. CPU高效解决方案的问题在于,我们需要为内存表示添加一些冗余,以便我们可以节省足够的CPU时间。 In some cases, adding such a redundancy can be accomplished without compromising the memory size (like for O(1) increment/decrement operation). 在某些情况下,可以在不牺牲内存大小的情况下实现添加此类冗余的操作(例如O(1)递增/递减操作)。 And if we allow arbitrary tree shapes, like in the CPU efficient solution with bootstrapped lists, there are simply too many tree shapes to distinguish them in O(log n) memory. 而且,如果我们允许任意树形,例如在具有自举列表的CPU高效解决方案中, 树形太多无法O(log n)内存中加以区分。

So the question is: Can we find just the right amount of redundancy so that sub-linear amount of memory is enough and with which we could achieve O(1) addition? 所以问题是:我们能否找到合适数量的冗余,以便亚线性内存足够,并且可以实现O(1)加法? I believe the answer is no : 我相信答案是否定的

Let's have a representation+algorithm that has O(1) time addition. 让我们有一个具有O(1)时间加法的表示形式+算法。 Let's then have a number of the magnitude of m -bits, which we compute as a sum of 2^k numbers, each of them of the magnitude of (mk) -bit. 然后,我们得到m位的大小,我们将其计算为2 ^ k个数字的总和,每个数字都为(mk)位的大小。 To represent each of those summands we need (regardless of the representation) minimum of (mk) bits of memory, so at the beginning, we start with (at least) (mk) 2^k bits of memory. 为了表示每个求和数,我们需要(不考虑表示形式)最少(mk)位存储器,因此,一开始,我们从(至少) (mk)2 ^ k位存储器开始。 Now at each of those 2^k additions, we are allowed to preform a constant amount of operations, so we are able to process (and ideally remove) total of C 2^k bits. 现在,在这2 ^ k个加法中的每个加法中,我们都可以执行恒定数量的操作,因此我们能够处理(理想情况下,删除)总共C 2 ^ k个比特。 Therefore at the end, the lower bound for the number of bits we need to represent the outcome is (mkC) 2^k bits. 因此,最后,我们需要表示结果的位数的下限是(mkC)2 ^ k位。 Since k can be chosen arbitrarily, our adversary can set k=mC-1 , which means the total sum will be represented with at least 2^(mC-1) = 2^m/2^(C+1) ∈ O(2^m) bits. 由于k可以任意选择,我们的对手可以设置k = mC-1 ,这意味着总和将至少用2 ^(mC-1)= 2 ^ m / 2 ^(C + 1)∈O( 2 ^ m)位。 So a natural number n will always need O(n) bits of memory! 因此,自然数n总是需要O(n)位内存!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用原始递归添加二进制自然数 - Addition for binary natural numbers using primitive recursion 证明自然数的类型级加法的交换性 - Proving commutativity of type level addition of natural numbers 在自然数上使用加法函数,给出自然数乘法的递归定义吗? - Using the addition function over the natural numbers, give a recursive definition of multiplication of natural numbers? 是否有自然数的代数表示形式允许并行加法? - Is there any algebraic representation of natural numbers that allow for parallel addition? 如何在Haskell中将自然数范围定义为char? - How to define range of natural numbers as chars in Haskell? 如何从1…n生成自然数列表? - How to generate a list of natural numbers from 1…n? 自然数的初代数 - Initial algebra for natural numbers 自然数作为递归数据类型 - Natural numbers as a recursive datatype 如何定义一个函数,该函数采用数据类型的自然数并返回它们的总和? - How to define a function that takes natural numbers of a data type and return their sum? 如何使用自然数的折叠来定义斐波那契数列? - How to define the fibonacci sequence using a fold for natural numbers?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM