2016-02-05 6 views
2

Давайте массив A (:, :)Изменение размера массива генерируется цикл

Real, Allocatable:: A(:,:), B(:) 
Integer, Allocatable:: rowin(:) 
Integer:: m,j,k, nl 

Allocate(A(max1,max2)) 

определяется несколькими петлями через т, J, K

nl = 0 

A(rowin(m)+j,k) = ... 

и теперь я хочу, чтобы выбрать подмножество а (:, :) таким образом, чтобы их значения отрицательны и хранить их в массив B (:) неизвестную длину еще, т.е.

if(A(rowin(m)+j,k).lt.0.d0) then 
    nl = nl + 1 
    B(nl) = A(rowin(m)+j,k)    
end if 

С max1 и max2 очень большие числа, Я не хочу выделять B (:) длины max1 * max2, но точно такой длины, что A (:, :) содержит отрицательные значения. Кроме того, я не хочу снова проходить сложные петли над m, j и k. Спасибо.

+0

Действительно ли вам нужен Fortran 90 или 95 или вы разрешаете Fortran 2003 (автоматическое перераспределение ответов используется с Fortran 2003 и очень полезно). –

+0

Возможно и то, и другое (желательно Fortran 90/95), но нужно иметь дело с циклами и индексами, как @roygvib представил свой ответ. – bosona

ответ

1

Если предварительно выделение B некоторого фиксированного размера не желательно, мы можем подражать динамический массив используя автоматическое перераспределение выделенного массива. В простейшем случае мы можем развернуть B, добавив новое значение. Например, предположим, что у нас есть двойные петли над m и j, выберите значения, которые соответствуют заданному условию, и добавьте значения tho к B. Здесь размер B растет с 0, 1, 2, ... автоматически.

integer, allocatable :: B(:) 
integer :: m, j 

allocate(B(0)) !! start from an empty array 

do m = 1, 10000 
do j = 1, 10000 

    !! Some condition to add an element, e.g. 
    if (m == j .and. mod(m, 1000) == 0) then 

     !! Add a new element to B. 
     B = [ B, m ] 
    endif 
enddo 
enddo 

print *, "size(B) = ", size(B) 
print *, B(:) 

В результате становится

size(B) = 10 
1000 2000 3000 4000 5000 6000 7000 8000 9000 10000 

В вашем случае, вероятно, можно добавить нужные значения A аналогичным образом, например

allocate(B(0)) 

do m = 1, ... 
do j = 1, ... 
do k = 1, ... 
    if (A(rowin(m)+j, k) < 0.d0) then !! or any condition 
     B = [ B, A(rowin(m)+j,k) ] 
    end if 
enddo 
enddo 
enddo 

Недостатком этого подхода заключается в том, что он очень неэффективен, потому что B перераспределяется каждый раз, когда новый элемент добавляется. Хотя это не проблема для небольших B, это может стать серьезным узким местом для больших B. В этом случае можно увеличить размер B, а не увеличиваться на 1. Например, следующий код удваивает размер B по мере необходимости, чтобы уменьшить количество перераспределения.

integer, allocatable :: B(:) 
integer :: m, j, nl, p 

allocate(B(0)) !! start from an empty array 
nl = 0    !! number of elements found 

do m = 1, 10000 
do j = 1, 10000 

    if (m == j .and. mod(m, 1000) == 0) then 

     nl = nl + 1 

     !! Expand B if the size becomes insufficient. 
     if (size(B) < nl) B = [ B, (0, p=1,nl) ] 

     !! Add a new value. 
     B(nl) = m 
    endif 
enddo 
enddo 

B = B(1 : nl) !! adjust B to the optimal size 

еще несколько заметок:

  • Если подобный подход используется очень часто, удобно написать утилиту рутина, как resize(), который принимает размещаемый массив в качестве аргумента и изменяет его размер.Если эффективность действительно имеет значение, может быть лучше использовать allocate() и move_alloc() явно в resize() для устранения одного временного массива.
  • Приведенный выше код требует относительно новых компиляторов, который поддерживает автоматическое перераспределение распределяемых массивов в левой части.
  • Если вы используете Intel fortran, необходимо добавить опцию -assume realloc_lhs. Если размер B может стать довольно большим, вам также понадобится -heap-arrays. Для gforran или fortran Oracle не требуются специальные параметры.
  • Мы должны не установить двоеточие в левой части; то есть B(:) = [ B, m ] является NG
  • Подробнее о динамических массивах (например, скорости роста) см. на странице Wiki.
3

Это довольно просто:

b = pack(a,a<0.0) 

упакует отрицательные элементы a в ранге-1 массив b. С недавним компилятором b автоматически присваивается правильный размер во время назначения.

(последняя Fortran стандарт дает очень похожую операцию, как, например, в документации функции pack.)

Смежные вопросы