这几天需要用到求一个数组的中值,普遍的做法是先排序,然后取中间那个值。
但是这样做,冗余度太高,我并不需要其他部分的顺序。
于是找到一个猜测法,基本思想是,先猜测一个值a,求大于a和小于a的数的个数。
如果小于a的数比较多,就减少猜测值。反之则增加猜测值。
上代码
Real , Intent(IN) :: x(n)
Real, Parameter :: AFac = 1.5, amp = 1.5
Integer :: np, nm, j, n
Real :: a, eps, ap, am, sum, sumx, xp, xm, dum
a = (x(1)+x(n))/2.0 !//猜测值
eps = abs(x(n)-x(1))
ap = Huge(ap)
am = -Huge(ap)
Do
sum = 0.
sumx = 0.
np = 0
nm = 0
xp = Huge(xp)
xm = -Huge(xm)
Do j = 1, n !//循环求大于猜测值和小于猜测值的个数
If (x(j)/=a) Then
If (x(j)>a) Then
np = np + 1
If (x(j)<xp) xp = x(j)
Else If (x(j)<a) Then
nm = nm + 1
If (x(j)>xm) xm = x(j)
End If
dum = 1./(eps+abs(x(j)-a))
sum = sum + dum
sumx = sumx + x(j)*dum
End If
End Do
If (np-nm>=2) Then !猜测值太低了
am = a
xmed = xp + max(0., sumx/sum-a)*amp
If (xmed>ap) xmed = (a+ap)/2
eps = AFac*abs(xmed-a)
if(eps<1.0e-6) exit !//精度不需要那么高,不猜了
a = xmed
Else If (nm-np>=2) Then !猜测值太高了
ap = a
xmed = xm + min(0., sumx/sum-a)*amp
If (xmed<am) xmed = (a+am)/2
eps = AFac*abs(xmed-a)
if(eps<1.0e-6) exit !//精度不需要那么高,不猜了
a = xmed
Else !猜测值合适
If (mod(n,2)==0) Then !//偶数个数求平均
If (np==nm) Then
xmed = (xp+xm)/2
Else If (np>nm) Then
xmed = (a+xp)/2
Else
xmed = (xm+a)/2
End If
Else !//奇数个数
If (np==nm) Then
xmed = a
Else If (np>nm) Then
xmed = xp
Else
xmed = xm
End If
End If
exit
End If
End Do
End Function median
采用中二猜测法,我的测试中可以提升2-3倍的效率。数组越大,越显著。