随机排列和随机种子
Random permutation and random seed
我正在使用 Knuth 算法生成随机排列
一个n元组。这是代码。固定 n,它生成随机排列并收集所有不同的排列,直到找到所有 n!排列。最后,它还会打印找到所有排列所需的试验次数。我还从那时起插入了种子的初始化(尽管以一种非常简单和幼稚的方式)。有两个选项(A 和 B)。 A:种子在主程序中是一次性固定的。 B:每次计算随机排列时种子都是固定的(下面第二个选项有注释)。
implicit none
integer :: n,ncomb
integer :: i,h,k,x
integer, allocatable :: list(:),collect(:,:)
logical :: found
integer :: trials
!
! A
!
integer :: z,values(1:8)
integer, dimension(:), allocatable :: seed
call date_and_time(values=values)
call random_seed(size=z)
allocate(seed(1:z))
seed(:) = values(8)
call random_seed(put=seed)
n=4
ncomb=product((/(i,i=1,n)/))
allocate(list(n))
allocate(collect(n,ncomb))
trials=0
h=0
do
trials=trials+1
list=Shuffle(n)
found=.false.
do k=1,h
x=sum(abs(list-collect(:,k)))
if ( x == 0 ) then
found=.true.
exit
end if
end do
if ( .not. found ) then
h=h+1
collect(:,h)=list
print*,h,')',collect(:,h)
end if
if ( h == ncomb ) exit
end do
write(*,*) "Trials= ",trials
contains
function Shuffle(n) result(list)
integer, allocatable :: list(:)
integer, intent(in) :: n
integer :: i, randpos, temp,h
real :: r
!
! B
!
! integer :: z,values(1:8)
! integer, dimension(:), allocatable :: seed
! call date_and_time(values=values)
! call random_seed(size=z)
! allocate(seed(1:z))
! seed(:) = values(8)
! call random_seed(put=seed)
allocate(list(n))
list = (/ (h, h=1,n) /)
do i = n, 2, -1
call random_number(r)
randpos = int(r * i) + 1
temp = list(randpos)
list(randpos) = list(i)
list(i) = temp
end do
end function Shuffle
end
你可以检查第二个选项一点也不好。对于 n=4,它需要大约 100 倍的试验才能获得排列总数,对于 n=5,它会卡住。
我的问题是:
为什么多次调用random_seed会给出错误的结果?我引入了什么样的系统错误?这不就相当于只调用一次随机种子,而是多次启动代码(每次只生成一个随机排列)吗?
如果我想多次启动代码,计算一个排列,我猜如果我初始化随机种子我有同样的问题(不管初始化的位置,因为现在我我只计算一个排列)。正确的?在这种情况下,为了在不破坏均匀采样的情况下初始化种子,我必须做什么?因为如果我不以随机方式初始化种子,我将获得相同的排列。我想我可以在每次启动代码时打印和读取种子,以免从相同的伪随机数开始。但是,如果我并行启动代码的多个实例,这会很复杂。
更新
回复我明白了。总之,如果我想通过初始化种子在每次调用时生成伪随机数,我可以做的是:
A) 旧 gfortran
这里使用子程序init_random_seed()
https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gfortran/RANDOM_005fSEED.html
B) 最新的 gfortran 版本
呼叫random_seed()
C) Fortran2018
调用 random_init(可重复,image_distinct)
问题
在C)的情况下,我应该设置repeatable=.false., image_distinct=.true.
每次都有不同的随机数?
以可移植的方式编写代码的有效方法是什么,以便
无论编译器是什么,它都能工作吗? (我的意思是,代码识别可用的内容并相应地工作)
您当然不应该重复调用 random_seed()
。它应该只被调用一次。你把问题设置得这么粗暴,只会让问题变得更糟。
是的,人们确实经常使用数据和时间来初始化它,但是我们必须通过添加一些熵的东西来预处理时间数据,比如通过一些非常简单的随机生成器。在旧版本 gfortran 的 RANDOM_SEED
文档中可以找到一个很好的例子:https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gfortran/RANDOM_005fSEED.html 查看那里如何使用 lcg()
来转换 data_and_time()
数据。
请注意,最新版本的 gfortran 将生成一个随机种子,每次只需调用 random_seed()
而不带任何参数即可生成不同的种子。旧版本每次都返回相同的种子。
另请注意,Fortran 2018 具有 random_init()
,您可以在其中将 repeatable=
指定为 true 或 false。使用 false 每次都会得到不同的序列。
可移植的东西就是使用标准的Fortran,仅此而已。但是您不能同时使用新功能和旧编译器版本。这种便携性是不存在的。对于旧的编译器,您只能使用旧的标准功能。我什至不会开始写关于 autoconf 之类的东西,这不值得。
所以,
- 您可以将随机数种子设置为每次相同或每次不同(见上文),
和
- 你应该总是调用
random_seed
或 random_init
只调用一次.
为什么多次调用random_seed会得到错误的结果?
您正在将伪随机序列重新启动到某个未指定的状态,可能是熵不足。很容易很接近上次的起始状态。
我引入了什么样的系统错误?这不就相当于只调用一次随机种子,而是多次启动代码(每次只生成一个随机排列)吗?
可能是相似的。但是你使用时间播种太天真了,当 运行 在循环中时,日期和时间即使在大多数位上不完全相等也太相似了。上面链接的一些转换可能会掩盖这个问题,但是将日期和时间本身作为你的种子是行不通的。
我正在使用 Knuth 算法生成随机排列 一个n元组。这是代码。固定 n,它生成随机排列并收集所有不同的排列,直到找到所有 n!排列。最后,它还会打印找到所有排列所需的试验次数。我还从那时起插入了种子的初始化(尽管以一种非常简单和幼稚的方式)。有两个选项(A 和 B)。 A:种子在主程序中是一次性固定的。 B:每次计算随机排列时种子都是固定的(下面第二个选项有注释)。
implicit none
integer :: n,ncomb
integer :: i,h,k,x
integer, allocatable :: list(:),collect(:,:)
logical :: found
integer :: trials
!
! A
!
integer :: z,values(1:8)
integer, dimension(:), allocatable :: seed
call date_and_time(values=values)
call random_seed(size=z)
allocate(seed(1:z))
seed(:) = values(8)
call random_seed(put=seed)
n=4
ncomb=product((/(i,i=1,n)/))
allocate(list(n))
allocate(collect(n,ncomb))
trials=0
h=0
do
trials=trials+1
list=Shuffle(n)
found=.false.
do k=1,h
x=sum(abs(list-collect(:,k)))
if ( x == 0 ) then
found=.true.
exit
end if
end do
if ( .not. found ) then
h=h+1
collect(:,h)=list
print*,h,')',collect(:,h)
end if
if ( h == ncomb ) exit
end do
write(*,*) "Trials= ",trials
contains
function Shuffle(n) result(list)
integer, allocatable :: list(:)
integer, intent(in) :: n
integer :: i, randpos, temp,h
real :: r
!
! B
!
! integer :: z,values(1:8)
! integer, dimension(:), allocatable :: seed
! call date_and_time(values=values)
! call random_seed(size=z)
! allocate(seed(1:z))
! seed(:) = values(8)
! call random_seed(put=seed)
allocate(list(n))
list = (/ (h, h=1,n) /)
do i = n, 2, -1
call random_number(r)
randpos = int(r * i) + 1
temp = list(randpos)
list(randpos) = list(i)
list(i) = temp
end do
end function Shuffle
end
你可以检查第二个选项一点也不好。对于 n=4,它需要大约 100 倍的试验才能获得排列总数,对于 n=5,它会卡住。
我的问题是:
为什么多次调用random_seed会给出错误的结果?我引入了什么样的系统错误?这不就相当于只调用一次随机种子,而是多次启动代码(每次只生成一个随机排列)吗?
如果我想多次启动代码,计算一个排列,我猜如果我初始化随机种子我有同样的问题(不管初始化的位置,因为现在我我只计算一个排列)。正确的?在这种情况下,为了在不破坏均匀采样的情况下初始化种子,我必须做什么?因为如果我不以随机方式初始化种子,我将获得相同的排列。我想我可以在每次启动代码时打印和读取种子,以免从相同的伪随机数开始。但是,如果我并行启动代码的多个实例,这会很复杂。
更新
回复我明白了。总之,如果我想通过初始化种子在每次调用时生成伪随机数,我可以做的是:
A) 旧 gfortran
这里使用子程序init_random_seed()
https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gfortran/RANDOM_005fSEED.html
B) 最新的 gfortran 版本
呼叫random_seed()
C) Fortran2018
调用 random_init(可重复,image_distinct)
问题
在C)的情况下,我应该设置repeatable=.false., image_distinct=.true. 每次都有不同的随机数?
以可移植的方式编写代码的有效方法是什么,以便 无论编译器是什么,它都能工作吗? (我的意思是,代码识别可用的内容并相应地工作)
您当然不应该重复调用 random_seed()
。它应该只被调用一次。你把问题设置得这么粗暴,只会让问题变得更糟。
是的,人们确实经常使用数据和时间来初始化它,但是我们必须通过添加一些熵的东西来预处理时间数据,比如通过一些非常简单的随机生成器。在旧版本 gfortran 的 RANDOM_SEED
文档中可以找到一个很好的例子:https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gfortran/RANDOM_005fSEED.html 查看那里如何使用 lcg()
来转换 data_and_time()
数据。
请注意,最新版本的 gfortran 将生成一个随机种子,每次只需调用 random_seed()
而不带任何参数即可生成不同的种子。旧版本每次都返回相同的种子。
另请注意,Fortran 2018 具有 random_init()
,您可以在其中将 repeatable=
指定为 true 或 false。使用 false 每次都会得到不同的序列。
可移植的东西就是使用标准的Fortran,仅此而已。但是您不能同时使用新功能和旧编译器版本。这种便携性是不存在的。对于旧的编译器,您只能使用旧的标准功能。我什至不会开始写关于 autoconf 之类的东西,这不值得。
所以,
- 您可以将随机数种子设置为每次相同或每次不同(见上文),
和
- 你应该总是调用
random_seed
或random_init
只调用一次.
为什么多次调用random_seed会得到错误的结果?
您正在将伪随机序列重新启动到某个未指定的状态,可能是熵不足。很容易很接近上次的起始状态。
我引入了什么样的系统错误?这不就相当于只调用一次随机种子,而是多次启动代码(每次只生成一个随机排列)吗?
可能是相似的。但是你使用时间播种太天真了,当 运行 在循环中时,日期和时间即使在大多数位上不完全相等也太相似了。上面链接的一些转换可能会掩盖这个问题,但是将日期和时间本身作为你的种子是行不通的。