您的位置:首页 > 其它

外排序

2015-07-14 12:30 211 查看
什么是外排序?

外排序(External sorting)是指能够处理极大量数据的排序算法。通常来说,外排序处理的数据不能一次装入内存。(摘自百度)

再简单点来说。比如我们要对10亿个数进行排序。如果用int[]来存储这10亿个数的话,我们需要3*1000000000/8/1024/1024/1024≈3.7G。对于只有2G内存的电脑来说根本就跑不起来。所以这时候什么内存排序算法都玩不起来了。

外排序的思想是将这10亿条数据分割成N个小项(比如每个分割项放2000条数据,然后对这2000条数据进行排序[2000个int放入内存当中只需要7.8K],排序后写入一个txt文件中)。这样就能得到N个有序的小项。再把这些小项通过运算聚合起来,得到最终结果。说白了其实就跟归并排序思路相似。

拿实际生活举例,比如我们想知道一个图书馆有多少本书,你会怎样做?自己一个一口气肯定数不完。只能今天数一个区域,得到一个总数。明天数另外一个区域得到一个总数。最后把所有总数聚集起来。又或者将图书馆分成N个区域,然后找N个人来数这N个区域。他们数完后向你汇报,你再把他们汇报的结果聚集起来得到最终结果。这跟google提出的MapReduce思想相似。跟之前一个人数的区别只在于一个是单线程,一个是多线程。

知道了思路之后,我们实现起来也就非常简单了

static void Main(string[] args)
{
int count = 11;//生成数据的数量
int step = 2;//设置分割时每份多少条
var sourcepath = string.Format("{0}{1}.txt", AppDomain.CurrentDomain.BaseDirectory, "source");
Init(sourcepath, count);//初始化数据--把所有数据写入source.txt的文件中
Queue<string> queue = Skip(sourcepath, step);//分割source.txt文件,并将每份都进行排序。所以分割后的每个文件中的内容都是有序的
while (queue.Count > 1)//把有序的队列聚合在一起
{
Sort(queue);
}
System.Diagnostics.Process.Start(queue.First());//显示结果
}

/// <summary>
/// 初始化数据
/// </summary>
/// <param name="sourcepath"></param>
/// <param name="count"></param>
private static void Init(string sourcepath, int count)
{
using (StreamWriter writer = new StreamWriter(sourcepath, false))
{
Random random = new Random();
for (int i = 0; i < count; i++)
{
writer.WriteLine(random.Next(1000));
}
writer.Close();
}
}

/// <summary>
/// 对源数据进行分割
/// </summary>
/// <param name="sourcepath"></param>
/// <param name="step"></param>
/// <returns></returns>
private static Queue<string> Skip(string sourcepath, int step)
{
Queue<string> queue = new Queue<string>();
StreamReader read = new StreamReader(sourcepath);
while (true)
{
List<long> temp = new List<long>();
for (int i = 0; i < step; i++)
{
string value = read.ReadLine();
if (string.IsNullOrEmpty(value))
break;
temp.Add(long.Parse(value));
}
if (temp.Count == 0)
break;
string tempfile = null;
do
{
tempfile = string.Format("{0}{1}.txt", AppDomain.CurrentDomain.BaseDirectory, DateTime.Now.ToString("MMddHHmmssfff"));
}
while (File.Exists(tempfile));
StreamWriter writer = new StreamWriter(tempfile);
temp = temp.OrderBy(x => x).ToList();
temp.ForEach(x => writer.WriteLine(x));
queue.Enqueue(tempfile);
writer.Close();
writer.Dispose();
}
read.Close();
read.Dispose();
return queue;
}

private static void Sort(Queue<string> queue)
{
StreamReader read1 = new StreamReader(queue.Dequeue());
StreamReader read2 = new StreamReader(queue.Dequeue());
Func<StreamReader, long?> GetValue = (x) =>
{
string temp = x.ReadLine();
if (temp == null)
return null;
return long.Parse(temp);
};
long? value1 = GetValue(read1);
long? value2 = GetValue(read2);
string resultpath = null;
do
{
resultpath = string.Format("{0}{1}.txt", AppDomain.CurrentDomain.BaseDirectory, DateTime.Now.ToString("MMddHHmmssfff"));
}
while (File.Exists(resultpath));
StreamWriter writer = new StreamWriter(resultpath);
while (value1.HasValue && value2.HasValue)
{
if (value1 < value2)
{
writer.WriteLine(value1);
value1 = GetValue(read1);
}
else
{
writer.WriteLine(value2);
value2 = GetValue(read2);
}
}
while (value1.HasValue)
{
writer.WriteLine(value1);
value1 = GetValue(read1);
}
while (value2.HasValue)
{
writer.WriteLine(value2);
value2 = GetValue(read2);
}
queue.Enqueue(resultpath);
read1.Close();
read1.Dispose();
read2.Close();
read2.Dispose();
writer.Close();
writer.Dispose();
}


可以看出思路其实很简单。首先把大量数据细分为N个小块(相当于图书馆划分区域)。然后这N个小块各自进行排序(相当于划分完区域后开始点算书籍数量)。最后再把结果聚集起来。

下回将介绍使用压缩的方式在内存中对大量数据进行排序的思路。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: