现在的位置: 首页 > 综合 > 正文

一亿个数排序的算法

2018年05月20日 ⁄ 综合 ⁄ 共 3371字 ⁄ 字号 评论关闭

有1亿个浮点数,请找出其中最小的10000个。提示:假设每个浮点数占4个字节,1亿个浮点数就要站到相当大的空间,因此不能一次将全部读入内存进行排序。
问题分析:
1) 1亿个浮点数,其数据大小为 400 M。如此规模的排序,首先想到分批处理。每次读取 1 000 000 个数据并进行快速排序。需要的内存空间为 1 000 000 * 4  = 4M。需要100 次这样的排序。

2)完全没的规律的数据,考虑使用快速排序。快速排序的平均复杂度是 O( Nlog(N) )。我们可以直接使用 stl 提供的全局函数 sort() , 它使用了快速排序算法(实际是三平均分区法 median-of-three )。
3) 最后只要最大的 10000 个。则每个批次只需要保留排序结果的前 10000 个数据。这段数据已经是分段有序的。数据量为 10000 * 100。
解法:
1) 数据结构定义:
定义数据规模:

enum
{
    batchCapacity = 1000000,
    batchCount    = 100,
    resultCount    = 10000
}

2)生成 数据样本:

void dataPrepare( const char* filName  )
{
    float* pbuf;
    if( ( pbuf= (  float *)malloc( batchCapacity  * sizeof( float ) )) == NULL )
    {
        throw( "failed to malloc" );
    }
    //  生成 batchCapacity * batchCount 个随机实数,并保存到文件 
    ofstream fout;
    fout.exceptions(std::ios::badbit | std::ios::failbit | std::ios::eofbit );
    fout.open( filName, ios::binary ) ;
    if ( !fout   )
    {
        throw( "file not exits" );
    }
    for( size_t index = 0; index < batchCount; index++ )
    {
        for( size_t index = 0; index < batchCapacity; index++  )
        {
            pbuf[index] = RandomFloat( 0, 65537 );
        }
        fout.write( (char*)pbuf, batchCapacity * sizeof( float )  );
    }
    fout.close();
    delete pbuf;
    pbuf = NULL;
    return ;
}

以上,用 RandomFloat() 生成随机数。其定义如下:

/********************************************
 * Rand::rand 线性同余算法获得随机数
 * 会循环出现相同的数。有待改进
 *
 *********************************************/

#include <cstdlib>
#include <ctime>
class Rand
{
public:
    static long long r;
    static int rand()//产生随机数
    {
       // 三个参数的取值 关键字:辗转相除 二次同余
        r = ( r * 1010557   + 79390691  ) %  100663363 ;  
        return r;
    }
};
long long Rand::r = 43215;

float RandomFloat( float low, float high) {
    float d = float( Rand::rand()) / ( float(RAND_MAX) + 1);
        return low + d * (high - low);
}

3 ) 排序

void dataOrder( const char* filName  )
{
    float* pbuf  = (  float *)malloc( batchCapacity  * sizeof( float ) );
    if ( pbuf == NULL )
    {
        throw( "failed to malloc " );
    }
    ifstream fin;
    ofstream fout;
    fin.exceptions(std::ios::badbit | std::ios::failbit | std::ios::eofbit );
    fout.exceptions(std::ios::badbit | std::ios::failbit | std::ios::eofbit );
    fin.open( filName, ios::binary );
    fout.open( string( string(filName).append(".order") ).c_str(), ios::binary ); 
    for( size_t index = 0;index < batchCount;index++ )
    {
        // 分批读入,排序
        fin.read( (char*)pbuf, batchCapacity * sizeof( float )  );
        std::sort( pbuf, pbuf + batchCapacity );
        fout.write( (char*)pbuf,  resultCount * sizeof( float ) );
        cout <<  "writed bytes:"  << resultCount * index + 1 <<  endl;
    }
    fin.close();
    fout.close();
    delete pbuf;
    // 将分组的数据综合排序
    pbuf =  (  float *)malloc( resultCount * batchCount * sizeof( float ) );
    if ( pbuf == NULL )
    {
        throw( "failed to malloc " );
    }
    fin.open( string( string(filName).append(".order") ).c_str(), ios::binary ); 
    fin.read( (char*)pbuf, resultCount * batchCount * sizeof( float )  );
    std::sort( pbuf, pbuf +  resultCount * batchCount  );

    //merge_sort<float>(  pbuf,0,( resultCount *  batchCount ) - 1 );
    // 输出
    for(  size_t index = 0; index < resultCount; index++ )
    {
        printf( "%d\t%f\n",  index, pbuf[index ] );
    }
    fin.close();
    delete pbuf;
    pbuf = NULL;
}

性能测试结果: p4 的 cpu,每秒大约处理 30万个记录。
整个程序:

const char* filName = "c:\\float.df";
void dataPrepare( const char* filName  );
void helpInfo();
void dataOrder( const char* filName  );
int main( int argc, char* argv[] )
{
    try
    {
        if  ( argc == 1 )
        {
            helpInfo();
            return 0;
        }
        const char* filename = argv[1]+ 2;
        if ( filename != NULL && strlen( filename ) > 0 )
        {
            filName = filename;
        }
        switch ( argv[1][1] )
        {
        case 'g':
            dataPrepare( filName  );
            break;

        case 'o':
            dataOrder( filName  );
            break;
        default:
            helpInfo();
            return 0;
            break;
        }
    }
    catch(  const char* e)
    {
        cout <<  e << endl;
    }
    catch(    )
    {
        cout <<  "unknown error" << endl;
    }
    system( "pause" );
    return 0;
}

Referance:
快速排序
the c++ programming lanauage, by bjarne stroustrup chapter 18: Algorithms and Function Objects
线性同余法生成随机数
Introduction to Algorithms, Second Edition,by Thomas H. Cormen, Charles E. 11.3 Hash functions 介绍了线性同余法的原理和用法。

三平均分区法:
Introduction to Algorithms, Second Edition,by Thomas H. Cormen, Charles E. Problems 7-5: Median-of-3 partition 

抱歉!评论已关闭.