用于迭代排序矢量

时间:2016-01-03 19:36:16

标签: fortran ranking

假设我有一个连续整数1:n的加扰向量,比如{3,6,2,1,4,5}。我的问题是为每个元素找到其左边小于自身的元素数。所以我希望程序为这个例子返回{0,1,0,0,3,4}。这就是我在Fortran中写的:

subroutine iterrank(n,invec,outvec,tempvec)
    implicit none

    integer :: n, i, currank
    integer, dimension(n) :: invec, outvec, tempvec

    tempvec = 0
    outvec = 0
    do i = 1,n
        currank = invec(i)
        outvec(i) = tempvec(currank)
        tempvec(currank:n) = tempvec(currank:n) + 1
    end do

    return
end subroutine

它需要一个临时数组(向量),并且对于每个数字 d ,循环会遇到,它会在临时向量中的位置 d 之外的每个元素上加1。然后,下一次迭代将临时向量中的适当元素作为小于其自身的元素的计数。我的问题是:

1)我认为这是复杂度O(n ^ 2),因为在循环的每次迭代中都有O(n)次写入临时向量。我是对的吗?

2)对于大n(例如,> 100k),有更有效的方法吗?

2 个答案:

答案 0 :(得分:3)

我相信这会更有效率,你也可以将临时整数数组减少到一个字节。

subroutine iterrank(n,invec,outvec,tempvec)
    implicit none

    integer :: n, i, currank
    integer, dimension(n) :: invec, outvec, tempvec

    tempvec = 0
    !outvec = 0 ! no need to initialize something overwritten below
    do i = 1 , n
        currank = invec(i)
        outvec(i) = sum( tempvec(1:currank) )
        tempvec(currank) = 1
    end do

end subroutine

增益是你每个索引只写两次,但是你读的元素最多只有n*n次。

编辑:

我没有试过这个,但它应该做更少的读取,可能有分支的开销。对于超大型阵列来说可能会更快,但我希望它对于短阵列来说会更慢:

subroutine iterrank(n,invec,outvec,tempvec)
  implicit none

  integer :: n, i, currank, prevrank
  integer, dimension(n) :: invec, outvec, tempvec

  tempvec = 0
  outvec(1) = 0
  tempvec(invec(1)) = 1
  do i = 2 , n
     prevrank = invec(i-1)
     currank = invec(i)
     if ( abs(prevrank-currank) > currank ) then
        outvec(i) = sum( tempvec(1:currank) )
     else if ( prevrank < currank ) then
        outvec(i) = outvec(i-1) + sum( tempvec(prevrank:currank) )
     else
        outvec(i) = outvec(i-1) - sum( tempvec(currank:prevrank-1) )
     end if
     tempvec(currank) = 1
  end do

end subroutine iterrank

答案 1 :(得分:2)

完全重写答案。如果内存不是问题,您可以添加另一个向量并使用类似下面的算法。附加矢量用于计算置换。由于原始向量是整数1到n的置换的事实,置换在O(n)中计算。在我的计算机上使用大小为100k的向量,该算法平均运行1.9秒(100次运行),并且第0次的初始命题平均为2.8秒。我建议这个解决方案只是因为zeroth说他没有测试他的新解决方案,你将测试并使用最好的解决方案。

subroutine iterrank(n,invec,outvec,tempvec,ord)
    implicit none
    !
    integer :: n, i, currPos, prevPos, currOut, prevOut
    integer, dimension(n) :: invec, outvec, tempvec,ord
    !
    tempvec = 0
    do i = 1, n
        ord(invec(i)) = i
    end do
    !
    currPos = ord(1)
    tempvec(currPos) = 1
    currOut = 0
    outvec(currPos) = currOut
    ! last = 0
    do i = 2 , n
        prevPos = currPos
        currPos = ord(i)
        !
        if(currPos>prevPos)then
            currOut = currOut+sum( tempvec(prevPos:currPos) )
        else
            currOut = sum( tempvec(1:currPos) )
        end if
        !
        outvec(currPos) = currOut
        tempvec(currPos) = 1
    end do
    !
end subroutine iterrank

此解决方案的缺点是对向量outvectempvec的随机访问,这不能充分利用缓存和寄存器。有可能解决这个问题并大大减少时间,可能以额外的临时向量为代价。