置换外部存储器的实用算法
Posted
技术标签:
【中文标题】置换外部存储器的实用算法【英文标题】:Practical algorithms for permuting external memory 【发布时间】:2019-09-20 03:28:05 【问题描述】:在旋转磁盘上,我有 N 条记录要置换。在 RAM 中,我有一个包含所需排列的 N 个索引的数组。我也有足够的 RAM 一次保存 n 条记录。考虑到顺序磁盘访问要快得多这一事实,我可以使用什么算法尽快在磁盘上执行置换?
如果需要,我有大量多余的磁盘可用于中间文件。
【问题讨论】:
没有额外的磁盘空间用于缓冲(只有 RAM)? @Gene 您可以使用 O(1) 额外空间加上 N 位(访问所有周期)应用任何排列。无需缓冲到磁盘。 @Gene 我没想提它,但是如果需要,我可以缓冲到磁盘。在这种情况下,我不关心磁盘使用情况,只关心速度和 RAM。我已编辑问题以提及这一点。 如果这是一个真实世界的应用程序,为什么不能列出所有排列并根据成本函数对它们进行排序,其中优先使用顺序访问?然后您可以按照排序列表进行操作。 @Prune :) 好吧,在这种情况下,你不能让它比 O(N) 时间更好。但我一直在寻找一种使用 O(n) 空间的方法,因为它是现实生活中的应用程序,Big-O 中的常量有时很重要。所以也许我们可以找到一种方法来使用更多的空间和更少的时间? 【参考方案1】:这是一个已知问题。找出排列顺序中的循环。例如,给定五个要置换的记录 [1, 0, 3, 4, 2],您有循环 (0, 1) 和 (2, 3, 4)。你可以通过选择一个未使用的起始位置来做到这一点;跟随索引指针,直到您返回起点。指针序列描述了一个循环。
然后你用一个内部临时变量排列记录,一个记录长。
temp = disk[0]
disk[0] = disk[1]
disk[1] = temp
temp = disk[2]
disk[2] = disk[3]
disk[3] = disk[4]
disk[4] = temp
请注意,您还可以在遍历指针时执行置换。您还需要一些方法来回忆哪些位置已被置换,例如清除置换索引(将其设置为 -1)。
你能明白如何概括吗?
【讨论】:
这里的问题是顺序访问,而不仅仅是限制读/写的愿望。访问所有周期,一次一个,不会利用这一点。 问题是让 one 跳过排列,从而重新排列记录以获得最佳顺序访问。 我认为,如果以利用顺序读取和写入的方式完成排列,则可以更快地完成排列。特别是因为我有一些缓冲空间。 考虑排列在 n 个元素的块上是连贯的(例如,当 n=4 时,排列 4,5,6,7,0,1,2,3。您的建议没有t 适当地利用额外的工作空间来应用排列,而不需要不必要的额外磁盘查找。 @Sneftel:对。该问题的更新说明了这一点。这个(“我的”)答案假设排列是为了 future 顺序效率,而不是进行排列本身的效率。需要全面检修。【参考方案2】:这是区间协调的问题。我将通过更改M
记录的可用内存来稍微简化符号——N
的大小写有点令人困惑。
首先,我们将排列重新转换为一系列间隔,即记录需要驻留在 RAM 中的旋转跨度。如果需要将记录写入编号较小的位置,我们将端点增加列表大小,以指示环绕——必须等待下一次磁盘旋转。例如,使用我之前的示例,我们展开列表:
[1, 0, 3, 4, 2]
0 -> 1
1 -> 0+5
2 -> 3
3 -> 4
4 -> 2+5
现在,我们应用标准的贪婪调度解决方案。首先,按端点排序:
[0, 1]
[2, 3]
[3, 4]
[1, 5]
[4, 7]
现在,为M-1
"lanes" 应用算法;交换空间需要额外的一个。我们填充每个车道,将间隔附加到最早的端点,其起点不重叠:
[0, 1] [2, 3] [3, 4] [4, 7]
[1, 5]
如果 M >= 3,我们总共可以在 7 个“滴答”中完成此操作。如果 M=2,我们将第二条车道推迟 2 个旋转到 [11, 15]。
Sneftal
的好例子给我们带来了更多麻烦,而且重叠更深:
[0, 4]
[1, 5]
[2, 6]
[3, 7]
[4, 0+8]
[5, 1+8]
[6, 2+8]
[7, 3+8]
这需要 4 个“通道”(如果可用),如果 M
病态的情况是排列中的每条记录都需要复制回一个位置,例如 [3, 0, 1, 2],M
=2。
[0, 3]
[1, 4]
[2, 5]
[3, 6]
在这种情况下,我们会多次遍历延迟周期。在每次旋转结束时,我们必须将所有剩余的时间间隔推迟一次旋转,从而导致
[0, 3] [3, 6] [2+4, 5+4] [1+4+4, 4+4+4]
这会让你感动吗,还是你需要更多细节?
【讨论】:
【参考方案3】:我有一个想法,可能需要进一步改进。但它是这样的:
假设硬盘具有以下结构:
5 4 1 2 3
我们想写出这个排列:
2 3 5 1 4
由于 hdd 是一个循环缓冲区,并且假设它只能沿一个方向旋转,我们可以使用移位来编写上述排列:
5 >> 2
4 >> 3
1 >> 1
2 >> 2
3 >> 2
所以让我们把它放在一个数组中,因为我们知道它是一个圆形数组,让我们把它的镜子并排放置:
| 2 3 1 2 2 | 2 3 1 2 2| 2 3 1 2 2 | 2 3 1 2 2 |... Inf
由于我们希望支持顺序读取(或写入),我们可以为上述系列添加一个成本函数。让成本函数是线性的,i。 e:
0 1 2 3 4 5 6 7 8 9 10 ... Inf
现在,让我们在上面的系列中添加成本函数,但是如何选择起点呢?
我们的想法是选择起点,以便获得最大全等单调递增序列。
例如,如果你选择 0 点在“3”上,你会得到 p>
(1) | - 3 2 4 5 | 6 8 7 9 10 | ...
如果您选择 0 点在“2”上,即“1”的右侧,您将得到:
(2) | - - - 2 3 | 4 6 5 7 8 | ...
由于我们试图支持连续读取,所以让我们定义我们的读写函数来这样工作:
f():
在任何当前指向的硬盘位置,函数会将当前指向的硬盘文件读取到可用 RAM 中。 (即总空间 - 1,因为我们要保存 1 用于交换) 如果 RAM 上没有可供读取的可用空间,则该函数将置位并且程序将停止。 在任何当前硬盘位置,如果 ram 保存我们想要写入该硬盘位置的值,函数将当前文件读入交换空间,将所需值从 ram 写入 hdd,并销毁 ram 中的值. 如果将值放入硬盘,函数将检查序列是否完成。如果是,程序将成功返回。现在,我们应该注意,如果以下情况成立:
shift amount <= n - 1 (n : available memory we can hold)
我们可以使用上面的函数一次性遍历硬盘。例如:
current: 4 5 6 7 0 1 2 3
we want: 0 1 2 3 4 5 6 7
n : 5
我们可以从我们想要的任何地方开始,比如从最初的“4”开始。我们顺序读取 4 个项目,(n 现在有 4 个项目),我们从 0 1 2 3 开始放置,(我们可以,因为总共 n = 5,使用 4。1 用于交换)。所以总共操作是4次连续读取,然后r-w操作8次。
使用该类比,很明显,如果我们从等式 (1) 和 (2) 中减去“n-1”,则值为“
所以我们选择 eq. (2) 并减去,假设“n = 3”,我们从等式中减去 2。 (2):
(2) | - - - 0 1 | 2 4 3 5 6 | ...
现在很清楚,使用 f(),从 0 开始,假设 n = 3,我们将有一个这样的开始操作:r, r, r-w, r-w, ...
那么,我们如何完成剩下的工作并找到最低成本?我们将放置一个具有初始最小成本的数组,就在等式(2)的下方。该数组中的位置将表示我们希望执行 f() 的位置。
| - - - 0 1 | 2 4 3 5 6 | ...
| - - - 1 1 | 1 1 1 1 1 | ...
第二个数组,带有 1 和 0 的数组告诉程序在哪里执行 f()。请注意,如果我们假设这些位置错误,f() 将断言。
在我们开始实际将文件放入硬盘之前,我们当然想看看 f() 的位置是否正确。我们检查是否有断言,我们将在删除所有断言的同时尽量降低成本。所以,例如:
(1) 1111000000000000001111
(2) 1111111000000000000000
(1) 显然比 (2) 具有更高的成本。所以问题简化了查找 1-0 数组。
关于寻找最佳数组的一些想法:
最简单的解决方案是写出所有 1 并将断言变为 0。 (本质上是跳过)。此方法保证有效。蛮力:编写一个如 (2) 所示的数组并开始向右移动 1,以尝试所有可用排列的顺序:
1111111100000000 1111111010000000 1111110110000000 ...
完全随机方法:插入 mt1997 并开始排列。每当您看到成本急剧下降时,请停止执行并实施硬盘复制粘贴。你不会找到全局最小值,但你会得到一个很好的权衡。
遗传算法:对于“移位计数远低于 n - 1”的排列,此答案中提供的方法应该(?)提供全局最小和平滑梯度。这允许人们使用遗传算法而无需过多依赖突变。
我在这种方法中发现的一个优点是,由于 OP 提到这是一个现实生活中的问题,因此该方法提供了一种简单(更)的方法来更改成本函数。更容易检测说,有很多连续的小文件要复制,而不是有一个大文件。或者也许 rrwwrrww 比 rrrrwwww 更好?
这有什么意义吗?我们将不得不尝试......
【讨论】:
以上是关于置换外部存储器的实用算法的主要内容,如果未能解决你的问题,请参考以下文章