⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 find_sub.f90

📁 The module LSQ is for unconstrained linear least-squares fitting. It is based upon Applied Statisti
💻 F90
📖 第 1 页 / 共 2 页
字号:
REAL (dp)  :: rss_last, ssq

IF (jmin == 0 .OR. last < 1 .OR. last-1 > max_size) RETURN

rss_last = rss(last)
IF (rss_last + smin > bound(last-1)) RETURN

list = vorder(1:last)
DO i = first, last-1
  vorder(i:last-1) = list(i+1:last)
  ssq = rss_last + ss(i)
  CALL report(last-1, ssq)
  vorder(i) = list(i)
END DO

RETURN
END SUBROUTINE exdrop1


SUBROUTINE forwrd(first, last, ier)

!     Forward selection from variables in positions FIRST .. LAST.
!     If FIRST > 1, variables in positions prior to this are forced in.
!     If LAST < ncol, variables in positions after this are forced out.
!     On exit, the array VORDER contains the numbers of the variables
!     in the order in which they were added.

INTEGER, INTENT(IN)  :: first, last
INTEGER, INTENT(OUT) :: ier

!     Local variables

INTEGER    :: pos, jmax
REAL (dp)  :: ss(last), smax

!     Check call arguments

ier = 0
IF (first >= ncol) ier = 1
IF (last <= 1) ier = ier + 2
IF (first < 1) ier = ier + 4
IF (last > ncol) ier = ier + 8
IF (ier /= 0) RETURN

!     For POS = FIRST .. max_size, call ADD1 to find best variable to put
!     into position POS.

DO pos = first, max_size
  CALL add1(pos, last, ss, smax, jmax, ier)
  IF (nbest > 0) CALL exadd1(pos, smax, jmax, ss, last)

!     Move the best variable to position POS.

  IF (jmax > pos) CALL vmove(jmax, pos, ier)
END DO

RETURN
END SUBROUTINE forwrd


SUBROUTINE report(nv, ssq)

!     Update record of the best NBEST subsets of NV variables, if
!     necessary, using SSQ.

INTEGER, INTENT(IN)    :: nv
REAL (dp), INTENT(IN)  :: ssq

!     Local variables

INTEGER    :: rank, pos1, j, list(nv)
REAL (dp)  :: under1 = 0.99999_dp, above1 = 1.00001_dp

!     If residual sum of squares (SSQ) for the new subset > the
!     appropriate bound, return.

IF(nv > max_size) RETURN
IF(ssq >= bound(nv)) RETURN
pos1 = (nv*(nv-1))/2 + 1

!     Find rank of the new subset

DO rank = 1, nbest
  IF(ssq < ress(nv,rank)*above1) THEN
    list = vorder(1:nv)
    CALL shell(list, nv)

!     Check list of variables if ssq is almost equal to ress(nv,rank) -
!     to avoid including the same subset twice.

    IF (ssq > ress(nv,rank)*under1) THEN
      IF (same_vars(list, lopt(pos1:,rank), nv)) RETURN
    END IF

!     Record the new subset, and move the others down one place.

    DO j = nbest-1, rank, -1
      ress(nv,j+1) = ress(nv,j)
      lopt(pos1:pos1+nv-1, j+1) = lopt(pos1:pos1+nv-1, j)
    END DO
    ress(nv,rank) = ssq
    lopt(pos1:pos1+nv-1, rank) = list(1:nv)
    bound(nv) = ress(nv,nbest)
    RETURN
  END IF
END DO

RETURN
END SUBROUTINE report



SUBROUTINE shell(l, n)

!      Perform a SHELL-sort on integer array L, sorting into increasing order.

!      Latest revision - 5 July 1995

INTEGER, INTENT(IN)     :: n
INTEGER, INTENT(IN OUT) :: l(:)

!     Local variables
INTEGER   :: start, finish, temp, new, i1, i2, incr, it

incr = n
DO
  incr = incr/3
  IF (incr == 2*(incr/2)) incr = incr + 1
  DO start = 1, incr
    finish = n

!      TEMP contains the element being compared; IT holds its current
!      location.   It is compared with the elements in locations
!      IT+INCR, IT+2.INCR, ... until a larger element is found.   All
!      smaller elements move INCR locations towards the start.   After
!      each time through the sequence, the FINISH is decreased by INCR
!      until FINISH <= INCR.

    20 i1 = start
    temp = l(i1)
    it = i1

!      I2 = location of element new to be compared with TEMP.
!      Test I2 <= FINISH.

    DO
      i2 = i1 + incr
      IF (i2 > finish) THEN
        IF (i1 > it) l(i1) = temp
        finish = finish - incr
        EXIT
      END IF
      new = l(i2)

!     If TEMP > NEW, move NEW to lower-numbered position.

      IF (temp > new) THEN
        l(i1) = new
        i1 = i2
        CYCLE
      END IF

!     TEMP <= NEW so do not swap.
!     Use NEW as the next TEMP.

      IF (i1 > it) l(i1) = temp
      i1 = i2
      temp = new
      it = i1

!     Repeat until FINISH <= INCR.
    END DO

    IF (finish > incr) GO TO 20
  END DO

!      Repeat until INCR = 1.

  IF (incr <= 1) RETURN
END DO

RETURN
END SUBROUTINE shell



FUNCTION same_vars(list1, list2, n) RESULT(same)

LOGICAL              :: same
INTEGER, INTENT(IN)  :: n, list1(:), list2(:)

same = ALL(list1(1:n) == list2(1:n))

RETURN
END FUNCTION same_vars



SUBROUTINE seq2(first, last, ier)

! Sequential replacement algorithm applied to the variables in positions
! FIRST, ..., LAST.   2 variables at a time are added or replaced.
! If FIRST > 1, variables in positions prior to this are forced in.
! If LAST < NP, variables in positions after this are left out.

INTEGER, INTENT(IN)  :: first, last
INTEGER, INTENT(OUT) :: ier

!     Local variables

INTEGER  :: nv, nsize

!     Check call arguments

ier = 0
IF (first >= ncol) ier = 1
IF (last <= 1) ier = ier + 2
IF (first < 1) ier = ier + 4
IF (last > ncol) ier = ier + 8
IF (ier /= 0 .OR. nbest <= 0) RETURN

nv = MIN(max_size, last-1)

!     Outer loop; SIZE = current size of subset being considered.

DO nsize = first+1, nv
  CALL replace2(first, last, nsize)
END DO

RETURN
END SUBROUTINE seq2



SUBROUTINE replace2(first, last, nsize)
! Replace 2 variables at a time from those in positions first, ..., nsize
! with 2 from positions nsize, .., last - if they reduce the RSS.

INTEGER, INTENT(IN)  :: first, last, nsize

! Local variables

INTEGER              :: ier, j1, j2, pos1, pos2, best(2), i, iwk(last)
REAL (dp)            :: smax, rssnew, rssmin, save_rss
REAL (dp), PARAMETER :: zero = 0.0_dp

10 best(1) = 0
best(2) = 0
rssmin = rss(nsize)

!     Two loops to place all pairs of variables in positions nsize-1 and nsize.
!     POS1 = destination for variable from position nsize.
!     POS2 = destination for variable from position nsize-1.

DO pos1 = first, nsize
  DO pos2 = pos1, nsize-1
    CALL add2(nsize-1, last, smax, j1, j2, ier)

    IF (j1+j2 > nsize + nsize - 1) THEN
      rssnew = MAX(rss(nsize-2) - smax, zero)
      IF (rssnew < rssmin) THEN
        best(1) = vorder(j1)
        best(2) = vorder(j2)
        iwk(1:nsize-2) = vorder(1:nsize-2)
        rssmin = rssnew
      END IF
    END IF

    CALL vmove(nsize-1, pos2, ier)
  END DO
  CALL vmove(nsize, pos1, ier)
  DO i = pos1, nsize
    CALL report(i, rss(i))
  END DO
END DO

!     If any replacement reduces the RSS, make the best one.

IF (best(1) + best(2) > 0) THEN
  iwk(nsize-1) = best(2)
  iwk(nsize) = best(1)
  save_rss = rss(nsize)
  CALL reordr(iwk, nsize, 1, ier)
  DO i = first, nsize
    CALL report(i, rss(i))
  END DO

!    The calculated value of rssmin above is only a rough approximation to
!    the real residual sum of squares, thiugh usually good enough.
!    The new value of rss(nsize) is more accurate.   It is used below
!    to avoid cycling when several subsets give the same RSS.

  IF (rss(nsize) < save_rss) GO TO 10
END IF

RETURN
END SUBROUTINE replace2



SUBROUTINE seqrep(first, last, ier)

!     Sequential replacement algorithm applied to the variables in
!     positions FIRST, ..., LAST.
!     If FIRST > 1, variables in positions prior to this are forced in.
!     If LAST < ncol, variables in positions after this are forced out.

INTEGER, INTENT(IN)  :: first, last
INTEGER, INTENT(OUT) :: ier

!     Local variables

INTEGER    :: nv, size, start, best, from, i, jmax, count, j
REAL (dp)  :: zero = 0.0_dp, ssred, ss(last), smax

!     Check call arguments

ier = 0
IF (first >= ncol) ier = 1
IF (last <= 1) ier = ier + 2
IF (first < 1) ier = ier + 4
IF (last > ncol) ier = ier + 8
IF (ier /= 0 .OR. nbest <= 0) RETURN

nv = MIN(max_size, last-1)

!     Outer loop; SIZE = current size of subset being considered.

DO size = first, nv
  count = 0
  start = first
  10 ssred = zero
  best = 0
  from = 0

!     Find the best variable from those in positions SIZE+1, ..., LAST
!     to replace the one in position SIZE.   Then rotate variables in
!     positions START, ..., SIZE.

  DO i = start, size
    CALL add1(size, last, ss, smax, jmax, ier)
    IF (jmax > size) THEN
      CALL exadd1(size, smax, jmax, ss, last)
      IF (smax > ssred) THEN
        ssred = smax
        best = jmax
        IF (i < size) THEN
          from = size + start - i - 1
        ELSE
          from = size
        END IF
      END IF
    END IF
    IF (i < size) CALL vmove(size, start, ier)
    DO j = start, size-1
      CALL report(j, rss(j))
    END DO
  END DO ! i = start, size

!     If any replacement reduces the RSS, make the best one.
!     Move variable from position FROM to SIZE.
!     Move variable from position BEST to FIRST.

  IF (best > size) THEN
    IF (from < size) CALL vmove(from, size, ier)
    CALL vmove(best, first, ier)
    DO j = first, best-1
      CALL report(j, rss(j))
    END DO
    count = 0
    start = first + 1
  ELSE
    count = count + 1
  END IF

!     Repeat until COUNT = SIZE - START + 1

  IF (count <= size - start) GO TO 10
END DO

RETURN
END SUBROUTINE seqrep


SUBROUTINE xhaust(first, last, ier)

!     Exhaustive search algorithm, using leaps and bounds, applied to
!     the variables in positions FIRST, ..., LAST.
!     If FIRST > 1, variables in positions prior to this are forced in.
!     If LAST < ncol, variables in positions after this are forced out.

INTEGER, INTENT(IN)  :: first, last
INTEGER, INTENT(OUT) :: ier

!     Local variables

INTEGER    :: row, i, jmax, ipt, newpos, iwk(max_size)
REAL (dp)  :: ss(last), smax, temp

!     Check call arguments

ier = 0
IF (first >= ncol) ier = 1
IF (last <= 1) ier = ier + 2
IF (first < 1) ier = ier + 4
IF (last > ncol) ier = ier + 8
IF (ier /= 0 .OR. nbest <= 0) RETURN

!     Record subsets contained in the initial ordering, including check
!     for variables which are linearly related to earlier variables.
!     This should be redundant if the user has first called SING and
!     init_subsets.

DO row = first, max_size
  IF (d(row) <= tol(row)) THEN
    ier = -999
    RETURN
  END IF
  CALL report(row, rss(row))
END DO

!     IWK(I) contains the upper limit for the I-th simulated DO-loop for
!     I = FIRST, ..., max_size-1.
!     IPT points to the current DO loop.

iwk(first:max_size) = last

!     Innermost loop.
!     Find best possible variable for position max_size from those in
!     positions max_size, .., IWK(max_size).

30 CALL add1(max_size, iwk(max_size), ss, smax, jmax, ier)
CALL exadd1(max_size, smax, jmax, ss, iwk(max_size))

!     Move to next lower numbered loop which has not been exhausted.

ipt = max_size - 1
40 IF (ipt >= iwk(ipt)) THEN
  ipt = ipt - 1
  IF (ipt >= first) GO TO 40
  RETURN
END IF

!     Lower variable from position IPT to position IWK(IPT).
!     Record any good new subsets found by the move.

newpos = iwk(ipt)
CALL vmove(ipt, newpos, ier)
DO i = ipt, MIN(max_size, newpos-1)
  CALL report(i, rss(i))
END DO

!     Reset all ends of loops for I >= IPT.

iwk(ipt:max_size) = newpos - 1

!     If residual sum of squares for all variables above position NEWPOS
!     is greater than BOUND(I), no better subsets of size I can be found
!     inside the current loop.

temp = rss(newpos-1)
DO i = ipt, max_size
  IF (temp > bound(i)) GO TO 80
END DO
IF (iwk(max_size) > max_size) GO TO 30
ipt = max_size - 1
GO TO 40

80 ipt = i - 1
IF (ipt < first) RETURN
GO TO 40

END SUBROUTINE xhaust



SUBROUTINE random_pick(first, last, npick)
! Pick npick variables at random from those in positions first, ..., last
! and move them to occupy positions starting from first.

INTEGER, INTENT(IN)  :: first, last, npick

! Local variables

INTEGER  :: first2, i, ilist(1:last), j, k, navail
REAL     :: r

navail = last + 1 - first
IF (npick >= navail .OR. npick <= 0) RETURN
DO i = first, last
  ilist(i) = vorder(i)
END DO

first2 = first
DO i = 1, npick
  CALL RANDOM_NUMBER(r)
  k = first2 + r*navail
  IF (k > first2) THEN
    j = ilist(first2)
    ilist(first2) = ilist(k)
    ilist(k) = j
  END IF
  first2 = first2 + 1
  navail = navail - 1
END DO

CALL reordr(ilist(first:), npick, first, i)

RETURN
END SUBROUTINE random_pick

END MODULE find_subsets

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -