讨论一个点名的算法问题
时间:2010-07-02
来源:互联网
在N个人中间点名,找到最近、最频繁被点名的10个人。
现在用这个方式决定最近+最频繁:
每次点名,有一个序列号加1, no++
每个人保留最近两次点名时的序列号:N2(次近) N1(最近),
两次序列号的间隔越小,说明被点名的越频繁。
所以通过N1 - N2 的值排序,就找到最频繁的前10个人。
------------------------------------------------------------------------
问题在这里,还有最近的要求,
如果当前点名总序列号为 no=100, 某个人的 N2=3, N1 = 10,
那么显然有100 - 10次没点到他了。所以他的 N1 - N2 = 7这个频繁程度的值,参考性就不大了。
所以最后需要按照 max(N1 - N2, no - N1) 的值排序,找出“最近+最频繁”。
因为每点名一次,no的值都加1。所以每个人的排序的参考值,没次点名都可能变化。用什么数据结构能满足这个要求?
作者: 群雄逐鹿中原 发布时间: 2010-07-02
作者: prolj 发布时间: 2010-07-02
作者: hellioncu 发布时间: 2010-07-02
作者: 没本 发布时间: 2010-07-02
作者: 没本 发布时间: 2010-07-02
1,只要一条链表和每个人保有的一个count记数就行了。count用来模拟频率(count>=0),每个人按频率count大小从高到低串成一条链表。
2,当其中一人被点名时,它的count++,而其余的count--,然后只要将这个人在链表中做一次插入排序就行了。
如下某一时刻的链表:括号内为频率count
a(9) -> b(6) -> c(3) -> d(1) -> e(0)
如果此时d被点名,重新排序后:
a(8) -> b(5) -> d(2) -> c(2) -> e(0)
作者: maxxfire 发布时间: 2010-07-02
这种方法结构和实现较简单,不过需要遍历整个链表。。
作者: maxxfire 发布时间: 2010-07-02
作者: windfail 发布时间: 2010-07-02
楼主没有给出最近的定义。
就是说优先级到底怎么算。
作者: 狗气球 发布时间: 2010-07-02
其实就是没本讲的页面置换问题,为了最大限度的减少置换次数,想到了最近最频繁这个模型。
作者: 群雄逐鹿中原 发布时间: 2010-07-02
小李还是不会被选择为活跃分子。
作者: 群雄逐鹿中原 发布时间: 2010-07-02
群雄逐鹿中原 发表于 2010-07-02 15:51
小李是最近点到的,小王是最频繁占到的,你要的是哪个?
作者: windfail 发布时间: 2010-07-02
windfail 发表于 2010-07-02 16:10
每此被点到名的,都要装进麻袋里。最多有10个麻袋,麻袋不够时,就解放一个旧人出来。
我要装麻袋次数最少的方案。
所以不是最近的,也不是最频繁的。
作者: 群雄逐鹿中原 发布时间: 2010-07-02
我要装麻袋次数 ...
群雄逐鹿中原 发表于 2010-07-02 16:13
哦,那没本的那个应该是最好的了
作者: windfail 发布时间: 2010-07-02
拿事实证明,数组比双向链表快得不是一点半点的。没有绝对的必要,我从来不用链表。
12 : 7349
- $ cat nc.cpp
- #if defined(_V_)
- #include <vector>
- #else
- #include <list>
- #endif
- #include <algorithm>
- #include <cstdlib>
- #include <cstdio>
- using namespace std;
-
- #define MAX(a,b) ((a)>(b))?(a):(b);
- struct item_t
- {
- item_t():no(0),val(0){N[0]=N[1]=0;}
- int id;
- int no;
- int N[2];
- int val;
- };
-
- #if defined(_V_)
- typedef vector<item_t> ii_t;
- #else
- typedef list<item_t> ii_t;
- #endif
-
- bool sfun( const item_t & v1, const item_t & v2 )
- {
- return v1.val > v2.val;
- }
-
- int main(int argc, char ** argv)
- {
- if( argc<3 )
- {
- printf("Usage: ./nc 人数 点名次数\n");
- return -1;
- }
- const int N = atoi(argv[1]);
- const int No = atoi(argv[2]);
- int i=0;
- ii_t ii(N);
- for( ii_t::iterator iii=ii.begin(), iie=ii.end(); iii!=iie; ++iii )
- iii->id = i++;
- srandom(10); //每次用相同的种子
- //点名
- for( int no=0; no<No; ++no )
- {
- int iN = random() % N;
- #if defined(_V_)
- item_t & it = ii[iN];
- it.no++;
- it.N[1] = it.N[0], it.N[0] = no;
- #else
- ii_t::iterator iins=ii.end(), idel=ii.end();
- for( ii_t::iterator iii=ii.begin(), iie=ii.end();
- iii!=iie; ++iii )
- {
- item_t & it = *iii;
- if( it.id==iN )
- {
- idel = iii;
- it.no++;
- it.N[1] = it.N[0], it.N[0] = no;
- it.val = it.N[0] - it.N[1];
- }
- else
- {
- it.val = MAX( it.N[0]-it.N[1], no-it.N[0] );
- if( idel!=ii.end() && iins==ii.end()
- && it.val<idel->val )
- iins = iii;
- }
- }
- ii.insert(iins, *idel);
- ii.erase(idel);
- #endif
- }
- #if defined(_V_)
- //计算值
- for( ii_t::iterator iii=ii.begin(), iie=ii.end(); iii!=iie; ++iii )
- iii->val = MAX( iii->N[0]-iii->N[1], No-iii->N[0] );
- //排序
- sort(ii.begin(), ii.end(), sfun);
- #endif
- //输出前十个
- int c10=0;
- for( ii_t::reverse_iterator iii=ii.rbegin(), iie=ii.rend();
- iii!=iie && c10<10; ++iii, ++c10 )
- {
- printf( "[id:%d-no:%d-N1:%d-N2:%d-val:%d]\n",
- iii->id, iii->no, iii->N[0], iii->N[1], iii->val );
- }
- return 0;
- }
-
- $ g++ nc.cpp -o ncv -D_V_
- $ g++ nc.cpp -o nc
- $ time ./ncv 2000 100000
- [id:1720-no:46-N1:99990-N2:99976-val:14]
- [id:1581-no:50-N1:99962-N2:99911-val:51]
- [id:1302-no:52-N1:99993-N2:99930-val:63]
- [id:1741-no:52-N1:99954-N2:99878-val:76]
- [id:1188-no:40-N1:99903-N2:99877-val:97]
- [id:470-no:58-N1:99886-N2:99772-val:114]
- [id:116-no:65-N1:99881-N2:99764-val:119]
- [id:835-no:61-N1:99912-N2:99780-val:132]
- [id:1994-no:48-N1:99894-N2:99754-val:140]
- [id:274-no:46-N1:99849-N2:99795-val:151]
-
- real 0m0.012s
- user 0m0.008s
- sys 0m0.003s
- $ time ./nc 2000 100000
- [id:1720-no:46-N1:99990-N2:99976-val:14]
- [id:1581-no:50-N1:99962-N2:99911-val:51]
- [id:1302-no:52-N1:99993-N2:99930-val:63]
- [id:1188-no:40-N1:99903-N2:99877-val:96]
- [id:1741-no:52-N1:99954-N2:99878-val:76]
- [id:274-no:46-N1:99849-N2:99795-val:150]
- [id:1872-no:52-N1:99802-N2:99745-val:197]
- [id:470-no:58-N1:99886-N2:99772-val:114]
- [id:116-no:65-N1:99881-N2:99764-val:118]
- [id:835-no:61-N1:99912-N2:99780-val:132]
-
- real 0m7.349s
- user 0m7.341s
- sys 0m0.007s
- $
作者: 没本 发布时间: 2010-07-02
汗, 我对链表相当的偏爱。。
作者: peidright 发布时间: 2010-07-02
那说明你该改改认识了。

作者: 没本 发布时间: 2010-07-02
我最喜欢用散列,其次是数组,都是相当稳定不容易出错的。
作者: 没本 发布时间: 2010-07-02

弱弱问下,散列是什么?.. hashtable 么?
作者: peidright 发布时间: 2010-07-02
if( idel!=ii.end() && iins==ii.end()
改成
if( idel!=iie && iins==iie
能减少2.5秒时间。
作者: 没本 发布时间: 2010-07-02
作者: 没本 发布时间: 2010-07-02
要排序什么的用数组当然比链表快
作者: windfail 发布时间: 2010-07-02
话说回来,可能还是没本直接排序的办法好,一万个数之内,std::map算上本身的开销,未必比vector好多少。
作者: 群雄逐鹿中原 发布时间: 2010-07-02
作者: 群雄逐鹿中原 发布时间: 2010-07-02
作者: 没本 发布时间: 2010-07-02
群雄逐鹿中原 发表于 2010-07-02 17:05
随时有新人加入没有问题,直接vector::push_back,再赋值一下id字段就行了,不影响最后的排序结果。
作者: 没本 发布时间: 2010-07-03
热门阅读
-
office 2019专业增强版最新2021版激活秘钥/序列号/激活码推荐 附激活工具
阅读:74
-
如何安装mysql8.0
阅读:31
-
Word快速设置标题样式步骤详解
阅读:28
-
20+道必知必会的Vue面试题(附答案解析)
阅读:37
-
HTML如何制作表单
阅读:22
-
百词斩可以改天数吗?当然可以,4个步骤轻松修改天数!
阅读:31
-
ET文件格式和XLS格式文件之间如何转化?
阅读:24
-
react和vue的区别及优缺点是什么
阅读:121
-
支付宝人脸识别如何关闭?
阅读:21
-
腾讯微云怎么修改照片或视频备份路径?
阅读:28