以下是引用onkey在2006-3-27 16:12:06的发言:
我所想要的答案不是形式上用一些其它函数取代IF函数或比较判断,而是真正减少比较判断次数。你的答案中的每一次使用SIGN(x+0.1)或SIGN(y+0.1)只不过是为了取代IF(x>=0,1,-1)或IF(y>=0,1,-1)而已。我的答案是=IF(x<y,3*big-x-y,(y<0)*(7*big+x)+y),要用SIGN函数把两个比较判断去掉很容易,只是这样就不利于别人对公式的理解,或者不利于从文字上对算法进行描述。
感谢楼主对我所做答案的评价,对于楼主的见解,我也有一些自己的看法,下面分两个部分进行一下说明,也是对我的算法的一个说明:(其中的图片需要放大一点看,否则有些字显示不出来)
1,首先,我认为,减少if或是其他简单的判断语句并不是一个简单的函数替换的过程。减少判断,不仅仅意味着替换,还包含了一个综合归纳的过程。
打个比方,你的公式中的(y<0)*(7*big+x)+y),就是利用了(y<0)这个判断产生0,1的两种结果使得下面两个判断式:
y<0,则=7*big+x+y
y>=0,则=y
综合归纳为了一个式子:=(y<0)*(7*big+x)+y)
但是同样的,你可以将前面的3*big-x-y也用某种方式和后面的(7*big+x)*(0或1)+y综合归纳成为一个式子吗?要知道,归纳也是一种数学、哲学上的重要技能和技巧。
我们来仔细分析一下你的公式,其实是把一个八等分的圈子分成了三个类型:
a,单元格位置落在2、3、4、5的时候,公式为3*big-x-y
b,单元格位置落在6、7、8的时候,公式为7*big+x+y
c,单元格位置落在1的时候,公式为y
其实我们如果分析能力不是很强的话,可能把问题情况分得更多,比如2、3一种,4、5一种,6、7一种,8一种,1一种。但是楼主通过归纳和总结,最后总结出了上面的三种情况。
因为有三种情况所以用了两个判断,其中一个判断带嵌套。
我要告诉楼主的是,我的算法把你剩下的三种情况也归纳总结成为了一种,所以我没有用判断。
sign函数确实可以替代if的判断,同样得到-1,+1两个函数值,但在我的公式运用中这个函数其实不仅仅是取代if函数,更重要的是它是三角函数的一种演化(就17楼我的公式说的,前面几楼的确实只是取巧而已)。
下面就来用文字描述一下我的算法(17楼的公式),方便大家的理解。
2,刚才楼主选择的基点是3*big、7*big、还有一个其实就是0*big,我们在下面的图上可以看到,其实就是135度角上的3、6、9….和315度角上的7、14、28….以及那根0度线。而我选择的基点就是紫色的那四条线,即45度、135度、225度、315度四条线。我们各自这样的选择都有自己的考虑,我就不详细说明了。
下面简单说一下我的考虑,我的想法是把一圈回文看作一个圆圈,由x、y轴分成四个象限。整个回文表格就可以看成一个个同心圆,圆圈的半径就相当于那个big(x、y中较大的那个绝对值)。围绕这这个圆圈,我们发现单元格的值随着圆心角度的增大而有规律地增大,对于某一个半径的一个圆圈来说,它圈上的最大值只跟它的圆心被划分(注意是“划分”而不是“平分”或“等分”)的份数有关。这个份数就是8*big。于是,我就可以考虑,是不是可以构造这么一个函数,类似于一个计算圆弧长度的函数,单元格的值就等于他所在位置的半径去乘以它的圆心角度大小。这就把四个象限甚至八个区域里的多种情况统一成了一种情况,归纳成为单独的一个公式,这就是这个算法的中心思想。
当然,这里的圆心角度的大小并不是指它真正的角度值,因为圆和方毕竟不一样,同样八等分一个圆和一个正方形,相等的圆心角对应的圆弧长度是相等的而对应的正方形边长却不一样。看一下下面的图可以发现,其实我们均分的是正方形的周长,而正方形中心的角度却不是等分的。这里提的只是一个可以将方类比为圆的一个抽象概念。
但是从上图我们发现,至少在正负45度、正负135度这样的对角线上,圆和方很有可比性,在这里有我们想要的值:
当单元格位于刚才所说的那四个对角线的时候,函数值等于1、3、5、7以及1、3、5、7的倍数(倍数就是半径big)。1、3、5、7很容易让我们产生联想,大家都减4就成了-3,-1,+1,+3,这个就更眼熟了。大家知道45度、135度、225度、315度的sin三角函数值就是根号(1/2)、根号(1/2)、-根号(1/2)、-根号(1/2)。经过变换后可以成为1,1,-1,-1。那么可以通过几个三角函数的组合得到四个对角线分别为-3,-1,+1,+3的结果吗?当然可以,我们不考虑三角函数具体值的大小,只当它们的数值绝对值都为1的话,可以看到:
对应45度、135度、225度、315度的时候:
sin +1 +1 -1 -1
tan +1 -1 +1 -1
那么就有-2×sin-tan=-3,-1,+1,+3,再加上4就成了1、3、5、7了。这就是我选择四条对角线的为基点的原因所在。
要把类三角函数转化为我们需要的公式,因为我们不需要x、y的具体数值,只要他们返回+1或者-1就行了,所以我采用了sign函数。『现在可以发现,其实我用sign函数并不是替换if函数,而是替换"y/︱y︱"或是"x/︱x︱"这样的式子,因为除法有个除数不能为零的禁忌,所以用sign函数来替代比较好些。』 三角函数Sin=y/根号(x2+y2),其实就是取了y的符号,tan=y/x,其实就是取了(y的符号/x的符号)。所以这部分公式就是j=-2*SIGN(y)-SIGN(x)*SIGN(y) ,它的函数值在我们划分的四个象限里面就分别等于-3、-1、+1、+3了。(因为当x或y=0的时候,sign会返回0值,我们不需要这个0,所以我在实际公式的编写中这部分改成了j=-2*SIGN(y+0.1)-SIGN(x+0.1)*SIGN(y+0.1))
好,下面看一下不在对角线上的情况。当单元格不是刚好落在对角线上而是落在对角线的边上,那些函数值也有一个规律:它的值就是离它最近的那根对角线加上或减去一个补偿值,那个补偿值的就是︱x︱-︱y︱的绝对值。打个比方,比如座标(-3,2),离它最近的是135度角,半径是3,所以对应对角线上的值是9,它的补偿值是︱-3︱-︱2︱的绝对值就是1,9加上1就是这个单元格的值10。至于补偿值何时该加何时该减,我们现在来分析一下。
第一张图:就是补偿值在8个区域里的符号。
第二张图:做过这道题目初步分析的朋友应该对︱y︱-︱x︱在8个区域里的符号很熟悉了。
第三张图:把第二张图变到第一张图,只需再乘上一个类tan函数就行了。
类tan函数和上面的做法一样,就是sign(x)*sign(y),所以补偿值这部分的公式可以写成:fu=(ABS(y)-ABS(x))*SIGN(x)*SIGN(y)(在实际公式应用中,采用sign(x+0.1)*sign(y+0.1))
归纳而成的公式就是这样:=(j+4)*big+fu,其中j+4就是那个1、3、5、7的基准值,*big就是乘上半径,+fu就是加上补偿值了。这个公式也可以看作是个类似求圆弧长度的公式,j+4是4个基准角度,补偿值可以看作偏离45度角时的算法,因为方形不同于圆形的等分,角度增大和边长增大并非常规线性关系,所以才会有补偿值的算法。
这就是我的算法的阐述了,如果楼主或是大家能够看懂我写的这些东西的话,应该多少可以理解一些我下面的这句话了:
公式的优劣并不完全在于公式的长短或是使用判断的多少,而是在于含义结构的清晰、对问题分析归纳的彻底以及对现有函数尽可能简单的应用。
再次感谢大家能够看完我的文字。
[此贴子已经被作者于2006-3-28 21:17:48编辑过] |