mql5语言的特点、微妙之处以及技巧 - 页 136

 
Nikolai Semko:
所以你愿意点燃一张100美元的钞票来寻找滚落在床下的一角钱?

可能是也可能不是一枚硬币

   srand(GetTickCount());
   uint v10 = 0, v19900 = 0;
   for (uint i = 0;  i < UINT_MAX;  ++ i) {
      int cur = rand();
      if (cur % 20000  == 10)
         ++ v10;
      else if (cur % 20000  == 19900)
         ++ v19900;   
   }
   Alert("10 probability = ", (double)v10/UINT_MAX*100, "%;  1990 probability = ", (double)v19900/UINT_MAX*100, "%");
   // Alert: 10 probability = 0.006103515626421085%;  19900 probability = 0.003051757813210543%
        

10的概率是两倍。而最重要的是,get_rand()的成本性要求被吸走了,所以当你可以拥有一个正态分布时,为什么要通过后门获得 概率偏移的随机数(同时期待一个均匀分布)?你保存的不是一张100美元的纸币,你保存的是火柴。

 
Vict:

可能是也可能不是一枚硬币

10的概率是两倍。而最重要的是,get_rand()的成本性要求被吸走了,所以当你可以拥有一个正态分布时,为什么要通过后门获得 概率偏移的随机数(同时期待一个均匀分布)?你保存的不是一张100美元的纸币,你保存的是火柴。

是的,我错了,你的功能太慢了。我误解了这个算法。对不起。
但我的算法仍然是所有提议的算法中最快的,尽管它更普遍,不像你的算法那样只限于32767。
代码来证明这一点。

这个脚本随机地生成一个具有随机颜色和随机坐标的点阵列。数组的大小等于图表图上的像素数。它重复了5次

  1. 用常规的rand()函数
  2. 用你的get_rand()函数
  3. 使用我的函数 ulong randUlong()
  4. 用我的函数uint randUint()
  5. 使用@AlexeyNavoykov的 ulongRandomLong() 函数
2019.06.0903:42:25.958 TestSpeedRand (EURGBP,H4)       Время формирования случайных массивов = 9894 микросекунд.  Всего сгенерировано 5203975 случайных чисел rand()
2019.06.0903:42:28.010 TestSpeedRand (EURGBP,H4)       Время формирования случайных массивов = 24899 микросекунд. Всего сгенерировано 5203975 случайных чисел get_rand()
2019.06.0903:42:30.057 TestSpeedRand (EURGBP,H4)       Время формирования случайных массивов = 22172 микросекунд. Всего сгенерировано 5203975 случайных чисел randUlong()
2019.06.0903:42:32.098 TestSpeedRand (EURGBP,H4)       Время формирования случайных массивов = 16013 микросекунд. Всего сгенерировано 5203975 случайных чисел randUint()
2019.06.0903:42:34.145 TestSpeedRand (EURGBP,H4)       Время формирования случайных массивов = 25524 микросекунд. Всего сгенерировано 5203975 случайных чисел RandomLong()

我以这样的方式挑选数字来显示问题的本质,当我们应用rand()%20000时


因为它应该是。


//+------------------------------------------------------------------+
uint get_rand(uint max)
  {
   staticbool f=false;
   if(!f) 
     {
      f=true;
      srand(GetTickCount());
     }
   uint limit=(max+1) *((32767+1)/(max+1));
   uint val;
   while((val=rand())>=limit);
   return val % (max+1);
  }
//+------------------------------------------------------------------+
ulong randUlong(ulong max=ULONG_MAX){return(((ulong)rand()<<60)|((ulong)rand()<<45)|((ulong)rand()<<30)|((ulong)rand()<<15)|(ulong)rand())%max;}
//+------------------------------------------------------------------+
uint randUint(uint max=UINT_MAX) {return(((uint)rand()<<30)|((uint)rand()<<15)|(uint)rand())%max;}
//+------------------------------------------------------------------+
ulong RandomLong(ulong range)
  {
#define _MAXRND(rang,rnd_range) ((rnd_range) -((rnd_range)-rang)%rang-1)
#define _RND ulong(rand())
   ulong rnd,max,const bit=1;
   if(range <= bit<<15) { if(!range) return0;  max=_MAXRND(range, 1<<15);  while((rnd=_RND) > max);  return rnd%range; }
   if(range <= bit<<30) { max=_MAXRND(range, bit<<30);  while((rnd=(_RND | _RND<<15)) > max);  return rnd%range; }
   if(range <= bit<<45) { max=_MAXRND(range, bit<<45);  while((rnd=(_RND | _RND<<15 | _RND<<30)) > max);  return rnd%range;  }
   if(range <= bit<<60) { max=_MAXRND(range, bit<<60);  while((rnd=(_RND | _RND<<15 | _RND<<30 | _RND<<45)) > max);  return rnd%range; }
   else  { max=_MAXRND(range,bit<<64);  while((rnd=(_RND|_RND<<15|_RND<<30|_RND<<45|_RND<<60))>max);  return rnd%range; }
#undef _RND
#undef _MAXRND
  }
//+------------------------------------------------------------------+
但99.9%的情况下,这个功能也会起作用。
uint randUint(uint max) {return(((uint)rand()<<15)|(uint)rand())%max;}
它将工作得更快。
这个函数将生成一个从0到1073741824的随机数。 这个数字甚至大于任何工具在整个历史上的点击数。对于99.9%的任务来说,这种功能的 "不公平 "将是微不足道的。
附加的文件:
 
Nikolai Semko:

但我的算法仍然是所有提议的算法中最快的,尽管它更具有普遍性,不像你的算法那样只限于32767。
作为证明的代码。

谢谢你的工作,真的很有趣的结果。 事实证明,rand()函数是如此之快,以至于它的工作速度比算术运算 快。

 
Alexey Navoykov:

谢谢你的努力,真的很有趣的结果。 事实证明,rand()的速度非常快,比算术运算 还快。

不,不是的。大约一纳秒,就像从一个双数中提取平方根一样。+-*/操作是以纳秒的几分之一进行的。
但就像平方根一样,rand()在现代处理器中是在硬件层面上执行的,而不是以编程方式。

 
Nikolai Semko:

不,不是更快。大约一纳秒,就像从一个双数中提取平方根一样。+-*/操作是以纳秒的几分之一进行的。
但就像平方根一样,rand()在现代处理器中是在硬件层面上执行的,而不是以编程方式。

你的版本与我的不同之处在于,rand()总是被调用5次,而我的版本在20000范围内平均被调用1.64次,在256范围内被调用1次。 在你的代码中,每次迭代rand()被调用25次,而我的是1.64*2+3=5.3次。当然,情况很奇怪,我们必须找出具体的原因。 你那里有很多位操作被执行,此外...

 
Nikolai Semko:

1.我们确实意识到,在你的功能中,问题并没有得到解决,只是被掩盖了,我不会减肥,我只是把腰带勒得更紧。

2.在我们和Alexey的版本中,这是最坏的情况,而在许多其他情况下,速度几乎会达到rand()的水平,而你有恒定的时间。

你有没有想过,为什么rand()生成的数字范围如此狭窄?这是一个伪随机发生器,所以它是周期性的,所以在不需要它的地方生成一堆随机数,随后丢弃这些随机数,它的质量就会越来越差(它将重复先前的内容)。

4.有些人以更复杂的方式挖掘随机数据。比如说我,从网上扯下来,有人可能还会买。我为什么要浪费来之不易的数据,以便然后直截了当地抛弃它(生成乌龙,写一个适当的算法毕竟不是我们的方式)?

 
Alexey Navoykov:

你的版本与我的不同之处在于,它总是调用5次rand(),而我的版本在20000范围内平均调用1.64次,在256范围内调用1次。 你的rand()每次迭代都要调用25次,而我的是1.64*2+3 = 5.3次。当然,这种情况很奇怪,我们必须找出具体的原因。 因为你那里有很多位操作被执行,此外...

位是最便宜的操作。几乎是免费的。

但总体而言,我同意。我也不明白为什么...也许这就是优化的奇迹。虽然那里可以优化的东西...

 
Nikolai Semko:

位是最便宜的操作。几乎是免费的。

但总体而言,我同意。我也不明白为什么...也许这就是优化的奇迹。虽然有什么可以优化...

这似乎与算术运算 无关,因为没有算术运算,所有的值都是在编译阶段计算出来的。 原因是存在一个迭代次数未知的循环(尽管这些迭代平均不到两次)。 所以你的代码由于已知的rand()调用次数而在某种程度上被优化。
 
Vict:

1.我们确实意识到,在你的功能中,问题并没有得到解决,只是被掩盖了,我不会减肥,我只是把腰带勒得更紧。

2.在我们和Alexey的版本中,这是最坏的情况,而在许多其他情况下,速度几乎会达到rand()的水平,而你有恒定的时间。

你有没有想过,为什么rand()生成的数字范围如此狭窄?这是一个伪随机发生器,所以它是周期性的,所以在不需要它的地方生成一堆随机数,随后被丢弃,它的质量就会变差(会提前重复)。

4.有些人以更复杂的方式挖掘随机数据。比如说我,从网上扯下来,有人可能还会买。这就是为什么我应该浪费来之不易的数据,以便然后愚蠢地抛弃它(生成ulong,写一个适当的算法,毕竟不是我们的方式)?

好吧,那是书呆子。
为了重现这种情况,当这个问题至少会被注意到0.1%的时候,你需要在以下数值以上的范围内操作。

  • (ULONG_MAX)/1000= 18446744073709551,用于函数randUlong()
  • (UINT_MAX)/1000= 4294967,用于函数randUint()。

你曾经使用过这样的范围吗? 那你为什么要加入这些检查和循环?
最好的东西是好东西的敌人。

就个人而言,我在实践中的随机生成 范围最多限制在2000、4000。rand()可以很好地实现这一目的。
在我的代码中插入这样一个选项。

ulong t=GetMicrosecondCount();
   for(int i=0;i<size;i++)
     {
      X[i]=ushort(rand()%W.Width);
      Y[i]=ushort(rand()%W.Width);
      clr[i]=XRGB(rand()%256,rand()%256,rand()%256);
     }
   t=GetMicrosecondCount()-t;
   Print("Время формирования случайных массивов = "+string(t)+" микросекунд. Всего сгенерировано "+string(size*5)+" случайных чисел rand()%W.Width");
   Canvas.Erase();
   for(int i=0;i<size;i++) Canvas.PixelSet(X[i],Y[i],clr[i]);
   Canvas.Update();
   Sleep(2000);

所以你不会注意到rand()函数的 "不公平"(就像rand()%20000那样),而且点会在视觉上均匀分布,所以它是最快和最有效的函数。

处理器开发人员将rand()限制在2^15=32768,这不是没有道理的。他们不是愚蠢的人。这涵盖了99%的实际问题。
而对于 "极端 "想法的爱好者来说,有足够多的选择。

ulong randUlong(ulong max=ULONG_MAX){return(((ulong)rand()<<60)|((ulong)rand()<<45)|((ulong)rand()<<30)|((ulong)rand()<<15)|(ulong)rand())%max;}
附加的文件:
 
Nikolai Semko:

就个人而言,我在实践中的随机数生成 范围被限制在2000,最多4000。对于这一点,rand()工作得相当好。
在我的代码中插入这样一个变体。

你不会注意到rand()函数的 "不公平"(就像rand()%20000那样),而且点数在视觉上是统一的,所以它是相当有效的,而且是最快的。

欢迎你使用它,我不介意。当%的右边是RAND_MAX+1(256或1024)的倍数时,更是如此。

处理器开发人员将rand()限制在2^15=32768,这不是没有道理的。他们不是愚蠢的人。它涵盖了99%的实际任务。
而对于那些喜欢 "越界 "想法的人来说,有足够多的变体。

处理器开发人员与此有什么关系?该发生器是由软件实现的。唯一的要求是RAND_MAX >= 32767和至少2^32的周期。所以μl在 "最小值 "时有一个非常稀疏的振荡器。

而最有远见的人会为自己做一个公平的rand()(如果没有倍数的话),这甚至在参考书中被推荐。