Нужно ли в любом случае проверить, равно ли n строк в строке матрицы в fortran?

Попробуйте следующее:

SELECT h.year, h.id, h.rate 
FROM (SELECT h.year, h.id, h.rate, IF(@lastid = (@lastid:=h.id), @index:=@index+1, @index:=0) indx 
      FROM (SELECT h.year, h.id, h.rate 
            FROM h
            WHERE h.year BETWEEN 2000 AND 2009 AND id IN (SELECT rid FROM table2)
            GROUP BY id, h.year
            ORDER BY id, rate DESC
            ) h, (SELECT @lastid:='', @index:=0) AS a
    ) h 
WHERE h.indx <= 5;
-3
задан Jack Tapay 13 July 2018 в 20:13
поделиться

4 ответа

Мне было интересно, если бы появился быстрый просмотр fortran по всем строкам maxtrix и определить, равно ли n число терминов.

Насколько я понял ваш проблема, это то, что вы хотите:

  • функция с сигнатурой: (integer(:), integer) -> logical
  • эта функция получает 1-D массив line и проверяет, есть ли любое значение, которое появляется как минимум n раз в массиве
  • , функция не должна указывать , что или , сколько были этими значениями, их позиции или точное количество повторений

Существует много способов добиться этого. «Что является наиболее эффективным?» Это будет зависеть от конкретных условий ваших данных, системы, компилятора и т. д. Чтобы проиллюстрировать это, я вышел с тремя различными решениями. Конечно, все они дают правильный ответ. Вам рекомендуется проверить каждый из них (или любой другой, который вы придумали) с образцами ваших реальных данных.


Наивное решение # 1 - хорошие «полные петли»

Это алгоритм по умолчанию. Он перемещается line и сохраняет каждое значение в списке агрегатора packed, который имеет каждое отдельное значение, найденное до сих пор, и сколько раз они появлялись. В тот момент, когда любое значение достигает n повторений, fuction возвращает .true.. Если никакие значения не достигли n повторений, и больше нет возможности завершить предикат, он возвращает .false..

Я говорю defalut , потому что это минимальный linear (который я выяснил) на основе хороших петлей do. Это, вероятно, будет лучшим для общего случая, если у вас есть нулевая информация о характере данных, системе или даже специфике языка программирования. Агрегатор должен завершить функцию сразу после выполнения условия, но за счет дополнительного переполнения списка (по его длине). Если в данных много разных значений, а n велико, агрегатор становится слишком длинным, и поиск может стать дорогостоящей операцией. Кроме того, почти нет места для параллелизма, векторизации и других оптимизаций.

! generic approach, with loops and aggregator
pure logical function has_at_least_n_repeated(line, n)
  integer, intent(in) :: line(:), n
  integer :: i, j, max_repetitions, qty_distincts
  ! packed(1,:) -> the distinct integers found so far
  ! packed(2,:) -> number of repetitions of each distinct integer so far
  integer :: packed(2, size(line) - n + 2)

  if(n < 1 .or. size(line) == 0) then
    has_at_least_n_repeated = .false.
  else if(n == 1) then
    has_at_least_n_repeated = .true.
  else
    packed(:, 1) = [line(1), 1]
    qty_distincts = 1
    max_repetitions = 1
    i = 1
    ! iterate until there aren't enough elements left to reach n repetitions
    outer: do, while(i - max_repetitions <= size(line) - n)
      i = i + 1
      ! test for a match on packed
      do j = 1, qty_distincts
        if(packed(1, j) == line(i)) then
          packed(2, j) = packed(2, j) + 1
          if(packed(2, j) == n) then
            has_at_least_n_repeated = .true.
            return
          end if
          max_repetitions = max(max_repetitions, packed(2, j))
          cycle outer
        end if
      end do
      ! add to packed
      qty_distincts = qty_distincts + 1
      packed(:, qty_distincts) = [line(i), 1]
    end do outer
    has_at_least_n_repeated = .false.
  end if
end

Наивное решение # 2 - попытка некоторой векторизации

Этот подход пытается воспользоваться из arraysh -настройки Fortran и быстрых реализаций внутренних функций. Вместо внутреннего цикла do существует вызов встроенного count с аргументом массива, позволяющий компилятору выполнять некоторую векторию. Кроме того, если вы используете какой-либо инструмент для параллелизма или знаете, как работать с coarrays (и поддержкой вашего компилятора), вы можете использовать этот подход для их реализации.

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

! alternative approach, intrinsic functions without cache
pure logical function has_at_least_n_repeated(line, n)
  integer, intent(in) :: line(:), n
  Integer :: i

  if(n < 1 .or. size(line) == 0) then
    has_at_least_n_repeated = .false.
  else if(n == 1) then
    has_at_least_n_repeated = .true.
  else
    ! iterate until there aren't enough elements left to reach n repetitions
    do i = 1, size(line) - n + 1
      if(count(line(i + 1:) == line(i)) + 1 >= n) then
        has_at_least_n_repeated = .true.
        return
      end if
    end do
    has_at_least_n_repeated = .false.
  end if
end

Наивное решение # 3 - функциональный стиль

Это мой любимый (личные критерии). Мне нравятся функциональные языки, и мне нравится заимствовать некоторые аспекты этого в императивные языки. Этот подход делегирует вычисление внутренней вспомогательной рекурсивной функции. Здесь нет do петель. В каждом вызове функции только часть файла line передается как аргумент: более короткий массив, который пока не проверен. Нет необходимости в кэше.

Честно говоря, поддержка Fortran для рекурсии далеко не велика - нет рекурсии хвостов, компиляторы обычно реализуют ограничение на низкий уровень вызовов, и многие автоматические оптимизации предотвращаются рекурсией , Несмотря на то, что алгоритм умный, мне нравится, как он выглядит, и я бы не отказался от него, прежде чем выполнять некоторые тесты и сравнения.

Примечание: Fortran не разрешает вложенные процедуры в contains части основная программа. Чтобы он работал как представленный, вам нужно будет поместить функцию в модуль, подмодуль или сделать его внешней функцией. Другой вариант - извлечь вложенную функцию и сделать ее нормальной функцией в той же области.

! functional approach, auxiliar recursive function and no loops
pure logical function has_at_least_n_repeated(line, n)
  integer, intent(in) :: line(:), n

  if(n < 1 .or. size(line) == 0) then
    has_at_least_n_repeated = .false.
  else if(n == 1) then
    has_at_least_n_repeated = .true.
  else
    has_at_least_n_repeated = aux(line)
  end if

contains
  ! on each iteration removes all entries of an element from array
  pure recursive function aux(section) result(out)
    integer, intent(in) :: section(:)
    logical :: out, mask(size(section))
    integer :: left
    mask = section /= section(1)
    left = count(mask)
    if(size(section) - left >= n) then
      out = .true.
    else if(n > left) then
      out = .false.
    else
      out = aux(pack(section, mask))
    end if
  end
end

Заключение

Проведите тесты перед выбором пути следования! Я говорил здесь о моем личном чувстве по каждому подходу и его последствиям, но было бы очень приятно, если бы некоторые из Fortran Gurus на этом сайте присоединились к обсуждению и предоставили точную информацию критику.

2
ответ дан Rodrigo Rodrigues 17 August 2018 в 12:10
поделиться
  • 1
    Что это за "abs (M [i] [k] - M [i] [j]) & lt; 1e-12 & Quot; ? Две проблемы: 1) это не Fortran и 2) 1e-12 нуждается в дополнительном объяснении: при сравнении реальных чисел вам нужно определить, какую допустимость допуска вы допустите. Это будет зависеть от значений в матрице (например, типичного значения) и точности, которую хранят эти числа. 1E-12 может быть разумным, если значения, как правило, 1000. и цифры хранятся в 8-байтовом формате. Допустимая ошибка может быть связана со средним значением (abs (значения)) * epsilon (M) * 100, где 100 отражает изменчивость значений в рассматриваемой матрице (или строках). – johncampbell 14 July 2018 в 05:59
  • 2
    OP никогда не запрашивал реальные цифры. Кроме того, представленная ссылка показывает пример данных с целыми числами. – Rodrigo Rodrigues 14 July 2018 в 06:54
  • 3
    Хорошо, я не обратил на это внимания. Я переключился на целые числа в примере и зафиксировал синтаксис – Kai Guther 14 July 2018 в 12:50
  • 4
    @KaiGuther Я смог принять это в соответствии с задачей, которую мне нужно было сделать, спасибо за вашу помощь. – Jack Tapay 16 July 2018 в 22:50
  • 5
    Фортран нечувствителен к регистру, поэтому n и N не могут быть разными объектами – Rodrigo Rodrigues 16 July 2018 в 23:05
2
ответ дан Rodrigo Rodrigues 6 September 2018 в 08:57
поделиться

Вот прагматическая альтернатива решениям @RodrigoRodrigues уже предоставлена. В отсутствие каких-либо хороших доказательств (вопрос серьезно не указан), что мы должны быть обеспокоены асимптотической сложностью и всеми этими хорошими вещами, вот простая простая функция, которая потребовала около 5 минут для разработки, кодирования и тестирования.

Эта функция принимает массив целых чисел ранга-1 и возвращает обратно массив ранга-1 целых чисел, каждый элемент соответствует счету этого элемента во входном массиве. Если это описание смущает вас, несите меня и читайте код, который довольно прост:

FUNCTION get_counts(arr) RESULT(rslt)
  INTEGER, DIMENSION(:), INTENT(in) :: arr
  INTEGER, DIMENSION(SIZE(arr)) :: rslt
  INTEGER :: ix
  DO ix = 1, SIZE(arr)
     rslt(ix) = COUNT(arr(ix)==arr)
  END DO
END FUNCTION get_counts

Для входного массива [1,1,2,3,4,1,5] он возвращает [3,3,1,1,1,3,1]. Если OP хочет использовать это в качестве основы для функции, чтобы увидеть, есть ли какое-либо значение, которое происходит n раз, тогда OP мог написать

any(get_counts(rank_1_integer_array)==n)

Если OP обеспокоен тем, какие элементы присутствуют n, тогда довольно просто использовать результат get_counts для возврата к исходному массиву для извлечения этого элемента.

Это решение является прагматическим в том смысле, что оно скупое с временем my , а не со временем компьютера. Мое решение несколько расточительно для пространства, что может быть проблемой для очень больших входных массивов. Любой из решений Родриго может превзойти мои, как во времени, так и в пространстве, в пределе.

1
ответ дан High Performance Mark 17 August 2018 в 12:10
поделиться
  • 1
    Если я сделаю вашу функцию elemental и вложил ее в функцию, объявленную как те, которые я представил, с телом, содержащим только has_at_least_n_repeated = n > 0 .and. any(get_counts(line) >= n), который может быть довольно выигрышным в параллельном сценарии. – Rodrigo Rodrigues 16 July 2018 в 18:00

Я поставил вопрос, чтобы определить, должно ли быть определено какое-либо значение в строке, по крайней мере, n раза. Чтобы понять это, я решил отсортировать копию каждой строки с помощью qsort из стандартной библиотеки C, а затем легко найти длины каждого запуска значений.

module sortrow
   use ISO_C_BINDING
   implicit none
   interface
      subroutine qsort(base, num, size, compar) bind(C,name='qsort')
         import
         implicit none
         integer base(*)
         integer(C_SIZE_T), value :: num, size
         procedure(icompar) compar
      end subroutine qsort
   end interface
   contains
      function icompar(p1, p2) bind(C)
         integer(C_INT) icompar
         integer p1, p2
         select case(p1-p2)
            case(:-1)
               icompar = -1
            case(0)
               icompar = 0
            case(1:)
               icompar = 1
         end select
      end function icompar
end module sortrow

program main
   use sortrow
   implicit none
   integer, parameter :: M = 3, N = 10
   integer i, j
   integer array(M,N)
   real harvest
   integer, allocatable :: row(:)
   integer current, maxMatch

   call random_seed
   do i = 1, M
      do j = 1, N
         call random_number(harvest)
         array(i,j) = harvest*3
      end do
   end do
   do i = 1, M
      row = array(i,:)
      call qsort(row, int(N,C_SIZE_T), C_SIZEOF(array(1,1)), icompar)
      maxMatch = 0
      current = 1
      do j = 2, N
         if(row(j) == row(j-1)) then
            current = current+1
         else
            current = 1
         end if
         maxMatch = max(maxMatch,current)
      end do
      write(*,'(*(g0:1x))') array(i,:),'maxMatch =',maxMatch
   end do
end program main

Пример прогона:

0 0 0 2 0 2 1 1 1 0 maxMatch = 5
2 1 2 1 0 1 2 1 2 0 maxMatch = 4
0 0 2 2 2 2 2 0 1 1 maxMatch = 5
0
ответ дан user5713492 17 August 2018 в 12:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: