置换外部存储器的实用算法

时间:2019-05-01 15:06:42

标签: algorithm permutation large-data large-files

在旋转的磁盘上,我有N个要置换的记录。在RAM中,我有N个索引数组,其中包含所需的排列。我也有足够的RAM一次容纳n条记录。考虑到顺序磁盘访问要快得多的事实,我可以使用哪种算法尽快在磁盘上执行置换?

如果需要,我有很多多余的磁盘可用于中间文件。

3 个答案:

答案 0 :(得分:0)

这是一个已知问题。在排列顺序中找到循环。例如,给定五个要置换[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)。

您能看到如何概括吗?

答案 1 :(得分:0)

这是间隔协调的问题。我将通过更改<img>记录的可用内存来略微简化表示法-大写和小写的M有点令人困惑。

首先,我们重新排列排列为一系列间隔,即记录需要驻留在RAM中的旋转范围。如果需要将记录写入编号较低的位置,我们将通过增加列表大小来增加端点,以指示卷回-必须等待下一次磁盘旋转。例如,使用我之前的示例,我们扩展列表:

N

现在,我们应用标准的贪婪调度解决方案。首先,按端点排序:

[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

如果M> = 3,我们总共可以进行7次“滴答”。如果M = 2,我们将第二车道推迟2圈旋转到[11,15]。


[0, 1] [2, 3] [3, 4] [4, 7] [1, 5] 的很好的例子为我们带来了更多麻烦,而且重叠程度更高:

Sneftal

这需要4个“车道”(如果有),如果[0, 4] [1, 5] [2, 6] [3, 7] [4, 0+8] [5, 1+8] [6, 2+8] [7, 3+8] <5,则根据需要推迟车道。


病理情况是需要将排列中的每个记录复制回一个位置,例如{3,0,1,2},M = 2。 / p>

M

在这种情况下,我们将多次执行延迟周期。每次旋转结束时,我们都必须将所有剩余间隔延迟一圈,从而导致

[0, 3]
[1, 4]
[2, 5]
[3, 6]

是让您感动还是需要更多细节?

答案 2 :(得分:0)

我有一个主意,可能需要进一步改进。但是就这样:

假设硬盘具有以下结构:

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

由于我们希望支持顺序读取(或写入),因此可以将成本函数放入上述序列中。令成本函数为线性,即。 e:

0 1 2 3 4 5 6 7 8 9 10 ... Inf

现在,让我们将成本函数添加到上述系列中,但是如何选择起点?

想法是选择起点,以便获得最大的全等单调递增序列。

例如,如果您将0点选择为“ 3”,则会得到

(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”,则值“ <= 0”的位置将更适合初始位置,因为高于零肯定会要求另一次通过。

因此,我们选择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()将声明。

在我们开始将文件实际放入HDD之前,我们当然想看看f()位置是否正确。我们检查是否有断言,我们将尝试在删除所有断言的同时将成本降至最低。因此,例如:

(1) 1111000000000000001111
(2) 1111111000000000000000

(1)显然要比(2)高。因此,该问题简化了查找1-0数组的过程。

有关寻找最佳阵列的一些想法:

  • 最简单的解决方案是写出全1并将断言变为0。 (本质上是跳过)。这种方法肯定可以使用。
  • 蛮力:编写一个如(2)所示的数组,并开始将1右移,以尝试可用的每个排列的顺序:

    1111111100000000 1111111010000000 1111110110000000 ...

  • 完全随机的方法:插入mt1997并开始置换。每当您发现成本急剧下降时,请停止执行并实施hdd复制​​粘贴。您不会找到全局最小值,但是会得到一个不错的权衡。

  • 遗传算法:对于“移位计数远低于n-1”的置换,此答案中提供的方法应(?)提供全局最小和平滑梯度。这样一来,人们就可以使用遗传算法,而不必过于依赖突变。

我在这种方法中发现的一个优点是,由于OP提到这是一个现实问题,因此该方法提供了一种简便的方法来更改成本函数。与要复制一个大文件相比,要复制很多连续的小文件要容易得多。还是rrwwrrww比rrrrwwww更好?

这甚至有意义吗?我们将不得不尝试...