我有一台有1mb内存的电脑,没有其他本地存储。我必须使用它通过TCP连接接受100万个8位十进制数字,对它们进行排序,然后通过另一个TCP连接发送排序的列表。
数字列表可能包含重复的,我不能丢弃。代码将放在ROM中,所以我不需要从1 MB中减去我的代码的大小。我已经有了驱动以太网端口和处理TCP/IP连接的代码,它需要2 KB的状态数据,包括1 KB的缓冲区,代码将通过它读取和写入数据。这个问题有解决办法吗?
问答来源:
slashdot.org
cleaton.net
我有一台有1mb内存的电脑,没有其他本地存储。我必须使用它通过TCP连接接受100万个8位十进制数字,对它们进行排序,然后通过另一个TCP连接发送排序的列表。
数字列表可能包含重复的,我不能丢弃。代码将放在ROM中,所以我不需要从1 MB中减去我的代码的大小。我已经有了驱动以太网端口和处理TCP/IP连接的代码,它需要2 KB的状态数据,包括1 KB的缓冲区,代码将通过它读取和写入数据。这个问题有解决办法吗?
问答来源:
slashdot.org
cleaton.net
当前回答
我认为从组合学的角度来思考这个问题:有多少种可能的排序数字的组合?如果我们给出的组合是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位剩余。我想一个有趣的技巧是在每个点都假设这是整个排序,找到该排序的代码,然后当你收到一个新数字时,返回并更新之前的代码。手,手,手。
其他回答
下面是一些可以解决这个问题的c++代码。
满足内存约束的证明:
编辑:无论是在这篇文章中还是在他的博客中,都没有作者提供的最大内存要求的证据。由于编码值所需的比特数取决于先前编码的值,因此这样的证明可能不是简单的。作者指出,根据经验,他可能遇到的最大编码大小是1011732,并任意选择了1013000的缓冲区大小。
typedef unsigned int u32;
namespace WorkArea
{
static const u32 circularSize = 253250;
u32 circular[circularSize] = { 0 }; // consumes 1013000 bytes
static const u32 stageSize = 8000;
u32 stage[stageSize]; // consumes 32000 bytes
...
这两个数组总共占用1045000字节的存储空间。剩下1048576 - 1045000 - 2×1024 = 1528字节作为剩余变量和堆栈空间。
它在我的至强W3520上运行大约23秒。您可以使用以下Python脚本验证程序是否工作,假设程序名称为sort1mb.exe。
from subprocess import *
import random
sequence = [random.randint(0, 99999999) for i in xrange(1000000)]
sorter = Popen('sort1mb.exe', stdin=PIPE, stdout=PIPE)
for value in sequence:
sorter.stdin.write('%08d\n' % value)
sorter.stdin.close()
result = [int(line) for line in sorter.stdout]
print('OK!' if result == sorted(sequence) else 'Error!')
该算法的详细解释可以在以下一系列帖子中找到:
1MB排序说明 算术编码与1MB排序问题 使用定点数学的算术编码
我在这里的建议很大程度上归功于Dan的解决方案
首先,我假设解决方案必须处理所有可能的输入列表。我认为流行的答案并没有做出这样的假设(在我看来这是一个巨大的错误)。
众所周知,任何形式的无损压缩都不会减小所有输入的大小。
所有流行的答案都假设它们能够有效地应用压缩来允许它们有额外的空间。事实上,一个足够大的额外空间块,以未压缩的形式保存他们部分完成的列表的一部分,并允许他们执行排序操作。这只是一个糟糕的假设。
对于这样的解决方案,任何了解如何进行压缩的人都能够设计一些不能很好地压缩该方案的输入数据,并且“解决方案”很可能会由于空间不足而崩溃。
相反,我采用数学方法。我们可能的输出是所有长度为LEN的列表,由0..MAX范围内的元素组成。这里LEN是1,000,000,MAX是100,000,000。
对于任意的LEN和MAX,编码此状态所需的比特数为:
Log2(MAX multichoice LEN)
因此,对于我们的数字,一旦我们完成了接收和排序,我们将需要至少Log2(100,000,000 MC 1,000,000)位来存储我们的结果,以一种能够唯一区分所有可能输出的方式。
这是~= 988kb。所以我们有足够的空间来存放结果。从这个角度来看,这是可能的。
[删除了无意义的漫谈,现在有更好的例子…]
最好的答案在这里。
另一个很好的答案是这里,它基本上使用插入排序作为函数,将列表扩展为一个元素(缓冲一些元素并进行预先排序,以允许一次插入多个元素,节省一些时间)。使用一个很好的压缩状态编码,7位增量的桶
你试过转换成十六进制吗?
我可以看到前后文件大小都有了很大的减小;然后,用自由空间分步计算。也许,再次转换为dec, order,十六进制,另一个块,转换为dec, order…
对不起. .我不知道是否可行
# for i in {1..10000};do echo $(od -N1 -An -i /dev/urandom) ; done > 10000numbers
# for i in $(cat 10000numbers ); do printf '%x\n' $i; done > 10000numbers_hex
# ls -lah total 100K
drwxr-xr-x 2 diego diego 4,0K oct 22 22:32 .
drwx------ 39 diego diego 12K oct 22 22:31 ..
-rw-r--r-- 1 diego diego 29K oct 22 22:33 10000numbers_hex
-rw-r--r-- 1 diego diego 35K oct 22 22:31 10000numbers
你用的是哪种电脑?它可能没有任何其他“正常”的本地存储,但它是否有视频RAM,例如?100万像素x每像素32位(比如说)非常接近你所需的数据输入大小。
(我主要是问旧的Acorn RISC PC的内存,如果你选择低分辨率或低颜色深度的屏幕模式,它可以“借用”VRAM来扩展可用的系统RAM !)这在只有几MB普通RAM的机器上非常有用。
在所有可能的输入中,这个问题只有一个解决方案。作弊。
通过TCP读取m个值,其中m接近内存中可排序的最大值,可能是n/4。 对250,000(大约)个数字进行排序并输出。 重复做另外3个四分之三。 让接收方在处理时合并接收到的4个数字列表。(这并不比使用单个列表慢多少。)