假设我有一个连续整数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),有更有效的方法吗?
答案 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
此解决方案的缺点是对向量outvec
和tempvec
的随机访问,这不能充分利用缓存和寄存器。有可能解决这个问题并大大减少时间,可能以额外的临时向量为代价。