编程中无穷大常量的设定技巧

首先,在做某一些算法的时候,会很常求最大最小值一类的问题,通常我们会设置一个初始一个answer最大int类型的最大或者最小,然后每次比较取大取小即可。

其实如果问题中各数据的范围明确,那么无穷大的设定不是问题,在不明确的情况下,很多程序员都取0x7fffffff作为无穷大,因为这是32-bit int的最大值。如果这个无穷大只用于一般的比较(比如求最小值时min变量的初值),那么0x7fffffff确实是一个完美的选择,但是在更多的情况 下,0x7fffffff并不是一个好的选择。理由如下:

很多时候我们并不只是单纯拿无穷大来作比较,而是会运算后再做比较,例如在大部分最短路径算法中都会使用的松弛操作:
if (d[u]+w[u][v]<d[v]) d[v]=d[u]+w[u][v];
我们知道如果u,v之间没有边,那么w[u][v]=INF,如果我们的INF取0x7fffffff,那么d[u]+w[u][v]会溢出而变成负数, 我们的松弛操作便出错了,更一般的说,0x7fffffff不能满足“无穷大加一个有穷的数依然是无穷大”,它变成了一个很小的负数。

计算机不会表示出“无穷大”的概念,所以我们只能以一个定值来表示“最大”。那么使用什么值呢?

32-bit int举例,我们选择的最大应该满足两个条件

  • 这个最大值真的很大,是和定义的最大值是同一个数量级的
  • 这个最大值+这个最大值并不会溢出的,也就是无穷大嘉无穷大依然是无穷大

所以我们需要一个更好的家伙来顶替 0x7fffffff ,最严谨的办法当然是对无穷大进行特别处理而不是找一个很大很大的常量来代替它(或者说模拟 它),但是这样会让我们的编程过程变得很麻烦。

在我看的大佬上面,最精巧的无穷大常量取值是 0x3f3f3f3f,我不知道是谁最先开始使用这个精妙的常 量来做无穷大,自己也是学以致用,你还别说发现非常好用,而当我对这个常量做更深入的分析时,就发现它真的是非常精巧了。

第一、0x3f3f3f3f的十进制是1061109567,也就是10^9级别的(和 0x7fffffff一个数量级),而一般场合下的数据都是小于10^9的,所以它可以作为无穷大使用而不致出现数据大于无穷大的情形。

第二、由于一般的数据都不会大于10^9,所以当我们把无穷大加上一个数据时,它并不会溢出(这就满足了“无穷大加一个有穷的数依然是无穷 大”),事实上 0x3f3f3f3f + 0x3f3f3f3f = 2122219134,这非常大但却没有超过32-bit int的表示范围,所以 0x3f3f3f3f 还满足了我们“无穷大加无穷大还是无穷大”的需求。

第三、0x3f3f3f3f还能给我们带来一个意想不到的额外好处:如果我们想要将某个数组清零,我们通常会使用 memset(a,0,sizeof(a))这样的代码来实现(方便而高效),但是当我们想将某个数组全部赋值为无穷大时(例如解决图论问题时邻接矩阵的 初始化),就不能使用memset函数而得自己写循环了(写这些不重要的代码真的很痛苦),我们知道这是因为 memset 是按字节操作的,它能够对数组清 零是因为0的每个字节都是0,现在好了,如果我们将无穷大设为 0x3f3f3f3f,那么奇迹就发生了,0x3f3f3f3f 的每个字节都是0x3f!所 以要把一段内存全部置为无穷大,我们只需要memset(a,0x3f,sizeof(a))。所以在通常的场合下,0x3f3f3f3f 真的是一个非常棒的选择。

补充:memset以字节为单位进行填充,可以全部置为0,-1,和某个int类型的值四个字节都是一样的表示的数值,别问问就是巧合!!