高效滚动最大和最小窗口我想高效计算滚动的最大和最小值。比每次窗口移动时重新计算所有使用的值的最大值/最小值要好。这里有一篇文章提出了同样的问题,有人发布了一个涉及某种堆栈方法的解决方案,该方法据称可以根据其评级工作。但是,对于我来说,我再也找不到它了。在寻找解决方案或帖子时,我们将不胜感激。谢谢你们!您要使用的算法称为升序最小值(C++实现)。要在C#中执行此操作,您需要获取deque类,NuGet上有一个名为Nito.Deque的好类。我已经使用Nito.Deque编写了一个快速的C#实现,但我只是简单地检查了一下,然后从头开始,所以它可能是错误的!publicstaticclassAscendingMinima{privatestructMinimaValue{publicintRemoveIndex{get;放;}publicdoubleValue{get;放;}}publicstaticdouble[]GetMin(thisdouble[]input,intwindow){varqueue=newDeque();varresult=newdouble[input.Length];对于(inti=0;i0&&i>=queue[0].RemoveIndex)queue.RemoveFromFront();while(queue.Count>0&&queue[queue.Count-1].Value>=val)queue.RemoveFromBack();queue.AddToBack(newMinimaValue{RemoveIndex=i+window,Value=val});结果[i]=队列[0].Value;}返回结果;这是一种更有效的方法。您仍然需要偶尔计算该值,但除了一些退化数据(递减值),在此解决方案中已将其最小化。我们将自己限制在最大范围内以简化事情,但扩展到最小范围也很容易。您只需要以下内容:这个想法是使用max和maxcount作为缓存来保存当前的最大值。在缓存有效的情况下,只需要返回其中的值即可,即非常快速的常量时间操作。如果在请求最大值时缓存无效,则填充缓存然后返回值。这比上一段中的方法慢,但是一旦缓存有效,后续的maxrequests将使用更快的方法。以下是维护窗口和相关数据的操作:获取下一个值N如果窗口已满,则删除最旧的条目M如果maxcount大于0且M等于max,则递减maxcount。一旦maxCount达到0,缓存就会失效,但是在用户请求最大值之前我们不需要担心这一点(在此之前没有必要重新填充缓存)。将N添加到滚动窗口。如果窗口大小现在为1(N是唯一的当前条目),将max设置为N,将maxcount设置为1,然后返回步骤1。如果maxcount大于0且N大于max,将max设置为N,将maxcount设置为1,则返回步骤1。如果maxcount大于0且N等于max,增加maxcount。返回到步骤1。现在,在窗口管理过程中的任何时候,您都可以请求最大值。这是一个独立的操作,不同于窗口管理本身。这可以按顺序使用以下规则来完成。如果窗口为空,则没有最大值:引发异常或返回一些合理的标记值。如果maxcount大于0,则缓存有效:仅返回max。否则,需要重新填充缓存。遍历整个列表,根据下面的代码片段设置max和maxcount。setmaxtowindow[0],maxcountto0foreachxinwindow[]:ifx>max:setmaxtox,maxcountto1else:ifx==max:incrementmaxcount其实主要是维护max并且仅在需要时重新计算,这比在添加条目时盲目地重新计算效率更高。对于一些明确的统计数据,我创建了以下Python程序。它使用大小为25的滑动窗口并使用0到999之间的随机数(您可以使用这些属性来查看它们如何影响结果)。首先是一些初始化代码。注意统计变量,它们将用于计算缓存命中和未命中:importrandomwindow=[]max=0maxcount=0maxwin=25statCache=0statNonCache=0然后一个函数根据我的给窗口添加一个数字上面的描述:defaddNum(n):globalwindowglobalmaxglobalmaxcountiflen(window)==maxwin:m=window[0]window=window[1:]ifmaxcount>0andm==max:maxcount=maxcount-1个窗口.append(n)iflen(window)==1:max=nmaxcount=1如果maxcount>0且n>max则返回:max=nmaxcount=1如果maxcount>0且n==max则返回:maxcount=maxcount+1接下来,从窗口返回最大值的代码:defgetMax():globalmaxglobalmaxcountglobalstatCacheglobalstatNonCacheiflen(window)==0:returnNoneifmaxcount>0:statCache=statCache+1returnmaxmax=window[0]maxcount=0forvalinwindow:ifval>max:max=valmaxcount=1else:ifval==max:maxcount=maxcount+1statNonCache=statNonCache+1returnmax最后,测试工具:random.seed()foriinrange(1000000):val=int(1000*random.random())addNum(val)newmax=getMax()print("%dcached,%dnon-cached"%(statCache,statNonCache))请注意,每次向窗口添加数字时,测试工具都会尝试获取最大值实际上,这可能不需要.换句话说,这是生成的随机数据的最坏情况。出于伪统计目的运行程序几次,我们得到(出于报告目的格式化和解析):960579缓存,39421非缓存960373缓存,39627非缓存960395缓存,39605非缓存960348缓存,39652非-缓存960441缓存,39559非缓存960602缓存,39398非缓存960561缓存,39439非缓存960463缓存,39537非缓存960409缓存,39591非缓存9602798==c=======9604961So39您可以看到,对于随机数据,平均只有大约3.95%的情况会导致计算命中(缓存未命中)。绝大多数使用缓存值。这应该比每次插入窗口时重新计算最大值要好得多。一些会影响该百分比的事情是:我假设“窗口”是指从[start]到[start+len],并且开始移动。考虑min,max相似,移动窗口a[start+1]到a[start+len+1]。那么窗口的最小值只有当(a)a[start+len+1](较小的值进来)或(b)a[start]==min(其中一个a[start]==min)会变走;重新计算分钟)。另一种可能更有效的方法是用第一个窗口填充优先级队列并更新每个值进入/离开,但我认为这不是更好(优先级队列不适合从中间随机元素“选择””(推进窗口时需要做的事情)。代码会比较复杂。最好坚持简单的解决方案,直到它证明性能是不可接受的并且这段代码是(主要)负责资源占用的。我昨天写了自己的算法,要求改进,我被推荐到这里。事实上,这个算法更优雅。我不确定它是否提供了无论窗口大小如何的恒速计算,但无论如何,我用我自己的缓存算法测试了性能(这很简单并且可能使用与其他人建议的相同的想法)。缓存快8-15倍(使用5、50、300、1000的滚动窗口进行测试我不需要更多)。下面是带有秒表和结果验证的替代方案。以上就是C#学习教程的全部内容:高效滚动最大和最小窗口。如果对你有用,需要进一步了解C#学习教程,希望大家多多关注---staticclassProgram{staticRandomr=newRandom();静态int窗口=50;//(小,方便视觉功能测试)。最终可能是1001000,但不会超过5000。constintFullDataSize=1000;staticdouble[]InputArr=newdouble[FullDataSize];//预先填充随机输入数据的数组。//=======================缓存算法变量staticdoubleLow=0;静态intLowLocation=0;静态intCurrentLocation=0;静态双[]结果1=新双[FullDataSize];//包含缓存的最小结果staticinti1;//incrementor,只是为了将结果存储回数组。在现实生活中,结果甚至不会存储回数组。//======================升序最小算法变量staticdouble[]Result2=newdouble[FullDataSize];//包含升序最小结果。staticdouble[]RollWinArray=newdouble[Window];//缓存算法的数组静态双端队列RollWinDeque=newDeque();//Niro.Dequenuget。静态整数i2;//由双端队列的结构使用(不仅用于结果存储)//======================================我最初提出的缓存算法staticvoidCalcCachingMin(doublecurrentNum){RollWinArray[CurrentLocation]=currentNum;如果(currentNum0&&i2>=RollWinDeque[0].RemoveIndex)RollWinDeque.RemoveFromFront();while(RollWinDeque.Count>0&&RollWinDeque[RollWinDeque.Count-1].Value>=newNum)RollWinDeque.RemoveFromBack();RollWinDeque.AddToBack(newMinimaValue{RemoveIndex=i2+Window,Value=newNum});结果2[i2++]=RollWinDeque[0].Value;}publicstaticdouble[]GetMin(thisdouble[]input,intwindow){//这是上升mimima的初始方法扩展//取自http://stackoverflow.com/a/14823809/2381899varqueue=new双端队列();varresult=newdouble[input.Length];对于(inti=0;i0&&i>=queue[0].RemoveIndex)queue.RemveFromFront();while(queue.Count>0&&queue[queue.Count-1].Value>=val)queue.RemoveFromBack();queue.AddToBack(newMinimaValue{RemoveIndex=i+window,Value=val});结果[i]=队列[0].Value;}返回结果;}//=============================================测试程序.staticvoidMain(string[]args){//这是测试程序。//它对同一数据运行两种算法的多次尝试。for(intj=0;j本文采集自网络,不代表立场,如涉及侵权,请点右联系管理员删除,如需转载请注明出处:
