[算法]“百万级”浮点数排序

本文介绍了一种处理百万级数据集的高效方法,包括使用C++内置函数读取、排序和写入数据。通过优化读文件过程,利用istringstream和vector.reserve减少内存分配次数,以及采用sprintf加速写文件效率,实现了数据处理的高性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

排序问题三步:读文件、排序、写文件

一.读文件

1.整体三秒版读文件[以行为单位的double数读入vector]

  • [应用了filebuf->sgetn、ifstream.rdbuf()、istringstream、vector.reserve]
  • 通过istringstream对已获取文件信息的string input作进一步操作,以换行为分隔符将每个substr用stof转换为double数后push_back入vector
//读入txt,一百万行,每行一个double数,数据存入vector
//下面重写buffer 加载char*
    ifstream filestr;
    long size;
    char * buffer;
    filebuf *pbuf;

    filestr.open ("M1.txt", ios::binary);//二进制方式打开文件

    pbuf = filestr.rdbuf();//利用filebuf *pbuf对ifstream输入流内容重定向

    size=pbuf->pubseekoff (0,ios::end,ios::in);//获取文件字节大小,1字节==sizeof(char)

    pbuf->pubseekpos (0,ios::in);//将指针位置更改为文件初始位置

    buffer=new char[size];//预先为字符串分配与文件大小相同的缓冲区

    pbuf->sgetn (buffer,size);//调用filebuf的sgetn()函数填充字符串缓冲区char *buffer
    filestr.close();//关闭ifstream

    string input(buffer);
    //cout<<input<<endl;
    delete []buffer;

    istringstream ss_sim(input);//利用istringstream流

    string fVecSim;

    vec_similarity.reserve(1000000);

    while(ss_sim >> fVecSim){
        //cout<<fVecSim<<endl;
        vec_similarity.push_back(atof(fVecSim.c_str()));//以“\n”为分隔符将文件每行的字符串转为double后添加到vector
        fVecSim.clear();//清除string中的内容
    }

    /*和上面的while功能类似
    while(ss_sim.good()){
        ss_sim>>fVecSim;
        vec_similarity.push_back(atof(fVecSim.c_str()));
    }*/

2.有问题的快速读文件(第一版)[读入vector]

/*有问题的buffer和char*部分
    ifstream iVecSim("M3.txt");

    iVecSim.seekg(0,iVecSim.end);

    long long file_size = iVecSim.tellg();//大小

    iVecSim.seekg(0,iVecSim.beg);

    char *buffer = new char[file_size];
    iVecSim.read(buffer,file_size);
*/

3.来自性能优化指南的读文件方法——file_reader(),写入string,耗时0.4s

  • [应用了filebuf->sgetn、ifstream.rdbuf()、istringstream、vector.reserve]
  • 实际上这个方法是1的简化版
  • [有时间时候需要将这两种方法结合起来,尝试进一步优化]
  • [后经过检验,该方法并不如filebuf填充的效果好]
#include <iostream>
#include<fstream>
#include<stdlib.h>
#include<sstream>
#include<time.h>
#include<utility>
using namespace std;
//void stream_read_streambuf_stringstream(std::istream& f, std::string& result);


//1 这个简单的方法就可以实现把所有的文件内容读取到一个string中 0.4 s
std::string file_reader(char const* fname){
    std::fstream f;
    f.open(fname);
    if(!f){
        std::cout<<"CAN NOT OPEN"<<endl;
        return "";
    }
    std::stringstream s;
    std::copy(std::istreambuf_iterator<char>(f.rdbuf()),
              std::istreambuf_iterator<char>(),
              std::ostreambuf_iterator<char>(s));
    return s.str();
}

int main()
{
    //cout << "Hello world!" << endl;

    // 对1的调用
    //clock_t t1 = clock();
    std::string s;
    //std::ifstream f;

    s = file_reader("M1.txt");
    cout<<"OK"<<endl;
    //cout<<(clock()-t1)/CLOCKS_PER_SEC *1000<<"ms"<<endl;

    return 0;
}

二、读文件后检测容器大小和已知数据的匹配关系,设置数据精度

    cout<<vec_similarity.size()<<endl;
    cout<<std::setprecision(10);
    //cout<<vec_similarity[0]<<endl;

/*
    for(vector<double>::iterator it = vec_similarity.begin();it != vec_similarity.end();it++){
        cout<<*it<<endl;
    }
//先注释掉输出所有vector中的数据的过程:用迭代器
*/


/*  //这里是测试读入vector中的内容:用下标 *测试成功
    //cout<<vec_similarity.size()<<endl;
    for(int i=0;i<1000;i++){
        //cout<<vec_similarity[i]<<endl;
        printf("%.16e\n",vec_similarity[i]);
    } */

三、排序(C++内置sort)

//对vector的内容进行排序
    sort(vec_similarity.begin(), vec_similarity.end());// 到这里一共1.5s 读入需要1.099s sort()需要大概0.5s
    cout<<"sort succeed"<<endl;

四、写文件(※)

1.开ofstream,直接将vector写入 | [改良]利用sprintf

//1-将排好序的vector的内容写入文件中
//直接写double,改为下标访问,总时间11s
//2019-2-8 12:48 将直接输出double数改为:通过sprintf将double转换为char[] ,速度提升了4倍,降低为原时间的25%

    std::ofstream res("res_directly.txt");
    res << std::setprecision(10);
    for(unsigned i=0; i<vec_similarity.size() ; i++){
        double tmp = vec_similarity[i];
        char str[30];
        sprintf(str, "%.9e\n", tmp);//sprintf大法好!
        res<<str;
    }
    cout<<"store succeed"<<endl;

2.二进制输出

/*
    //2-该方法输出格式为二进制文件
    int k=0;
    for(vector<double>::iterator it = vec_similarity.begin();it != vec_similarity.end();it++){
        da[k++] = *it;//这里对vector进行遍历不需要花多少时间 保存到了double数组da中[全局]
    }

    int len = sizeof(double) * vec_similarity.size();
    ofstream ofile;
    ofile.open("res4.txt",ios::binary);
    ofile.write((const char*)da, len);
    ofile.close();
*/

3.利用迭代器和std::copy进行格式化输出

/* 3-该方法1 精度不对  2时间太长 12s
    std::ofstream output_file("res.txt");
    std::ostream_iterator<double> output_iterator(output_file,"\n");
    std::copy(vec_similarity.begin(), vec_similarity.end(), output_iterator);
*/

4.将vector元素转换为string | char* buffer指向的内容

-当将所有数据内容集中到一个string中,再一次性用ofstream 的 << 运算符,即可快速写入文件

/*//4-该方法虽然将vector中的内容全部转换为string添加到result中,但时间过长,共16s 还不如上一个方法;
  //或者改为char*数组,利用sprintf 和 strcat进行拼接,更慢
 //改成了下标访问,还是很慢

//想办法将vector中的内容全部保存到   char*

    long size2 = sizeof(double) * vec_similarity.size();
    char *buffer2 = new char[size2];

    for(unsigned i=0;i<vec_similarity.size();i++){
        double tmp = vec_similarity[i];
        char str[30];
        sprintf(str,"%.9e\n", tmp);
        strcat(buffer2, str);
        //cout<<str;
    }

    ofstream file("res5.txt");
    file << buffer2; //这里直接把char* buffer2的buffer2给file 就成功地把内容写进去了 double数组名不行
    cout<<"\n\n\n";     //因此考虑将double数组变为char*数组
    delete []buffer2;

*/

/*  将double转换为string
    result.reserve(1000000);
    for(unsigned i=0; i<vec_similarity.size() ; i++){
        to_string(result, vec_similarity[i]);
    }
    cout<<result.size()<<endl;
*/

/*
    void to_string(string& result, double& T){
    ostringstream oss;
    oss<<T;
    result.append(oss.str().c_str());
    result.append("\n");
}
*/
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值