首页
社区
课程
招聘
[求助]工作中遇到的一个追求速度的算法,国外有人控制在30秒内,我却要6分钟.
发表于: 2011-10-12 18:57 36306

[求助]工作中遇到的一个追求速度的算法,国外有人控制在30秒内,我却要6分钟.

2011-10-12 18:57
36306
算法题:通过最快的速度遍历下面黑盒中的字串(字串大于1G),并写入到文件.

黑盒中数据的格式如下:
000000000000000000000000000000000000000000000001111111111111
111111111111111111111122222222222222222223333333333333333333
333300000000000000000000000001111111111111111111111111111122
222222222222333333333333333333333333333300000000000000000001
111111111111111111122222222222222222222222222223333333333333
333333333333333333333333333330000000000000000000000000000001
111111111111111111111111111111111122222222222222222222222222
333333333333333333333333300000000000000000000000001111111111
111111111111111111111112222222222222222222222222222333333333
333333333333333300000000000000000000000000000000111111111111
111111111111111222222222222222222222222222333333333333333333
333333333333333300000000000000000000000000000000000000000000


想获取黑盒里某处的值是多少,调用下面的函数(dwPos最大值等于黑盒中字串的长度).
BYTE GetPosValue(DWORD dwPos);


例如:				                      结果:
    BYTE byVal = GetPosValue(0);	     byVal == 0;

    BYTE byVal = GetPosValue(48); 	    byVal == 1;(第一个1出现的位置是48)


黑盒中每一种数值连续出现的次数不一定是相同的.但即使不同,也不会相差太远.
例如:
    不会出现的情况: 某处有10000个连续的1,紧接着出现50个连续的2.
    常见的情况    : 某处有10000个连续的1,紧接着出现的2可能在7654--13456之间.

整个遍历过程中,调用GetPosValue这个函数的次数越少越好。

如果存在一种极端的情况:
    就是上面提到的 : 某处有10000个连续的1,紧接着出现50个连续2.

    这里的50个2可以忽略掉,把它当作它前面的1来保存也可以.

国外有人将1.2G的数据在30秒内整出来了,黑盒数据量越大,要的时间就越长。

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 0
支持
分享
最新回复 (85)
雪    币: 527
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
楼主能否说下思路,可以参考一下,关注中……还有就是楼主给出的格式每一个都代表一BYTE吗?总过有1G大的数据量?
2011-10-12 19:40
0
雪    币: 1839
活跃值: (295)
能力值: ( LV9,RANK:370 )
在线值:
发帖
回帖
粉丝
3
如果一个一个的遍历,1G,的确需要很长时间。

用2分法就减少很多啊。

定义一个  数据块大小,比如10000,

在这10000大小的数据块中,用二分法查询,1和2的分界点,平均只需要调用GetPosValue,13次就够了。因为2的13次方差不多就够10000这么大了。

这样快的就非常多了。
2011-10-12 19:46
0
雪    币: 416
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
修改你的演算法 盡量多重迴圈就請把它變成

讀取檔案,盡量用binary 0.0。

大部分就是演算法,通常都是優化for迴圈。

或者是不要用遞迴,盡量用迴圈,除非你遞迴的數字不大。
2011-10-12 20:04
0
雪    币: 188
活跃值: (85)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
5
如果我理解的没错, 数据是按0,1,2,3这样循环出现的,且每次循环不会丢失某一种序列

我的做法:
从黑盒读数据写文件的时候对数据压缩成类似位图的方法
比如100个0,200个1,300个2,200个3,。。。的序列
存储成100,300,600,800,。。。 的类似斐波那契
这样每个位置的数值可以通过二分法对应参数的位置
而存储序列的位置又可以通过取膜对应成真实的数据0,1,2,3。。。
对于压缩度高的应该比较快吧
2011-10-12 21:14
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
恩,黑盒中的每个位置都是一个byte..

我的方法:(GetPosValue的参数按以下规律来获取黑盒中的数据)
0    //在这里取的是0
20
40
80
160
320   
640  //如果在这里发现取的是1,与前面的不一样了,那么就从320到640之间采用二分法来找相邻的位置

另一种方法(也是按以下规律来获取黑盒中数据):
0          //在这里取的是0
1000
2000
3000
4000  //在这里取的是1,与前面不一样了,那么就从3000到4000之间用二分法。

两者速度差不多。

还一种思路,没写出代码,感觉速度也不快。
还是按
20
40
80
160
320

发现在160和320间不一样了,
于是从160这里开始:
160
160 + 20
160 + 40
160 + 80
160 + 160........
速度应该更慢。
2011-10-12 21:17
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
目前是采用的二分法.但我发觉2分的效率还不是蛮高的。
2011-10-12 21:22
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
黑盒中的数据是不透明的。你想获取某处的数据只有通过调用函数GetPosValue。而且一次只能获取一个byte.
2011-10-12 21:26
0
雪    币: 1632
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
等s牛来弄个3秒的,吓死你。
2011-10-13 12:12
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
其实我也蛮希望S大来关照一下哈。。什么算法对S牛来说都是浮云。。。。我对S牛的膜拜用登峰造极都形容不过来。。。。
2011-10-13 14:28
0
雪    币: 139
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
学习到新东西了,另外请问谁是S牛啊??
2011-10-13 15:22
0
雪    币: 1233
活跃值: (907)
能力值: ( LV12,RANK:750 )
在线值:
发帖
回帖
粉丝
12
最极端的情况每个byte都不一样,你怎么搞,这只能一个一个的遍历
否则你没办法确保数据是对的,二分法不能确保数据正确阿,最快的算法就是
一个byte一个byte的遍历
write_to_binary_file(GetPosValue(pos++))
OVER!!!
2011-10-13 16:04
0
雪    币: 206
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
呵呵学习 了
2011-10-13 16:20
0
雪    币: 142
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
好像蛮有趣的样子,坐等高手的解决思路
2011-10-13 16:42
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
没有这种最极端的情况.
可以保证0、1、2、3每个byte连续出现的个数至少在20000以上.

二分法的确不能保证数据全部正确,但当出现2W个1后面接几百个2的情况,可以忽略掉这几百个2

用write_to_binary_file(GetPosValue(pos++))的确能保证百分百的精度,但那就不存在算法了啊。而且速度就。。。。
2011-10-13 16:44
0
雪    币:
能力值: (RANK: )
在线值:
发帖
回帖
粉丝
16
如果格式是固定的依次由多个0,1,2,3组成, 那么可以用RLE方式压缩, 即{11, 22, 33, 44}, {22,33,44,55}这样, 四个一组, 每个数分别对应0,1,2,3的个数.
查找时直接计算总和就可以了.
2011-10-13 21:25
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
格式的确是固定的0、1、2、3组成,
但四个元素的个数并不能确定,
而且每个元素的连续数量不一定相等。
并且调用GetPosValue() 函数,一次只能取一个位置的值。
2011-10-13 21:32
0
雪    币:
能力值: (RANK: )
在线值:
发帖
回帖
粉丝
18
刚才没看清要求. 如果有统计信息, 比如"可以保证0、1、2、3每个byte连续出现的个数至少在20000以上", 那么可以以这个统计信息做为跨度, 依次读1, 20001, 40001, ..., 如果相邻两次的数据不同, 比如分别为0, 1, 那就按20000个0, 从20001开始是1, 这样输出(反正你说部分数据是可以忽略的).
2011-10-13 21:45
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
兄弟谢谢你,
我做过这种方案,但有的连续次数不是2W,说不定只有1.3W,那么2W会跳过。
有的连续次数可能会是2.1W。现在最耗时的处理就是这种情况了,因为在这里要用二分。
而且这种情况出现频率非常高。
2011-10-13 22:47
0
雪    币: 2015
活跃值: (902)
能力值: ( LV12,RANK:1000 )
在线值:
发帖
回帖
粉丝
20
这是一个二分枚举的问题,其实就是在一个退化了的有序序列中进行二分查找,只是这个序列仅含0、1、2、3这4种值。兄弟知道怎么谷歌了吧!
2011-10-13 23:08
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
多谢天易大侠指点,为了学习二分枚举,我翻了好多ACM题,但发现好多是英文题目。

还在继续google中。。

如果大家还有其它的思路,也来发表发表呀。
2011-10-14 15:32
0
雪    币: 527
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
既然楼主可以保证 0、1、2、3每个byte连续出现的个数至少在20000以上 何不以20000为分界线去查找呢?还是用二分,基本思路如下(不知可行否)取第一个位置的值假设是1,保存做比较值,然后取20000处的值,有两种情况,1.如果一样则在20000到40000之间进行二分查找,找到最后一个1并记录当前位置,规模缩小了,之后继续类似的操作,之前的值可以认为都是1(因为是连续的) 2.不一样则在0到20000之间进行二分查找,找到最后一个1,记录位置,规模缩小了,之后继续类似的操作....(有一个小问题就是如果规模小于1000(即连续的数量太小的的时候,可以忽略,暂时不考虑))  以上是我的一点不成熟的想法,有待完善,希望大家多交流,如果有办法生成仿真数据的话,希望楼主告知,我们也可以在自己的机子上进行速度测试
2011-10-14 15:51
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
谢谢你。我用的第一总方法与你提供的思路差不多。
我是用
0
10000
20000
30000
这样的dwPos来累加的。。
我发现现在最耗时的就是二分查找的时候。

如果要生成测试文件很简单。
以2W为基数,用一个1.6W 到  2.3W之间的随机值来写0、1、2、3.生成一个2G的文件就可以了。我这里的数据是在硬件中取的,提供不了伪文件
2011-10-14 16:03
0
雪    币: 527
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
个人感觉二分查找不会费那么多时间,20000个数据查找14左右次就可以找到你要的数据了,一共需要进行 2的30次方 处以 20000 再乘以 14 次查询. 猜测可能是楼主代码的问题(具体不详),二分应该是查找最快的了,能否先把数据读到内存中在内存中进行查找呢? 周末有时间的话一定要写代码测试
2011-10-14 16:20
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
二分查找某个数的位置的确很快的。
但我现在的二分是找相邻的两个byte不等.
所以比查找某个数位置的二分要耗时一些。。

现在我是这样做的(假设发现2W和4W的位置不等了):
DWORD min = 2W;
DWORD max = 4W;
if (F(min) != F(max))  //这里只是做为递归的依据,
{
	if ((F(max) == F(max-1)) && (F(max-1) != F(min)))
	{//说明 max 处相邻的两个是 相等 的,并且 不等于 min的位置处,此时要max设为3W
		....小值不变min = 2W,大值 max 为3W后递归....
	}

	if ((F(max) == F(max-1)) && (F(max-1) == F(min)))
	{//说明 max 处相邻的两个是 相等 的,并且 等于 min处位置的值,
		...此时要将 mix 设为3W,max 设为4W(这里的将max设为4W应该可以优化的,因为前面已经读过这里的值)...
	}

	if ((F(max) != F(max-1)) && (F(max-1) == F(min)))
	{//max 不等于 max-1 ,说明找到分隔点了,直接返回max
		return max
	}
}

我的大致程序就是这样子的。感觉这个算法很迂回,


不行的,因为调用GetPosValue一次只能取一个byte.
如果我能全放到内存里*pBuf++ = GetPosValue(dwPos++);
相当于是全部遍历了一遍
2011-10-14 16:55
0
游客
登录 | 注册 方可回帖
返回
//