【算法比赛】在一亿个数中寻找出现频率最多的4个。

王集鹄 2010-04-19 03:33:48
加精
性能最高者奖励150分专家分+1000可用分,其余酌情散掉。

需求:寻找数组中出现频率最多的4个数
数字=出现次数
56=101024
488=100892
576=100879
91=100857
247=100820
160=100811
323=100789
213=100762
417=100756
363=100754

特殊情况
出现并列任意抽取即可
463=16
979=13
168=13
88=12
694=12
709=12
691=11

即输出:
463
979
168
88

463
979
168
709
均可

输出样例:
Zswang_0 开始运行
56
488
576
91
共耗时1141毫秒


测试代码:
using System;

namespace ConsoleApplication1
{
class Program
{
const int max = 1000; // 最大取值范围
//const int count = 5000; // 小数据量
const int count = 100000000; // 数据量
const int resultLen = 4; // 返回长度

static void Main(string[] args)
{
var random = new Random(2010); // 固定随机种子,确保大家测试数据一致
var data = new int[count];
#region 生成测试数据,不在性能计算之内
for (var i = 0; i < count; i++) data[i] = random.Next(max);
#endregion 生成测试数据

#region 计算性能
Console.WriteLine("Zswang_0 开始运行");
var tick = Environment.TickCount;
foreach (var i in Zswang_0(data, 4))
{
Console.WriteLine(i);
}
Console.WriteLine("共耗时{0}毫秒", Environment.TickCount - tick);
#endregion

Console.ReadKey();
}

// 作者_版本
static int[] Zswang_0(int[] data, int len)
{
// 计算每个数据出现的次数
var dict = new int[max];
for (var i = 0; i < count; i++) dict[data[i]]++;

// 按出现的次数排序
var indexs = new int[max];
for (var i = 0; i < max; i++) indexs[i] = i; // 获得完整的序号
Array.Sort(indexs, delegate(int a, int b)
{
return dict[b] - dict[a];
});
/*
for (var i = 0; i < 100; i++)
{
Console.WriteLine("{0}={1}", indexs[i], dict[indexs[i]]);
}
//*/
var result = new int[len];
for (var i = 0; i < len; i++) result[i] = indexs[i]; // 输出
return result;
}
}
}
...全文
14938 510 打赏 收藏 转发到动态 举报
写回复
用AI写文章
510 条回复
切换为时间正序
请发表友善的回复…
发表回复
japanbbq666 2011-07-11
  • 打赏
  • 举报
回复
算出来2200+毫秒,代码就不发出来了。。。T_T
xxxx09 2011-07-08
  • 打赏
  • 举报
回复
太好了,正需要呢。谢谢
SpecialName8 2011-03-16
  • 打赏
  • 举报
回复
楼上贴错了.
楼主给帮忙删除了吧
SpecialName8 2011-03-16
  • 打赏
  • 举报
回复
Zswang_0 开始运行
56
488
576
91
共耗时733毫秒
Zswang_2 with 开始运行
61
720
84
144
共耗时31毫秒




// with_版本
static int[] Zswang_2(int[] data,int len)
{
// 计算每个数据出现的次数
var dict = new int[max];
int cc_0 = count / 16;
int cc_1 = count % 16;
int ii = 0;
for(ii = 0;ii < cc_0;ii++)
{
dict[data[ii++]]++;
dict[data[ii++]]++;
dict[data[ii++]]++;
dict[data[ii++]]++;

dict[data[ii++]]++;
dict[data[ii++]]++;
dict[data[ii++]]++;
dict[data[ii++]]++;

dict[data[ii++]]++;
dict[data[ii++]]++;
dict[data[ii++]]++;
dict[data[ii++]]++;

dict[data[ii++]]++;
dict[data[ii++]]++;
dict[data[ii++]]++;
dict[data[ii++]]++;
}
for(;ii < cc_1;ii++)
{
dict[data[ii++]]++;
}
// 按出现的次数排序
var indexs = new int[max];
for(var i = 0;i < max;i++) indexs[i] = i; // 获得完整的序号
Array.Sort(indexs,delegate(int a,int b)
{
return dict[b] - dict[a];
});
/*
for (var i = 0; i < 100; i++)
{
Console.WriteLine("{0}={1}", indexs[i], dict[indexs[i]]);
}
//*/
var result = new int[len];
for(var i = 0;i < len;i++) result[i] = indexs[i]; // 输出
return result;
}
VCxiaopihai2010 2011-03-16
  • 打赏
  • 举报
回复
为什么同一程序,在同一机器上运行,结果会不一样呢?
duandaoke 2011-02-08
  • 打赏
  • 举报
回复
必须对全部数据排序,因为可能出现并列情况。
futurecn 2011-01-31
  • 打赏
  • 举报
回复
1亿个什么样的数,整型,长整型,浮点型,这个说清楚,不好弄。如果全是整型,问题就简单许多。
cejay 2010-06-25
  • 打赏
  • 举报
回复
算法,制约很多人发展的瓶颈
softman11 2010-06-10
  • 打赏
  • 举报
回复
当Max=100,0000的时候,情况就变化了。

单线程绝对的优势。多线程速度一下就慢很多。主要原因是多线程要合并结果,这个就太费时间了。
如果不合并结果,采用Monitor,一样效率很低。没办法。

这个时候最快的算法是单线程的!

我的算法修改成单线程,这个时候跑出的成绩是:
1330ms

而Zswang_0 要1880ms

这个时候Lihanbing_0算法速度就很好了,需要1440MS和我很接近。

多线程统统需要2800MS以上。

这个结果证明,到数据到了100,0000档次的时候, 排序来寻找最大4个数效率就低了,就不如直接找最大4个数的算法了。



softman11 2010-06-10
  • 打赏
  • 举报
回复
当把max=10,0000的时候,
我写的SuperSort优势就比微软Array.Sort稳定的快20MS左右。数据越大,越快。
这个时候成绩单如下。(注意都是在Release版本下运行,这点很重要)

max=100000
Zswang_0 开始运行
91160
16083
30976
37088
共耗时418毫秒

ChrisAK_0 开始运行
91160
16083
30976
37088
共耗时411毫秒

Lihanbing_0 开始运行(他的算法有点小问题,顺序没对)
91160
30976
16083
37088
共耗时376毫秒

sp1234_0 开始运行
91160
16083
30976
37088
共耗时417毫秒

softman11_0 开始运行
91160
30976
16083
37088
共耗时327毫秒

以上测试环境是在windows xp(SP3)+VS2010

softman11 2010-06-10
  • 打赏
  • 举报
回复
我来晚了。我也做了一个测试了一下:

运行结果如下:计时器用的StopWatch,这是高精度计时器,比楼主的计时器要准确的多,我的机器配置Intel T7500 2核心,2G内存。
Zswang_0 开始运行
56
488
576
91
共耗时277毫秒
ChrisAK_0 开始运行
56
488
576
91
共耗时343毫秒
Lihanbing_0 开始运行
56
488
576
91
共耗时227毫秒
sp1234_0 开始运行
56
488
576
91
共耗时332毫秒
softman11_0 开始运行
56
488
576
91
共耗时118毫秒
===========
采用方法:
1,对遍历数组优化,采用一次加16个单元。事实证明确实比一次一个单元要快不少。
2。采用2线程。但是没有用ChrisAK的方法,因为他采用2维数组大大降低了速度。
采用2线程和4线程,基本上没有时间上的差距。
3。尝试采用.net的The Task Parallel Library (TPL),但是事实证明,采用了TPL的话,这个案例,速度将会无比的慢。比最差的串行算法都要慢几倍。放弃。看来微软的TPL,并不适合这个小算法。
4。自己写排序类,采用比QuickSort更快的SuperSort,但是测试结果和Array.Sort的实现没有差别。差距只是快了1MS,几乎不具有统计意义,看来微软的.net算法确实很强劲,佩服!
max=10000
Zswang_0 开始运行
3092
168
2275
2904
共耗时314毫秒
ChrisAK_0 开始运行
3092
168
2275
2904
共耗时344毫秒
Lihanbing_0 开始运行
3092
168
2275
2904
共耗时259毫秒
sp1234_0 开始运行
3092
168
2275
2904
共耗时343毫秒
softman11_0 开始运行
3092
168
2275
2904
共耗时141毫秒
请按任意键继续. . .
lilip_serein 2010-06-06
  • 打赏
  • 举报
回复
题目中并未给出数字的可用范围,因此默认连小数亦有可能出现,则:
1. 对数组进行QSort,时间复杂度O(nlogn),算法到处都是,就不写伪代码了。
2. 遍历一遍数组,记录连续出现次数最多的前4个数,时间复杂度O(n):
for (i = 1; i++; i <= n)
j = i + 1
while (arr[i] == arr[j] && j <= n) do j++
for (k = 1; k++; k <= 4)
if (j - i > freq[k]) then
freq.insertAt(k, j - i);
freq.dropAt(5);
res.insertAt(k, arr[i]);
res.dropAt(5);}
break;
end if
end for
i = j
end for
其中freq、res为[1..5]的数组,用于记录频率和对应数字。
总时间复杂度O(nlogn)

从测试代码中可以看出隐含的条件:不超过1000的正整数,因此可简化算法为:
1. 遍历一遍数组,记录所有出现过数字的频率,时间复杂度O(n),另需空间O(m):
for (i = 1; i++; i <= n)
f[arr[i]]++;
end for
其中f[0..1000]数组,用于记录频率。
2. 遍历一遍数组,从中找出频率最大的数字,时间复杂度O(n):
for (i = 1; i++; i <= n)
for (k = 1; k++; k <= 4)
if (f[arr[i]] > freq[k]) then
freq.insertAt(k, f[arr[i]]);
freq.dropAt(5);
res.insertAt(k, arr[i]);
res.dropAt(5);}
break;
end if
end for
end for
其中freq、res为[1..5]的数组,用于记录频率和对应数字。
总时间复杂度O(n)。(若第二次并非遍历arr数组,而遍历f数组,则时间复杂度为O(n+m),需依赖于数据的最大值)
peejacky 2010-05-27
  • 打赏
  • 举报
回复
这个对我来说还比较困难,我会虚心学习的啊!
kkkkkkmn 2010-05-27
  • 打赏
  • 举报
回复
。。。。。。。。。。。。。。。。。
mai359954900 2010-05-26
  • 打赏
  • 举报
回复
up up

菜鸟帮顶中
zhxingway 2010-05-25
  • 打赏
  • 举报
回复

路过.
wenfeng1 2010-05-14
  • 打赏
  • 举报
回复
我觉得楼主题目应该改成“一千个数中寻找出现频率最多的4个”,一直在数组个数为1000的数组中做排序,怎么能说是一亿个呢? 楼主表述有误。
liu2510865 2010-05-13
  • 打赏
  • 举报
回复
飘过···
wenfeng1 2010-05-13
  • 打赏
  • 举报
回复
[Quote=引用楼主 zswang 的回复:]
性能最高者奖励150分专家分+1000可用分,其余酌情散掉。

需求:寻找数组中出现频率最多的4个数
数字=出现次数
56=101024
488=100892
576=100879
91=100857
247=100820
160=100811
323=100789
213=100762
417=100756
363=100754

特殊情况
出现并列任意抽取即可
……
[/Quote]
这位老师,有些地方看不懂,能解释一下吗?
xujia7625 2010-05-13
  • 打赏
  • 举报
回复
看着有点晕
加载更多回复(485)

110,533

社区成员

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

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

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