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

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

2011-10-12 18:57
36307
收藏
免费 0
支持
分享
最新回复 (85)
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
51
使用环境:winxp 下的硬件访问,每使用一次GetPosValue都会调用几百次IO。
不太清楚入口技术是啥,不知道是不是说的需要积累哪方面知识??因为这个问题可以抛开物理设备等概念,只要用算法就可以了。

技术限制当然就是用算法了,不过速度的关键还是在于每一段连续的0、1、2、3最少保证在多少数目,最多保证在多少数目。。

感谢大家关注,我因为工作忙,这些天没有讨论这个了,但问题依然没有解决。
2011-11-1 17:41
0
雪    币: 1737
活跃值: (110)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
52
还不太懂,只能帮你顶下了……
2011-11-2 21:05
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
53
算法固然重要,但是硬件的配置也很重要,还有实现算法的途径!

比如,1G数据如果使用字符型,访问速度会比直接内存访问慢;使用多核CPU会比单核快。

算法用时对比,必须是相同条件下,否则没有意义。
2011-11-2 21:36
0
雪    币: 17
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
54
我觉得先用一定的  GetPosValue次数去获得大致的连续数量,以这个为基准再做二分法会靠谱点吧,然后把连续次数的结果都记录下(黑盒  GetPosValue花费IO多,但用主机来进行运算和预测就不用那么费时间了 ),然后获取大致的连续范围,然后就可以越来越准确和快速了
2011-12-16 16:35
0
雪    币: 2210
活跃值: (12)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
55
先关注一下,晚上回去想想
2011-12-16 17:17
0
雪    币: 2210
活跃值: (12)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
56
我觉得楼主的20,40,60这样二分的步长有点太保守了

不是说数据重复的次数差不多吗,前几个的重复长度可以记录下来,
做为后面查找时的步长,
比如前四个0,1,2,3都是2W左右和重复,那么查找下个数据的步长先设置为2W,然后再在2W的左右进行查找
2011-12-20 12:56
0
雪    币: 1
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
57
如果黑盒里数据都是0、1、2、3的话不管有没有其他规律都可压缩,方法是用2比特表示一个数据,这样就可将4个数据变成一个字节,定位查找也很容易实现。

如果出现相邻字符重复的概率很高且黑盒里数据都是0、1、2、3的话,则可用一个字节中6比特表示重复次数,另外两比特表示值(0、1、2、3),这样可大幅压缩空间,为了便于定位查找,可通过等间距的插入位置参数,这样定位查找时先采用二分法,然后查找。
2011-12-21 20:11
0
雪    币: 1
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
58
  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
  }
这些地方不要用递归,只用一个循环就可
2011-12-21 20:59
0
雪    币: 41
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
59
LZ牛淫把KX给我把。。
我注册一年了还没到正式会员,
2011-12-21 22:25
0
雪    币: 41
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
60
ReadAll(int m_BoxLength)//遍历并保存到文件或内存
{
  int m_Current=0;
  while(m_Current<m_BoxLength)
  {  
      BOOL IsEnd=FALSE;
      int m_HeiPos=m_Current+20000;      //20000为最少重复字符数值
      if(m_HeiPos>m_BoxLength){m_HeiPos=m_BoxLength;IsEnd=TRUE;}
      BYTE m_Vaule
      m_Vaule=GetPosValue(m_Current);
      if(IsEnd==FALSE)
      {
         while(m_Vaule==GetPosValue(m_HeiPos))
        {
           m_HeiPos+=20000;
           if(m_HeiPos>m_BoxLength){m_HeiPos=m_BoxLength;IsEnd=TRUE; break;}  
        }
      }
      if(IsEnd==FALSE)
      {  
         int m_LowPos=m_HeiPos-20000;
         while(1)
        {
           int m_MidPos=(m_LowPos+m_HeiPos)/2;
           if(m_Vaule==GetPosValue(m_MidPos)) m_LowPos=m_MidPos;  
           else m_HeiPos=m_MidPos;
           if(m_HeiPos=m_LowPos+1) break;
         }
       }
       SaveVaule(m_Vaule,m_HeiPos-m_Current);
       //SaveVaule(int m_Vaule,int m_SameLength)的作用是将读取到值保存到文件中或内存中,自己写吧
       m_Current=m_HeiPos;
   }
}
2011-12-21 23:20
0
雪    币: 416
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
61
那種應該是用到演算法的技巧了吧,他沒有遍歷,但是他的也很準確的那種方法,那種我有學到。演算法有不少這種東西。
2011-12-21 23:28
0
雪    币: 41
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
62
我虽然在60楼给出了遍历算法,但没经调试,所以我希望楼主采用该法后,能公布测试结果(包括 DEBUG版本和RELEASE版本)
2011-12-21 23:58
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
63
这,一直没关注了。。马上测试。。谢谢你。
2011-12-24 23:20
0
雪    币: 1123
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
64
我觉得啊,你应该给出你平均调用一次GetPosValue需要多少时间,分别调用100次,500次,1000次给出各自的平均时间。假设这里记为t0,平均连续串长度是20k吧(2W),就是占用20KByte=160Kb。1G/160Kb=1024*1024/160= 6553.6(个),就是至少要使用65534次GetPosValue吧,楼主可以测试一下调用65534次GetPosValue需要多少时间吗?如果用二分,需要在平均2*2W个数据(保守估计)中找到分割点,所以平均每次找到一个分割点需要log2(40k) = 15.28次 算作16次(最坏次数)吧,所以调用65534*16次GetPosValue。
猜测。还有外国人是1.2G,30秒,你是1G,6min。6*60、30* 1.2/1 = 14.4 和16差不多啊。总感觉楼主是不是哪里搞错了啊!比如问题的描述,自己写的代码,或者实验环境什么的...
2011-12-25 14:11
0
雪    币: 1123
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
65
对了,你同时也需要对顺序读取和随机读取分别进行测试,计算平均时间,针对GetPosValue!
2011-12-25 14:23
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
66
VOID ReadAll(DWORD m_BoxLength)//遍历并保存到文件或内存
{
 DWORD dwPos = 0;
 DWORD m_Current = 0;
 while(m_Current < m_BoxLength)
 {  
  BOOL IsEnd = FALSE;
  DWORD m_HeiPos = m_Current + 20000;      //20000为最少重复字符数值
  if(m_HeiPos > m_BoxLength)
  {
   m_HeiPos = m_BoxLength;
   IsEnd = TRUE;
  }
  BYTE m_Vaule = GetPosValue(m_Current);
  if(IsEnd == FALSE) 
  {
   while(m_Vaule == GetPosValue(m_HeiPos))
   {
    m_HeiPos += 20000;
    if(m_HeiPos > m_BoxLength)
    {
     m_HeiPos = m_BoxLength;
     IsEnd = TRUE; 
     break;
    }  
   }
  }
  if(IsEnd == FALSE)
  {  
   int m_LowPos = m_HeiPos - 20000;
   while(1)
   {
    //int m_MidPos = (m_LowPos+m_HeiPos) / 2; //取中间值
    int m_MidPos = m_LowPos + (m_HeiPos - m_LowPos) / 2; //修改:取中间值
    if(m_Vaule == GetPosValue(m_MidPos)) 
     m_LowPos = m_MidPos;  
    else 
     m_HeiPos = m_MidPos;
    if (GetPosValue(m_LowPos) != GetPosValue(m_LowPos + 1))//修改这里的退出条件
     break;
    //if(m_HeiPos = m_LowPos + 1) //修改后代码
    // break;
   }
  }
  memset(pFile + dwPos, m_Vaule, m_HeiPos - m_Current); //pFile是内存映射的文件缓冲指针
  dwPos += (m_HeiPos - m_Current);
  //SaveVaule(int m_Vaule,int m_SameLength)的作用是将读取到值保存到文件中或内存中,自己写吧
  m_Current = m_HeiPos;
 }
}


修改了一下并且做了测试。。你的思路和我的应该是一样。也是二分比较相邻的是否不等,并用它来作为退出的条件。
现在这个速度就取决于最少重复次数20000..合理的设置它的值,能有效的控制速度。

谢谢你提供这个算法,比我自己写的二分要好多了。。。学习。。
2011-12-25 14:37
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
67
FK   [word packed]
2011-12-25 14:39
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
68
那就这样做测试:生成一个500M的文件,文件填充0、1、2、3、4、5这样的数据,连续的数量控制在5W-6W左右(之前说的是2W,这里可以改大点)。每调用一次函数只能取某个偏移的值。
要把所有的分界找出来。看看多长时间。

我这里调用GetPosValue是读硬件上面的数据,实在无法提供测试文件。。。一个函数的调用有几百次IO。
2011-12-25 14:49
0
雪    币: 1123
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
69
我是让你自己测试一下时间,然后公布出来!
这样就可以估算用二分法的时间了。到底是你自己写的程序有问题,还是什么的就清楚了。
分别调用100次,500次,1000次,2000次,。。。给出各自的平均时间
楼主可以测试一下调用65534次GetPosValue需要多少时间
我是让你测试,明白吗?然后把测试结果公布出来。
比如时间为t0,那么二分法差不多是用了65534*16*t0的IO时间
2011-12-25 15:16
0
雪    币: 1123
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
70
自我感觉lz是由于二分法使用不当造成的。
2011-12-25 15:17
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
71
release 下测试了一组数据:
1000次:
开始时间 :   11694796毫秒, 11694秒
结束时间 :   11696328毫秒, 11696秒
差值:1532
----------------------------------

3000次:
开始时间 :   11696328毫秒, 11696秒
结束时间 :   11700640毫秒, 11700秒
差值:4312
----------------------------------

5000次:
开始时间 :   11700640毫秒, 11700秒
结束时间 :   11707828毫秒, 11707秒
差值:7188
----------------------------------

8000次:
开始时间 :   11707828毫秒, 11707秒
结束时间 :   11719546毫秒, 11719秒
差值:11718
----------------------------------

15000次:
开始时间 :   11719546毫秒, 11719秒
结束时间 :   11741390毫秒, 11741秒
差值:21844
----------------------------------

20000次:
开始时间 :   11741390毫秒, 11741秒
结束时间 :   11770437毫秒, 11770秒
差值:29047
----------------------------------

使用的二分法是60楼的兄弟提供的
2011-12-25 17:28
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
72
兄弟上线后PM我,我跟你给个邀请码
2011-12-25 17:49
0
雪    币: 1123
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
73
那就是平均调用一次1.4ms~1.5ms,那就记为t0=1.45ms吧。
那么二分法差不多是用了65534*16*t0的IO时间=6554*16*1.45ms = 152.0s=2.53min.
麻烦你再统计一下,你使用上面的方法总共得到了多少连续的字符串。假设为m个,则总IO时间平均为m*log2(numOfALLBtyes/m)*1.45ms
你把步进的策略改变一下。
设下一个要读取的数据的位置为pnext。当前连续字符串的开头位置为pcurrent。上一个连续字符串的长度为lenLast,两个连续字符串长度差最大值为MAX_DELTA,则取pnext = pcurrent + 2*(lenLast - MAX_DELTA )【比较保守的步进方式,你还可以再试试大胆一点比如pnext = pcurrent + 2*lenLast - MAX_DELTA 】。明白我的意思吗?
这样会大大缩短时间的。当平均长度为10W时,则平均有1342个连续字符串。
IO时间=1342*log2(10W)*1.45ms=1342*16.6*1.45ms=32s。大约是那个外国人的时间吧(至少数量级一样了O(∩_∩)O~)。
2011-12-25 18:13
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
74
先膜拜一下。
我测试将156 301 488的数据量整理出来,得花3.6分钟.  我把步进改为1000和2000和3000效果不大。
改大后,二分时间长。改小后,二分时间短,但前面时间又变长了。

我把你的分析过程一字一字的啃了几遍,我发现我的数学好差。
2011-12-25 19:13
0
雪    币: 1123
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
75
步长不是你随意改的,是根据前一个连续字符串的长度定的(程序动态的更改的),最少是2W。
2011-12-25 20:25
0
游客
登录 | 注册 方可回帖
返回
//