Topic: Merge Sort Routine
Hi Jeff,
While in lock-down and to keep my mind active, I've written a sorting routine for real arrays. I'm posting the sort routine here. Although it's far from being the fastest sort routine, it's simple. I've sorted up to 65,000 random numbers with the routine with no problem. However, speed wise, it's more practical in the range of 3 to 4000 numbers (e.g., Random numbers =1034, t = 0.04 sec.; Random numbers =4108, t = 0.4 sec.). Like many routines, sometimes we learn that similar logic has been applied before and are not aware of it. The program is self explanatory and can be compiled as is and run with SimplyFortran.
I would welcome any constructive suggestions from forum members on how to improve the routine.
Thanks,
Frank
!-------------------------------------------------------------------
!----------------- Start of MergSort Routine -----------------------
!-------------------------------------------------------------------
MODULE nrtype
IMPLICIT NONE
!--- Listing valid kind parameters types
INTEGER, PARAMETER :: I1B = KIND(2)
INTEGER, PARAMETER :: I2B = KIND(4)
INTEGER, PARAMETER :: I4B = KIND(9)
INTEGER, PARAMETER :: SP = KIND(1.0E0)
INTEGER, PARAMETER :: DP = SELECTED_REAL_KIND(15, 200)
INTEGER, PARAMETER :: QP = SELECTED_REAL_KIND(2*PRECISION(1.0_DP))
INTEGER, PARAMETER :: LGT = KIND(.TRUE.)
END MODULE nrtype
!-------------------------------------------------------------------
Module MSort
USE nrtype
Implicit None
PRIVATE
PUBLIC :: MergSort
INTERFACE MergSort
MODULE PROCEDURE MergSort
END INTERFACE
!-------------------------------------------------------------------
CONTAINS
!-------------------------------------------------------------------
!---------------------------------------------------------------
FUNCTION MergSort( array ) RESULT( sorted )
!---------------------------------------------------------------
!--- MergSort version 1.0
!---
!--- Developed by F.W. Perrella, Ph.D. November 27, 2020
!--- Silver Spring, Maryland U.S.A.
!---------------------------------------------------------------
! NAME:
! MergSort
!
! PURPOSE:
! Program to sort a numeric array in ascending order.
!
! MODULE:
! nrType: Definition for kind variable types.
!---------------------------------------------------------------
!
! CALLING SEQUENCE:
! Result = MergSort( array )
!
! INPUT ARGUMENTS:
! array: Real array of numbers to sort.
! TYPE: REAL( KIND = Double )
!
! FUNCTION RESULT:
! TYPE: Real( double )
!
! RESULT: A real array of values sorted in ascending order.
!---------------------------------------------------------------
!
! BASIC MERGSORT ROUTINE:
! Function MergSort(array) Result(sorted)
! c = 123456789.0
! unsorted = array
! sorted = 0.0
! Do i = 1, n
! j = MINLOC(unsorted,1)
! vecMin = unsorted(j)
! unsorted = MERGE(unsorted,c,MASK=(unsorted /= vecMin))
! sorted(i) = vecMin
! End Do
! End Function
!---------------------------------------------------------------
use nrtype
implicit none
real(DP), dimension(:), intent(in) :: array
real(DP), dimension(1:size(array)) :: sorted
!--- Local orking arrays
real(DP), allocatable, dimension(:) :: unsorted
real(DP), allocatable, dimension(:) :: c
!--- Local variables
real(DP) :: vecMin
integer(I4B) :: i, j, n
!--- Number of values to sort
n = SIZE( array )
!--- Array too small to be sorted
if ( n == 1 ) then
sorted = array
return
endif
!--- Allocate working arrays
allocate( unsorted(n), c(n) )
!--- Large number array place holder
c = 123456789.0
!--- Begin sorting array
unsorted = array
sorted = 0.0
do i = 1, n
!--- Find minimum value in array
j = MINLOC( unsorted, 1 )
vecMin = unsorted(j)
!--- Merge array eliminating minimum value
unsorted = MERGE( unsorted, c, MASK=(unsorted /= vecMin) )
!--- Save the minimum values in array
sorted(i) = vecMin
end do
!--- Deallocate working arrays
Deallocate( unsorted, c )
return
End Function MergSort
End Module MSort
!-------------------------------------------------------------------
PROGRAM Test_Sort
use nrtype
implicit none
character(1) :: a
call Test_MergSort()
WRITE(*,'(/1x,"Press return to exit",$)')
READ(*,'(a1)') a
End Program Test_Sort
!-------------------------------------------------------------------
Subroutine Test_MergSort()
use nrtype
use MSort
implicit none
real(SP) :: t1, t2, tsum
real(DP),allocatable :: array1(:)
integer(I4B) :: j, k, m, n
integer(I4B) :: left, right, isize
logical(LGT) :: bflag, btot
m = 10
n = 14
btot = .TRUE.
WRITE(*,'(/1x,"Testing MergSort")')
WRITE(*,'(1x,a)') REPEAT("-", 35)
do j = m, n
!--- 1,034 to 16,398 random numbers
isize = 2**j
isize = isize + j
allocate( array1(isize) )
tsum = 0.0
do k = 1, m
call random_number( array1 )
left = 1
right = isize
!--- Start timer
t1 = DeltaTime( .TRUE. )
!--- Sort in Ascending Order
array1 = MergSort( array1 )
!--- End timer
t2 = DeltaTime( .FALSE. )
tsum = tsum + (t2 - t1)
end do
WRITE(*,'(1x,"Random numbers =",1x,I0)') isize
WRITE(*,'(1x,"Average elapsed time =",1x,F6.4,1x,"sec")') tsum/m
!--- Check that the array was sorted correctly.
call check_sort( array1, left, right, bflag )
btot = (btot .eqv. bflag)
deallocate(array1)
end do
WRITE(*, &
'(1x,"All tests were sorted correctly:",1x,L1,"RUE")') btot
WRITE(*,'(1x,a)') REPEAT("-", 35)
return
!End Subroutine Test_MergSort
!-------------------------------------------------------------------
CONTAINS
!-------------------------------------------------------------------
!-------------------------------------------------------------------
Subroutine check_sort( array, left, right, bflag )
use nrtype
implicit none
integer(I4B), intent(in) :: left, right
real(DP), intent(inout) :: array(left:right)
logical(LGT), intent(out) :: bflag
integer(I4B) :: j
bflag = .true.
do j = 1, size(array)-1
if( array(j+1) < array(j) ) then
bflag = .false.
EXIT
end if
end do
if (bflag) then
print *, "Correctly sorted array"
else
print *, "Incorrectly sorted array"
end if
PRINT *
return
End Subroutine check_sort
!-------------------------------------------------------------------
FUNCTION DeltaTime(Start) RESULT(tm)
!-------------------------------------
!--- Routine to calculate run time
! print *, 'Starttime:', t0
! print *, 'Finishtime:', t1
! print *, 'Elapsed time=', t1 - t0
!-------------------------------------
IMPLICIT NONE
LOGICAL, INTENT(IN) :: Start
LOGICAL :: Begin
REAL, SAVE :: t0
REAL :: t1
REAL :: tm
REAL, SAVE :: tt
!--- Start timer
!--- Start = .TRUE.
Begin = Start
IF ( Begin ) THEN
call cpu_time ( t0 )
t1 = t0
Begin = .FALSE.
!--- Stop timer
ELSE !--- Start = .FALSE.
call cpu_time ( t1 )
END IF
!--- Calculate time in seconds
tt = tt + (t1 - t0)
!--- Return total time in seconds
tm = tt
END FUNCTION DeltaTime
!-------------------------------------------------------------------
End Subroutine Test_MergSort
!-------------------------------------------------------------------
!---------------- End of MergSort Routine --------------------------
!-------------------------------------------------------------------