1.有10亿个浮点数,从中找出1万个最大的数。写一个高性能的算法 .^_^..比我的算法快200分就都给你了..

zhiang75 2008-03-02 03:04:10
1.有10亿个浮点数,从中找出1万个最大的数。写一个高性能的算法
凑个热闹...和我PK..谁的算法比我的快200分就给谁..


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace WindowsApplication14
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
for (int a = 0; a < 65536; a++)
{
container[a] = new List<float>();
}
}

/// <summary>
/// 内存容器
/// </summary>
List<float>[] container = new List<float>[65536];

/// <summary>
/// 可以使用的内存容器最小索引
/// </summary>
uint containerIndex = 0;

/// <summary>
/// 构造数据文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
FileStream data=File.Create("d:\\data.float", 1024 * 1024 * 10);
DateTime bt1 = DateTime.Now;
Random random=new Random();
for (int a = 0; a < 200000000; a++)
{
double temp = random.NextDouble();
int bi1 = random.Next(10000000);
temp = temp * bi1;
float rand = (float)temp;
byte[] array = BitConverter.GetBytes(rand);
data.Write(array,0,4);
}
data.Close();
DateTime bt2 = DateTime.Now;
this.Text=(bt2-bt1).ToString();
}

int addCount = 0;
/// <summary>
/// 将数据加入到容器
/// </summary>
/// <param name="value"></param>
/// <param name="dataFrame"></param>
void AddContainer(float value, uint dataFrame)
{
addCount++;

uint index = dataFrame & 0x7FFF8000;//01111111 11111111 10000000 00000000
index >>= 15;
//以上获得8位指数和8位头尾数组成的索引
if (index >= containerIndex)
{
container[(int)index].Add(value);
}
//每当添加的数据大于1000w时进行整理
if (addCount > 10000000)
{
//计数器清零
addCount = 0;

//容器中数据量的合计
int sumContainerCount = 0;

for (int a = 65535; a >= 0; a--)
{
sumContainerCount += container[a].Count;
if (sumContainerCount >= 10000)
{
//移动可以使用的容器索引,小于此索引的容器不可以使用
containerIndex = (uint)a;
break;
}
}
//清理不可以使用的容器,释放内存
for (int a = 0; a < containerIndex; a++)
{
container[a].Clear();
}
}


}
/// <summary>
/// 合并数据整理输出
/// </summary>
/// <returns></returns>
List<float> Retuen()
{
List<float> R = new List<float>();
for (int a = 65535; a >= containerIndex; a--)
{
R.AddRange(container[a]);
}
//调用.Net内置排序器,其实就是快排
R.Sort();
//将顺序反转
R.Reverse();
//返回10000个最大的数
return R.GetRange(0, 10000);
}


/// <summary>
/// 计算获得1W个最大数
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
for (int a = 0; a < 65536; a++)
{
container[a] = new List<float>();
}
containerIndex = 0;
addCount = 0;


FileStream data = File.OpenRead("d:\\data.float");
DateTime bt1 = DateTime.Now;
byte[] array = new byte[4];
while (data.Read(array, 0, 4) > 0)
{
//转换为一个整数结构
uint bi1 = BitConverter.ToUInt32(array,0);
//还原为浮点数
float bf1 = BitConverter.ToSingle(array, 0);
AddContainer(bf1, bi1);
}

data.Close();
Retuen();


DateTime bt2 = DateTime.Now;
this.Text = (bt2 - bt1).ToString();
}
}
}
...全文
1560 64 打赏 收藏 转发到动态 举报
写回复
用AI写文章
64 条回复
切换为时间正序
请发表友善的回复…
发表回复
小白菜VS 2011-10-16
  • 打赏
  • 举报
回复
这个题目要用全排序的话那代价岂不是很大吗
shanyue198 2010-07-31
  • 打赏
  • 举报
回复
学习中····看牛人的精彩···
longjurujiang 2008-05-30
  • 打赏
  • 举报
回复
学习学习再学习
ghao0 2008-03-04
  • 打赏
  • 举报
回复
不是讨论过吗?
部分快排,余下2分插入

lz 数不够,folat = byte? 以此优化??
gushansanren 2008-03-04
  • 打赏
  • 举报
回复
兄弟,你就赶快给分吧
gushansanren 2008-03-04
  • 打赏
  • 举报
回复
#include <stdio.h>
#include <time.h>

typedef struct {
double data;
int flag;
}zryData;

//先分组,再排序,再验证
int PaiXu(double *inbuf,int inlen,double *outbuf,int outlen)
{
int j,i,k,count,flag;
double tmpdou,td2;
double *p,*q,*pout;
zryData *tmpdata,*pd,*qd;

if(inlen%outlen) return -1; //做个检测,如果不能整除的情况,先不考虑

//选取每组最大数
count = inlen/outlen; //每组大小
p = inbuf; //临时指针
for(i=0;i<outlen;i++)
{
tmpdou = *p; //每组最后一个最大
for(j=0;j<count-1;j++)
{
q = p+1;
if(*p > *q)
{
tmpdou = *q;
*q = *p;
*p = tmpdou;
}
p = q;
}
p++; //如果不加这个,可能选出来的是整个数组最大的
}

//虽然快速排序比较快,但是如果对大量数据需要多次递归,对系统资源浪费太大,
//改用冒泡,如果有递归实现可以使用
tmpdata = (zryData *)malloc(outlen *sizeof(zryData));
pd = tmpdata;
for(i=1;i<=outlen;i++)
{
pd->data = inbuf[i*count-1];
pd->flag = i-1;
pd++;
}

for (i=0;i<outlen;i++)
{
pd = tmpdata;
flag = 0;
for(j=0;j<outlen-i;j++)
{
qd = pd+1;
if((*pd).data < (*qd).data)
{
tmpdou = (*qd).data;
(*qd).data = (*pd).data;
(*pd).data = tmpdou;

flag = (*qd).flag;
(*qd).flag = (*pd).flag;
(*pd).flag = flag;

flag =1;
}
pd++;
}
if(flag == 0) //已经排序完成
break;
}

//现在开始验证所选的数,变相冒泡排序
pout = outbuf;
pd = tmpdata;

for(i=0;i<outlen;i++)
{
tmpdou = (*pd).data;

//检验
for(j=0;j<i;j++)
{
qd = tmpdata;
p = &inbuf[((*qd).flag)*count];
for(k=0;k<count;k++)
{
if(*p >tmpdou && *p < td2)
tmpdou = *p;
p++;
}
qd++;
}
//完成
*pout = td2 =tmpdou;
pd++;
pout++;
}
return 0;
}
//直接找
int PaiXu2(double *inbuf,int inlen,double *outbuf,int outlen)
{
int j,i;
double tmpdou;
double *p,*q,*pout;

pout = outbuf;
for (i=0;i<inlen;i++)
{
p = inbuf;
for(j=0;j<inlen-i-1;j++)
{
q = p+1;
if(*p >*q)
{
tmpdou = *q;
*q = *p;
*p = tmpdou;
}
p++;
}
*pout = *q;
pout++;

if(i ==outlen-1)
break;
}
return 0;
}

void main()
{
int i,inlen;
double inbuf[100000],outbuf[100];
unsigned long start,end;

inlen = 100000;
srand(10000000);
for(i=0;i<inlen;i++)
{
inbuf[i] =rand()/100.00;
}
time(&start);
PaiXu(inbuf,inlen,outbuf,100);
time(&end);
printf("time is %d \n",end-start);

time(&start);
PaiXu2(inbuf,inlen,outbuf,100);
time(&end);
printf("time is %d \n",end-start);

getchar();
}
ghao0 2008-03-04
  • 打赏
  • 举报
回复
运算耗时:83156
返回结果[0]=0.999999998603016
返回结果[1000]=0.999999084044247
返回结果[2000]=0.999998142477124
返回结果[3000]=0.999997217208146
返回结果[4000]=0.999996210914103
返回结果[5000]=0.999995217192916
返回结果[6000]=0.999994203913954
返回结果[7000]=0.999993198085573
返回结果[8000]=0.99999222392216
返回结果[9000]=0.999991221353408
p4 2.93 内存512M
运行前可用物理内存222M
-------------------------------
private void button4_Click(object sender, EventArgs e)
{
//常量固定
const int numberCount = 1000000000;
const int resultCount = 10000;

Random random = new Random(1000); // 随机数种子固定,这样保证大家测试的数据一致
long tickCount = Environment.TickCount;

#region 初始化
//初始化对象的时间也要计算在内
List<double> a;
a = new List<double>();
#endregion 初始化
#region 处理数据
//部分快排
const int Num = 16384;//2^14
for (int i = 0; i < Num; i++)
{
double value = random.NextDouble();
a.Add(value);
}
a.Sort();
a.Reverse();
//余下2分插入
for (int i = 0; i < numberCount - Num; i++)
{
double value = random.NextDouble();
if (value < a[10000 - 1]) continue;
int locate;
int step;
if (a[Num / 2 - 1] < value)
{
step = Num / 4;
locate = Num / 2 - step;
}
else
{
step = Num / 16;
locate = Num / 2 + step;
}
while (step > 1)
{
step = step / 2;
if (a[locate - 1] > value)
locate = locate + step;
else
locate = locate - step;
}
a.Insert(locate - 1, value);
}
#endregion 处理数据
Console.WriteLine("运算耗时:{0}", Environment.TickCount - tickCount);

#region 采用输出结果
for (int i = 0; i < resultCount; i += 1000)
{
Console.WriteLine("返回结果[{0}]={1}", i, a[i]);
}
#endregion 采用输出结果

}
ghao0 2008-03-04
  • 打赏
  • 举报
回复
上条看错了。
-----------------
我的算法
45.78
lz的52.07
---------------------------
private void button3_Click(object sender, EventArgs e)
{
List<float> a;
a =new List<float>();

FileStream data = File.OpenRead("I:\\data.float");
DateTime bt1 = DateTime.Now;
byte[] array = new byte[4];
const int Num = 16384;//2^14
for (int i = 0; i < Num; i++)
{
data.Read(array, 0, 4);
//转换为一个整数结构
uint bi1 = BitConverter.ToUInt32(array, 0);
//还原为浮点数
float bf1 = BitConverter.ToSingle(array, 0);
a.Add(bf1);
}
a.Sort();
a.Reverse();
while (data.Read(array, 0, 4) > 0)
{
//转换为一个整数结构
uint bi1 = BitConverter.ToUInt32(array, 0);
//还原为浮点数
float bf1 = BitConverter.ToSingle(array, 0);
if (bf1 < a[10000-1]) continue;
int locate;
int step;
if (a[Num/2 - 1] < bf1)
{
step = Num / 4;
locate = Num/2 -step;}
else
{
step = Num / 16;
locate = Num / 2 + step;
}
while (step > 1)
{
step = step / 2;
if (a[locate - 1] > bf1)
locate = locate + step;
else
locate = locate - step;
}
a.Insert(locate - 1, bf1);
}
data.Close();
DateTime bt2 = DateTime.Now;
this.Text = (bt2 - bt1).ToString();
}
}
qgmzhfj 2008-03-03
  • 打赏
  • 举报
回复
关注中...
zhiang75 2008-03-03
  • 打赏
  • 举报
回复
to:vwxyzh
你的计算机的内存多大啊?
王集鹄 2008-03-03
  • 打赏
  • 举报
回复
.NET自身在IO上做了优化(也是分块读取,可以参考内核代码)。
否则2亿次访问硬盘可想而知。。。
vwxyzh 2008-03-03
  • 打赏
  • 举报
回复
修正一处错误,顺便稍微改一下:
        static float[] GetTop10000()
const int MaxCount = 10000;
const int FloatLength = 65536;
const int ByteLength = FloatLength * 4;
bool isInit = true;
byte[] buf = new byte[ByteLength];
byte[] byteData = new byte[ByteLength];
float[] maxAfterMerge = new float[MaxCount];
float[] maxBeforeMerge = new float[MaxCount];
float[] current = new float[FloatLength];
int readCount;
float[] tempFloats;
byte[] tempBytes;
using (FileStream data = File.OpenRead("data.float"))
{

readCount = data.Read(byteData, 0, ByteLength);
while (readCount == ByteLength)
{

IAsyncResult ar = data.BeginRead(buf, 0, ByteLength, null, null);

Buffer.BlockCopy(byteData, 0, current, 0, ByteLength);

Array.Sort(current);
Array.Reverse(current);
if (isInit)
{
Buffer.BlockCopy(current, 0, maxBeforeMerge, 0, MaxCount);
isInit = false;
}
else
{

Merge(maxAfterMerge, maxBeforeMerge, current);

tempFloats = maxBeforeMerge;
maxBeforeMerge = maxAfterMerge;
maxAfterMerge = tempFloats;
}

readCount = data.EndRead(ar);

tempBytes = byteData;
byteData = buf;
buf = tempBytes;
}
// 需要讨论一下正好读完的情况,否则Merge方法会有逻辑错误
if (readCount == 0)
return maxBeforeMerge;
Buffer.BlockCopy(byteData, 0, current, 0, readCount);
Array.Sort(current, 0, readCount >> 2);
Array.Reverse(current, 0, readCount >> 2);

Merge(maxAfterMerge, maxBeforeMerge, current, readCount >> 2);

return maxAfterMerge;
}
}

顺便说一下,我的机器是T2400@1.83G,内存1G,比较小,硬盘是fujitsu mhv2060bh,都比较烂

对lz代码有少量修改:
        public Form1()
{
InitializeComponent();
}

// lz的算法需要的东西
List<float>[] container = null;

在Button3_Click里面补上:
            container = new List<float>[65536];
for (int a = 0; a < 65536; a++)
{
container[a] = new List<float>();
}
// ...lz的代码
container = null;


实测:
程序绑定到CPU0后(强制单核),测试的结果是:
我的代码在运行时,使用约14m内存(包含Winform用掉的),CPU占49%(系统会占用1%),偶尔比较低(估计是磁盘碎片导致的)
lz的代码在运行时,使用约104m内存(包含Winform用掉的),CPU占10%-40%,平均在25%左右(估计我的硬盘实在太烂了,喂不饱lz高效的排序算法)

分析:
我的代码因为IO算法好,排序算法烂,所以CPU占用率高,最快时间受排序算法限制
lz的代码因为排序算法好,IO算法烂,所以CPU占用率低,最快时间受IO算法限制

例外:
如果内存足够大,并且Windows已经缓存这个或部分文件,或者因为机器原因IO性能很好(Raid 0或5,或者什么?Ram盘。。。),lz的代码瓶颈就消失了,所以,某些时候可以更快的完成。
王集鹄 2008-03-03
  • 打赏
  • 举报
回复
就是单独空循环10亿次也是很耗时间的(我机器上是30秒左右)
qinglisheng 2008-03-03
  • 打赏
  • 举报
回复
mark
王集鹄 2008-03-03
  • 打赏
  • 举报
回复
我的算法如下:
private void button1_Click(object sender, EventArgs e)
{
const int numberCount = 1000000000;
const int resultCount = 10000;
double[] resultData = new double[resultCount];

Random random = new Random(1000); // 随机数种子固定,这样保证大家测试的数据一致
long tickCount = Environment.TickCount;

#region 初始化
int count = 0; // 所填的数据个数
#endregion 初始化

for (int i = 0; i < numberCount; i++)
{
double value = random.NextDouble();
#region 处理数据
if (count >= resultCount && value <= resultData[resultCount - 1]) continue;
///////Begin 对分查找
int low = 0;
int high = count - 1;
while (low <= high)
{
int j = (low + high) >> 1; // 除2
if (value < resultData[j])
low = j + 1;
else
{
high = j - 1;
if (value == resultData[j]) low = j;
}
}
if (low < count && low < resultCount - 1)
{
int l = count - low;
if (low + l > resultCount - 1) l--;
Array.Copy(resultData, low, resultData, low + 1, l);
}
resultData[low] = value;
if (count < resultCount) count++;
///////End 对分查找
#endregion 处理数据
}
Console.WriteLine("运算耗时:{0}", Environment.TickCount - tickCount);

#region 采用输出结果
for (int i = 0; i < resultCount; i += resultCount / 10)
{
Console.WriteLine("返回结果[{0}]={1}", i, resultData[i]);
}
#endregion 采用输出结果
}


我的机器上(CPU:1.6G*2,内存:2G)运行的结果是:
[code=BatchFile]运算耗时:55766
返回结果[0]=0.999999998603016
返回结果[1000]=0.999999055173248
返回结果[2000]=0.999998114071786
返回结果[3000]=0.999997190199791
返回结果[4000]=0.999996206257491
返回结果[5000]=0.99999517155811
返回结果[6000]=0.999994183890519
返回结果[7000]=0.999993186444041
返回结果[8000]=0.999992232769724
返回结果[9000]=0.999991213902827[/code]
zhiang75 2008-03-03
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 zswang 的回复:]
200000000?明显不是10亿,是2亿。
我建议不要折腾硬盘了(文件上G了)
直接按顺序生成10亿个随机数来处理。
只要随机种子一样,大家的数据也就一样。
运行后输出10个结果(每1000个抽出一个)。
如果结果都错了,再快的效率有何用?
本楼只是提供一个测试样本,看看楼主是否认可。

C# codeprivate void button1_Click(object sender, EventArgs e)
{
//常量固定
const int numberCount = 100000000;…
[/Quote]

我非常认可,不过4G的东东不用文件不行啊...
zhiang75 2008-03-03
  • 打赏
  • 举报
回复
我非常认可,不过4G的东东不用文件不行啊...
zhiang75 2008-03-03
  • 打赏
  • 举报
回复
...顶一下...
rangeon 2008-03-03
  • 打赏
  • 举报
回复
学习
王集鹄 2008-03-03
  • 打赏
  • 举报
回复
const int numberCount = 100000000;
//上面的代码是1亿,改成:
const int numberCount = 1000000000;
加载更多回复(43)
abstract (关键字) 抽象 ['æbstrækt] access vt.访问,存取 ['ækses]'(n.入口,使用权) algorithm n.算法 ['ælgәriðm] Annotation [java] 代码注释 [ænәu'teiʃәn] anonymous adj.匿名的[ә'nɒnimәs]'(反义:directly adv.直接地,立即[di'rektli, dai'rektli]) apply v.应用,适用 [ә'plai] application n.应用,应用程序 [,æpli'keiʃәn]' (application crash 程序崩溃) arbitrary a.任意的 ['ɑ:bitrәri] argument n.参;争论,论据 ['ɑ:gjumәnt]'(缩 args) assert (关键字) 断言 [ә'sә:t] ' (java 1.4 之后成为关键字) associate n.关联(同伴,伙伴) [ә'sәuʃieit] attribute n.属性(品质,特征) [ә'tribju:t] boolean (关键字) 逻辑的, 布尔型 call n.v.调用; 呼叫; [kɒ:l] circumstance n.事件(环境,状况) ['sә:kәmstәns] crash n.崩溃,破碎 [kræʃ] cohesion 内聚,黏聚,结合 [kәu'hi:ʒәn] (a class is designed with a single, well-focoused purpose. 应该不止这点) command n. 命令,指令 [kә'mɑ:nd](指挥, 控制) (command-line 命令行) Comments [java] 文本注释 ['kɒments] compile [java] v.编译 [kәm'pail]' Compilation n.编辑[,kɒmpi'leiʃәn] const (保留字) constant n. 常量, 常, 恒量 ['kɒnstәnt] continue (关键字) coupling 耦合,联结 ['kʌpliŋ] making sure that classes know about other classes only through their APIs. declare [java] 声明 [di'klєә] default (关键字) 默认值; 缺省值 [di'fɒ:lt] delimiter 定义符; 定界符 Encapsulation[java] 封装 (hiding implementation details) Exception [java] 例外; 异常 [ik'sepʃәn] entry n.登录项, 输入项, 条目['entri] enum (关键字) execute vt.执行 ['eksikju:t] exhibit v.显示, 陈列 [ig'zibit] exist 存在, 发生 [ig'zist] '(SQL关键字 exists) extends (关键字) 继承、扩展 [ik'stend] false (关键字) final (关键字) finally (关键字) fragments 段落; 代码块 ['frægmәnt] FrameWork [java] 结构,框架 ['freimwә:k] Generic [java] 泛型 [dʒi'nerik] goto (保留字) 跳转 heap n.堆 [hi:p] implements (关键字) 实现 ['implim
Python编程基础教程本教程旨在帮助初学者了解Python编程的基础知识和高级应用。我们将介绍Python的各个方面,包括基础语法、据类型、控制结构、函、文件操作、面向对象编程、模块和包、异常处理、装饰器、生成器、迭代器以及协程。1. Python基础Python是一种解释型、交互式的编程语言。它具有简单易学的语法和丰富的库,使得开发过程既速又高效。在开始学习Python之前,建议您先熟悉基本的计算机科学概念,例如变量、据类型、运算符等。2. 据类型Python具有多种据类型,包括字(整浮点)、字符串、布尔值、列表、元组、集合和字典等。这些据类型在Python中有着广泛的应用,从简单的值计算到复杂的据处理。3. 控制结构控制结构是编程中的基本组成部,它们决定了程序如何执行。Python支持条件语句(if-elif-else)和循环语句(for和while),可以根据程序中的特定条件和情况来选择适当的操作。4. 函是封装一段代码的便捷方式,它们可以在需要时被调用。在Python中,您可以定义函,并使用参来传递据。函可以返回值,以便在调用它们时使用。5. 文件操作文件操作是编程中常见的任务之一。Python提供了许多内置的函和方法来进行文件操作,如读取、入和删除文件等。此外,Python还支持对文件进行高级操作,如读二进制文件、文件锁定等。6. 面向对象编程面向对象编程是一种流行的编程范式,它使用类和对象的概念来构建复杂的系统。Python支持面向对象编程,您可以使用类来定义对象,并使用继承和多态等特性来扩展和定制对象的行为。7. 模块和包模块和包是Python中组织代码的重要工具。模块是一个包含Python代码的文件,而包则是一个包含多个模块的目录。通过使用模块和包,您可以轻松地组织和管理大型项目中的代码。8. 异常处理异常处理是Python中处理错误的方式之一。当程序中出现错误时,Python会抛出一个异常。通过使用try-except语句块,您可以捕获并处理这些异常,以确保程序的稳定性。9. 装饰器装饰器是Python中的高级功能之一,它允许您在函或方法之间添加额外的功能,而不改变其原始实现。装饰器是一种强大的工具,可用于实现各种功能,如日志记录、性能析等。10. 生成器生成器是Python中的一种特殊类型的迭代器。通过使用生成器函和方法,您可以在需要时生成据,而不是一次性生成所有据。这使得生成器在处理大量据时非常有用,因为它们可以节省内存空间。11. 迭代器和协程迭代器和协程是Python中的两个重要概念。迭代器允许您遍历容器类型的据结构(如列表和元组),而协程则是一种异步编程的方式,它允许您在程序中执行多个任务并发执行。这两个概念在处理大量据和高性能应用程序方面非常有用。

110,533

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧