我有一台有1mb内存的电脑,没有其他本地存储。我必须使用它通过TCP连接接受100万个8位十进制数字,对它们进行排序,然后通过另一个TCP连接发送排序的列表。

数字列表可能包含重复的,我不能丢弃。代码将放在ROM中,所以我不需要从1 MB中减去我的代码的大小。我已经有了驱动以太网端口和处理TCP/IP连接的代码,它需要2 KB的状态数据,包括1 KB的缓冲区,代码将通过它读取和写入数据。这个问题有解决办法吗?

问答来源:

slashdot.org

cleaton.net


当前回答

如果我们对这些数字一无所知,我们就会受到以下约束:

我们需要在排序之前加载所有的数字, 这组数字是不可压缩的。

如果这些假设成立,则无法执行您的任务,因为您将需要至少26,575,425位的存储空间(3,321,929字节)。

你能跟我们说说你的数据吗?

其他回答

诀窍是将算法状态表示为“增量计数器”=“+”和“输出计数器”=“!”字符的压缩流,这是一个整数多集。例如,集合{0,3,3,4}将被表示为“!+++!!+!”,后面跟着任意数量的“+”字符。要修改多集,您可以输出字符,每次只保持恒定的解压缩量,并在以压缩形式流回之前进行适当的更改。

细节

我们知道最终集合中恰好有10^6个数字,所以最多有10^6个“!”字符。我们还知道我们的范围大小为10^8,这意味着最多有10^8个“+”字符。10^6 "的排列方式!s在10^8 "+"s中的值是(10^8 + 10^6)选10^6,因此指定某种特定的排列需要大约0.965 MiB '的数据。那太紧了。

我们可以独立对待每个角色而不超出我们的配额。“+”字符正好是“!”字符的100倍,如果我们忘记了它们是相互依赖的,那么每个字符是“+”的概率就简化为100:1。100:101的几率对应于每个字符0.08位,对于几乎相同的~0.965 MiB(忽略依赖关系在这种情况下只有~12位的代价!)

The simplest technique for storing independent characters with known prior probability is Huffman coding. Note that we need an impractically large tree (A huffman tree for blocks of 10 characters has an average cost per block of about 2.4 bits, for a total of ~2.9 Mib. A huffman tree for blocks of 20 characters has an average cost per block of about 3 bits, which is a total of ~1.8 MiB. We're probably going to need a block of size on the order of a hundred, implying more nodes in our tree than all the computer equipment that has ever existed can store.). However, ROM is technically "free" according to the problem and practical solutions that take advantage of the regularity in the tree will look essentially the same.

伪代码

Have a sufficiently large huffman tree (or similar block-by-block compression data) stored in ROM Start with a compressed string of 10^8 "+" characters. To insert the number N, stream out the compressed string until N "+" characters have gone past then insert a "!". Stream the recompressed string back over the previous one as you go, keeping a constant amount of buffered blocks to avoid over/under-runs. Repeat one million times: [input, stream decompress>insert>compress], then decompress to output

到目前为止,这里还没有提到一个相当狡猾的技巧。我们假设您没有额外的方法来存储数据,但严格来说这并不正确。

解决问题的一种方法是做以下可怕的事情,任何人在任何情况下都不应该尝试:使用网络流量存储数据。不,我指的不是NAS。

你可以用以下方法对只有几个字节内存的数字进行排序:

首先取两个变量:COUNTER和VALUE。 首先将所有寄存器设置为0; 每次你收到一个整数I,增加COUNTER并将VALUE设置为max(VALUE, I); 然后向路由器发送数据集为I的ICMP echo请求报文。擦掉I,重复。 每次收到返回的ICMP包时,只需提取整数并在另一个回显请求中再次发送出去。这将产生大量的ICMP请求,其中包含整数。

Once COUNTER reaches 1000000, you have all of the values stored in the incessant stream of ICMP requests, and VALUE now contains the maximum integer. Pick some threshold T >> 1000000. Set COUNTER to zero. Every time you receive an ICMP packet, increment COUNTER and send the contained integer I back out in another echo request, unless I=VALUE, in which case transmit it to the destination for the sorted integers. Once COUNTER=T, decrement VALUE by 1, reset COUNTER to zero and repeat. Once VALUE reaches zero you should have transmitted all integers in order from largest to smallest to the destination, and have only used about 47 bits of RAM for the two persistent variables (and whatever small amount you need for the temporary values).

我知道这很可怕,我知道可能会有各种各样的实际问题,但我想这可能会让你们中的一些人发笑,或者至少会吓到你们。

现在的目标是一个实际的解决方案,覆盖所有可能的情况下,输入在8位数范围内,只有1MB的RAM。注:工作正在进行中,明天继续。使用对已排序整型的增量进行算术编码,对于1M个已排序整型,最坏的情况是每个条目花费大约7位(因为99999999/1000000是99,而log2(99)几乎是7位)。

但是你需要将1m个整数排序到7位或8位!级数越短,delta就越大,因此每个元素的比特数就越多。

我正在努力尽可能多地压缩(几乎)在原地。第一批接近250K的整数最多每个需要大约9位。因此结果大约需要275KB。重复使用剩余的空闲内存几次。然后解压缩-就地合并-压缩这些压缩块。这很难,但也是可能的。我认为。

合并后的列表将越来越接近每整数7位的目标。但是我不知道合并循环需要多少次迭代。也许3。

但是算术编码实现的不精确性可能使它不可能实现。如果这个问题是可能的,它将是非常紧张的。

有志愿者吗?

谷歌的(坏)方法,从HN线程。存储rle风格的计数。

你的初始数据结构是“99999999:0”(都是零,没有看到任何数字),然后假设你看到了数字3,866,344,那么你的数据结构就变成了“3866343:0,1:1,96133654:0”,你可以看到数字总是在零位数和1位数之间交替,所以你可以假设奇数代表0位,偶数代表1位。这就变成了(3866343,1,96133654)

他们的问题似乎不包括副本,但让我们假设他们使用“0:1”来表示副本。

大问题#1:1M个整数的插入将花费很长时间。

大问题#2:像所有的普通增量编码解决方案一样,一些分布不能用这种方式覆盖。例如,1m整数,距离为0:99(例如,每个整数+99)。现在考虑相同的情况,但随机距离在0:99的范围内。(注:99999999/1000000 = 99.99)

谷歌的方法既不值得(缓慢),也不正确。但要为他们辩护,他们的问题可能略有不同。

我认为从组合学的角度来思考这个问题:有多少种可能的排序数字的组合?如果我们给出的组合是0,0,0 ....,0代码0,和0,0,0,…,1代码1,和999999999,99999999,…99999999是代码N, N是什么?换句话说,结果空间有多大?

Well, one way to think about this is noticing that this is a bijection of the problem of finding the number of monotonic paths in an N x M grid, where N = 1,000,000 and M = 100,000,000. In other words, if you have a grid that is 1,000,000 wide and 100,000,000 tall, how many shortest paths from the bottom left to the top right are there? Shortest paths of course require you only ever either move right or up (if you were to move down or left you would be undoing previously accomplished progress). To see how this is a bijection of our number sorting problem, observe the following:

您可以将路径中的任何水平支腿想象成排序中的一个数字,其中支腿的Y位置表示值。

所以如果路径只是向右移动一直到最后,然后一直跳到顶部,这相当于顺序为0,0,0,…,0。相反,如果它开始时一直跳到顶部,然后向右移动1,000,000次,这相当于999999999,99999999,……, 99999999。它向右移动一次,然后向上移动一次,然后向右移动一次,然后向上移动一次,等等,直到最后(然后必然会一直跳到顶部),相当于0,1,2,3,…,999999。

幸运的是,这个问题已经解决了,这样的网格有(N + M)个选择(M)条路径:

(1,000,000 + 100,000,000)选择(100,000,000)~= 2.27 * 10^2436455

N因此等于2.27 * 10^2436455,因此代码0表示0,0,0,…,0和代码2.27 * 10^2436455,一些变化表示999999999,99999999,…, 99999999。

为了存储从0到2.27 * 10^2436455的所有数字,您需要lg2(2.27 * 10^2436455) = 8.0937 * 10^6位。

1兆字节= 8388608比特> 8093700比特

这样看来,我们至少有足够的空间来存储结果!当然,有趣的部分是在数字流进来时进行排序。不确定最好的方法是我们有294908位剩余。我想一个有趣的技巧是在每个点都假设这是整个排序,找到该排序的代码,然后当你收到一个新数字时,返回并更新之前的代码。手,手,手。