ITM Grid Service Library: Fortran 90
|
00001 module itm_combinations 00002 00003 use itm_assert 00004 00005 implicit none 00006 00007 contains 00008 00009 !> Service routines for counting and enumerating combinations of a vector 00010 !> (i_1, ..., i_n), with the range of every component i_j is 0 <= i_j <= d_j and 00011 !> for the sum of all components holds sum(i_j) = dsum for a given value of dsum. 00012 !> 00013 !> These routines are used to build lists of all possible object classes of a given 00014 !> dimension for a given grid. d_j is then the dimension of space j (stored in the vector dmax), 00015 !> and dsum the dimension of the grid objects under consideration. 00016 00017 !> Compute the number of possible combinations for the vector. 00018 integer recursive function count_combinations(dmax, dsum) result(ccount) 00019 integer, intent(in) :: dmax(:), dsum 00020 00021 ! internal 00022 integer :: iFirst, iRemaining 00023 00024 call assert(sum(dmax) >= dsum) 00025 00026 ! Extreme case: vector of length 1 00027 ! Only one combination (the value of dsum) 00028 if (size(dmax) == 1) then 00029 ccount = 1 00030 return 00031 end if 00032 00033 ! Vary the first component of the vector in the allowed range 00034 ! and sum up the possible combinations of the remaining part of the vector 00035 ccount = 0 00036 do iFirst = min(dmax(1), dsum), 0, -1 00037 iRemaining = dsum - iFirst 00038 if ( sum(dmax(2:)) < iRemaining ) exit 00039 ccount = ccount + count_combinations(dmax(2:), iRemaining) 00040 end do 00041 end function count_combinations 00042 00043 00044 !> Build a list of all possible combinations of the vector. 00045 recursive function enumerate_combinations(dmax, dsum, cCountTotal) result(comb) 00046 integer, intent(in) :: dmax(:), dsum, cCountTotal 00047 integer :: comb(cCountTotal, size(dmax)) 00048 00049 ! internal 00050 integer :: iFirst, cCount, cSubCount, iRemaining 00051 00052 ! Extreme case: vector of length 1 00053 ! Only one combination (the value of dsum) 00054 if (size(dmax) == 1) then 00055 comb(1,1) = dsum 00056 return 00057 end if 00058 00059 00060 ! Vary the first component of the vector in the allowed range 00061 ! and recursively assemble the possible combinations of the remaining part of the vector 00062 cCount = 0 00063 do iFirst = min(dmax(1), dsum), 0, -1 00064 iRemaining = dsum - iFirst 00065 if ( sum(dmax(2:)) < iRemaining ) exit 00066 cSubCount = count_combinations(dmax(2:), iRemaining) 00067 comb(cCount + 1 : cCount + cSubCount, 1) = iFirst 00068 comb(cCount + 1 : cCount + cSubCount, 2:) = enumerate_combinations(dmax(2:), iRemaining, cSubCount) 00069 cCount = cCount + cSubCount 00070 end do 00071 00072 call assert( cCount == cCountTotal ) 00073 end function enumerate_combinations 00074 00075 00076 !> Convenience routine: allocate and populate an array with all possible combinations. 00077 subroutine allocate_combinations(dmax, dsum, comb) 00078 integer, intent(in) :: dmax(:), dsum 00079 integer, allocatable :: comb(:, :) 00080 00081 ! internal 00082 integer :: ccount 00083 00084 ccount = count_combinations(dmax, dsum) 00085 allocate( comb(ccount, size(dmax)) ) 00086 comb = enumerate_combinations(dmax, dsum, ccount) 00087 00088 end subroutine allocate_combinations 00089 00090 end module itm_combinations