【问题标题】:Merging files with mergesort algorithm in c++在 C++ 中使用合并排序算法合并文件
【发布时间】:2013-03-02 03:38:12
【问题描述】:

我编写了一个程序来对一个包含 100,000 个双精度数的文件进行外部合并排序。我无法快速找到 c++ 的外部存储库,因为谷歌搜索它只会导致一堆关于 extern 关键字的页面,所以我决定自己编写,我认为这就是问题所在。

该程序确实有效,除了一些细节。输出填充将按排序顺序包含所有双打,但在文件末尾是 30 行

-9.2559631349317831e+061

不在输入文件中。我在输出文件和输入文件中还有 21 个值,这还不包括我刚才提到的单个数字的 30 行。

程序的运行方式是它一次读取 100,000 个双精度 ~ 4000 行并对其进行排序,然后将它们存储到 26 个文本文件中,然后将这 26 个文件合并为 13 个文件,将这 13 个文件合并为 7 个,等等。 ..直到只有一个文件。

如果代码真的很难看,我很抱歉,我自己通过铅笔、纸、反复试验找出了所有外部存储的东西。该程序不会用于任何事情。我还没有清理它。除了调用这些方法之外,驱动程序并没有做太多事情。

//读取一个ifstream文件并将数据存储在一个双端队列中。返回一个布尔值,指示文件是否未达到 EOF

bool readFile(ifstream &file, deque<DEQUE_TYPE> &data){
double d;
for(int i = 0; i < DEQUE_SIZE && file.good(); i++){
    file >> d;
    data.push_back(d);
}

return file.good();
}

//用指定的文件名打开一个文件并将双端队列的内容打印到它。如果 append 为真,则数据将附加到文件中,否则将被覆盖

void printFile(string fileName, deque<DEQUE_TYPE> &data, bool append){

ofstream outputFile;

if(append)
    outputFile.open(fileName, ios::app);
else
    outputFile.open(fileName);

outputFile.precision(23);   

while(data.size() > 0){
    outputFile << data.front() << endl;
    data.pop_front();
}
}

//合并排序文件,直到剩下一个文件

void mergeFiles(){
ifstream inFile1, inFile2;
ofstream outFile;
string fileName1, fileName2;
int i, k, max;
deque<DEQUE_TYPE> data1;
deque<DEQUE_TYPE> data2;
bool fileGood1, fileGood2;

i = 0;
k = 0;
max = 25;
while(max > 1){

    fileName1 = ""; fileName1 += "sortfile_"; fileName1 += to_string(i); fileName1 += ".txt";
    fileName2 = ""; fileName2 += "sortfile_"; fileName2 += to_string(i+1); fileName2 += ".txt";

    try{
        inFile1.open(fileName1);
        inFile2.open(fileName2);
    } catch(int e){
        cout << "Could not open the open the files!\nError " << e;
    }

    fileGood1 = true;
    fileGood2 = true;

    while(fileGood1 || fileGood2){
        fileGood1 = readFile(inFile1, data1);
        fileGood2 = readFile(inFile2, data2);

        data1 = merge(data1, data2);

        printFile("temp", data1, true);

        data1.clear();
    }

    inFile1.close();
    inFile2.close();
    remove(fileName1.c_str());
    remove(fileName2.c_str());


    fileName1 = ""; fileName1 += "sortfile_"; fileName1 += to_string(k); fileName1 += ".txt";
    rename("temp", fileName1.c_str());

    i = i + 2;
    k++;

    if(i >= max){ 
        max = max / 2 + max % 2;
        i = 0;
        k = 0;
    }
}
}

//合并函数

deque<double> merge(deque<double> &left, deque<double> &right){
deque<double> result;

while(left.size() > 0 || right.size() > 0){
    if (left.size() > 0 && right.size() > 0){
        if (left.front() <= right.front()){
            result.push_back(left.front());
            left.pop_front();
        }
        else{
            result.push_back(right.front());
            right.pop_front();
        }
    }

    else if(left.size() > 0){
        result.push_back(left.front());
        left.pop_front();
    }
    else if(right.size() > 0){
        result.push_back(right.front());
        right.pop_front();
    }
}

return result;
}

按照 ThePosey 的建议,我对包含 26 个数字 (0 - 25) 的文件进行了排序,结果如下:

-9.2559631349317831e+061 (47 lines of this)
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
25
25
25
25
25

所以我很确定文件的最后一个数字被复制了,但我仍然不确定随机大数字的 47 次出现是由什么引起的。我检查了一下,100,000 个数字的最后一个数字只在输出文件中出现了两次,而不是 22 次,所以我想我有 11 个单独的最后一个数字被重复。

【问题讨论】:

  • 我没有看到merge 函数,我怀疑问题出在那儿。
  • 我用合并功能更新了它。我不认为会是这样,因为我为整个列表工作,但我只是从 Wikipedia 复制了它,它可能不是为了合并双端队列而设计的,所以它可能在那里。我会试着在纸上写一遍。

标签: c++ sorting merge external


【解决方案1】:

我不知道这是否是整个问题,但您的输入循环中有一个典型错误。 file.good() 不保证下一次读取会成功,它只会告诉您前一次成功。尝试像这样重组它:

for(int i = 0; i < DEQUE_SIZE && (file >> d); i++){
    data.push_back(d);
}

表达式file &gt;&gt; d 返回一个对file 的引用,当您尝试将其计算为布尔值时,它调用good

【讨论】:

  • 这很奇怪。每当我看到它被使用时,它似乎与!EOF 相同。我猜测随机的大数字是由于文件被额外读取时间造成的,但我不认为 file.good() 是罪魁祸首。这几乎解决了所有的错误。现在我得到了一个额外的值,我相信这是输入文件中被读取两次的最大值,因为输入文件的末尾有一个换行符。谢谢。
【解决方案2】:

您是否有理由不能使用几兆内存将整个列表一次读入 RAM 并一次对其进行排序?它会大大简化你的程序。如果您尝试将此作为一项挑战,我会先将问题缩小为 1 个包含 100 个双打的文件,将其拆分为 4、25 个双读,然后应该很容易追踪并查看额外的线路来自。

【讨论】:

  • 这将删除整个 mergeFiles() 方法。我确实在内存中对它进行了排序,以确保我的归并排序算法有效,这很好。是的,这是一个挑战。这是个好建议。我看看会发生什么。
【解决方案3】:

假设您的文件是文本格式,您可以使用std::merge 进行外部合并以及内部合并,方法是使用std::istream_iterators。

std::ifstream in1("temp1.txt");
std::ifstream in2("temp2.txt");
std::ofstream out("output.txt");

std::merge(std::istream_iterator<double>(in1),
           std::istream_iterator<double>(),
           std::istream_iterator<double>(in2),
           std::istream_iteraror<double>(),
           std::ostream_iterator<double>(out, "\n"));

【讨论】:

  • 我不认为问题出在合并算法上。对整个列表进行排序时效果很好。
猜你喜欢
  • 2013-08-04
  • 2013-03-27
  • 2021-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-27
  • 1970-01-01
相关资源
最近更新 更多