Reputation: 1
For example, if I have the following 2D array:
2 1 4
1 2 3
2 1 2
and want to sort by each row, how I can I use the column values to have ascending order? In this case I am able to sort the first column in the array, getting
1 2 3
2 1 4
2 1 2
With this example, my final result should be this:
1 2 3
2 1 2
2 1 4
First I want to look at the first column, then sort rows. Since two rows begin with 2, I want to look at the second column, and sort. If those are still the same number, I want to look at the next column and so on. How should I do this?
Upvotes: 0
Views: 1855
Reputation: 974
I tried the lazy approach of writing an interface to the standard C library subroutine qsort
. To avoid global data, I made the comparison function an internal function to the subroutine that invokes qsort
and holds the array to be sorted. The intermediate subroutine allocates an array of indices that qsort
actually sorts and then the index array is used to straighten of the input array. Here's how it works:
module sortmod
use ISO_C_BINDING
implicit none
interface
subroutine qsort(base,nitems,size,compar) bind(C,name='qsort')
import
implicit none
type(C_PTR), value :: base
integer(C_SIZE_T), value :: nitems
integer(C_SIZE_T), value :: size
interface
function compar(x,y) bind(C)
import
implicit none
integer(C_INT) compar
type(C_PTR),value :: x
type(C_PTR),value :: y
end function compar
end interface
end subroutine qsort
end interface
contains
recursive subroutine startsort(array)
integer(C_INT) array(:,:)
integer(C_INT), allocatable, target :: indices(:)
integer i
indices = [(i,i=1,size(array,1))]
call qsort(C_LOC(indices),size(indices,1,kind=C_SIZE_T),C_SIZEOF(indices(1)),callback)
array = array(indices,:)
contains
function callback(x,y) bind(C)
integer(C_INT) callback
type(C_PTR), value :: x, y
integer(C_INT), pointer :: ix,iy
integer j
call C_F_POINTER(x,ix)
call C_F_POINTER(y,iy)
callback = 0
do j = 1, size(array,2)
callback = array(ix,j) - array(iy,j)
if(callback /= 0) return
end do
end function callback
end subroutine startsort
end module sortmod
program testsort
use sortmod
implicit none
integer(C_INT), allocatable :: array(:,:)
character(20) fmt
array = reshape([2, 1, 4, &
1, 2, 3, &
2, 1, 2], &
[3, 3], order = [2, 1])
call startsort(array)
write(fmt,'(*(g0))') '(',size(array,2),'i3)'
write(*,fmt) transpose(array)
end program testsort
Output with gfortran:
1 2 3
2 1 2
2 1 4
Upvotes: 0
Reputation: 8140
What you need are two different procedures:
One that compares two rows with each other and decides which of these should go earlier, and another that actually does the sorting.
Here is a version using a poorly implemented bubble sort:
program sort
implicit none
integer, parameter :: num_rows = 3
integer, parameter :: num_cols = 3
character(len=*), parameter :: fmt = '(3I4)'
integer :: a(num_cols,num_rows)
a = reshape([2, 1, 4, 1, 2, 3, 2, 1, 2], [3, 3])
call sortrow(a)
print fmt, a
contains
subroutine sortrow(a)
implicit none
integer, intent(inout) :: a(num_cols, num_rows)
integer :: i, j
integer :: tmp(num_cols)
do i = 1, num_rows
do j = i+1, num_rows
if (islarger(a(:,i), a(:,j))) then
tmp(:) = a(:, i)
a(:, i) = a(:, j)
a(:, j) = tmp(:)
end if
end do
end do
end subroutine sortrow
function islarger(a, b)
implicit none
integer, intent(in) :: a(num_cols), b(num_cols)
logical :: islarger
integer :: i
do i = 1, num_cols
if (a(i) > b(i)) then
islarger = .TRUE.
return
end if
if (b(i) > a(i)) then
islarger = .FALSE.
return
end if
end do
islarger = .FALSE.
return
end function islarger
end program sort
Alternatively you could write a function that maps a row onto a single integer value in such a way that if row n has to come after m, then this value of n is larger than that of m.
For example, if all values single digits (0 through 9) then you could convert [2, 1, 4]
to 214
which would be much easier to sort.
Upvotes: 1