当代计算机的创始人约翰.冯.诺依曼曾经说过,任何试图以算法生成随机数据的人都将处于一种两难境地。所谓两难说是指随机性与算法确定性之间是不可能完全统一的。所以我们必须有意识地根据需要伪随机序列的随机性才能达到模拟真随机序列的效果。
线性同余伪随机数发生器
目前应用最为广泛的均匀分布伪随面数发生器就是线线同余伪随机发生器(LCG)
LCG的迭代描述公式为:
Xi+1=(a*Xi+c)(mod m) (1)
式(1)中有三个参数a,c,m。其中m是一个素数,a是m的一个原根,c属于[0,m),初值x为种子。我们将a,c,m分别叫做乘数、增量、模。
Microsoft Visual C/C++的随机函数rand()就是用的上面相似的算法
其a=214013即343FDh ,c=2531011即269EC3h m=2^32 即一个DWORD数据
那你可能会问,x的初值取什么呢?对于rand(),初值为1,即randseek=1,当然也可以通过一个函数srand(int x)将种子更改,这样我们得到的随机数序列就不一样了,通常使用时间戳(用rdtsc指令)
通过简单的写一个c函数,然后OD跟踪一下,就可以得到rand了
#include "stdlib.h"
int main(int argc, char* argv[])
{
int Array[5][5];
int i,j;
//srand(1);
for(i=0;i<=4;i++)
for(j=0;j<=4;j++)
Array[i][j] = rand();
for(i=0;i<=4;i++){ //测试输出
for(j=0;j<=4;j++)
printf("%d ",Array[i][j]);
printf("/n");
}
return 0;
}
//----------------------
跟踪rand得到:
mov eax,dword ptr ds:[40703c];种子赋给eax
imul eax,eax,343fd ;eax=a*x (有符号乘法)
add eax,269ec3 ;eax=a*x+c
mov dword ptr ds:[40703c],eax;将当前值(下一次的种子)写回
sar eax,10 ;算术右移16位(0~32767)
and eax,7fff ;取低15位(保证为正数),即得到了一个随机数
retn
;-----------------------------
下面给出一个完整的例子:
;************************************************************
;*--==--* Rand example
;*--==--* By G-Spider 2010.11.2
;*--==--* Web:http://blog.csdn.net/G_Spider
;*--==--* -------------------------------------------------------
;*--==--* ml /c /coff rand.asm
;*--==--* link /subsystem:console rand.obj
;*--==--* 请保持以上完整性!!
;************************************************************
.686p
.XMM
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
include kernel32.inc
include msvcrt.inc
includelib user32.lib
includelib kernel32.lib
includelib msvcrt.lib
.data
RandSeed dd 1 ;默认的种子
fmt0 db '%f',0dh,0ah,0
fmt1 db '%6d/32767=',0
.data?
lpA dd ?
lpB dd ?
lpC dd ?
lpFloatA dd ?
lpFloatB dd ?
lpFloatC dd ?
.code
;*************************************
;种子生成函数
;-------------------------------------
_srand proc seed
push seed
pop RandSeed
ret
_srand endp
;*************************************
;m为矩阵的行,n为矩阵的列,生成m*n个随机数
;返回数据首指针
;------------------------------------
rand proc m,n
local @lpRand
mov eax,m ;eax作乘数
mul n ;n*m,结果高位在edx中,低位在eax中
;-------------------------------------------------
push eax
shl eax,2
;-------------------------------------------------
;申请空间
invoke VirtualAlloc,NULL,eax,MEM_RESERVE /
or MEM_COMMIT,PAGE_READWRITE
.if eax
mov @lpRand,eax ;分配的指针是页对齐的
mov esi,eax
.else
xor eax,eax ;失败则返回NULL
ret
.endif
pop eax
Loop1:
mov ecx,RandSeed
imul ecx,ecx,343FDh
add ecx,269EC3h
mov RandSeed,ecx
sar ecx,10h
and ecx,7FFFh
mov [esi],ecx
add esi,4
dec eax
jnz Loop1
mov eax,@lpRand ;成功则返回0
ret
rand endp
;*************************************
;将rand生成的随机整数转成0~1之间的随机数
Int2FloatRand0_1 proc m,n,lpRand,flag
local @lpNewRand
mov esi,lpRand
mov eax,m ;eax作乘数
mul n ;n*m,结果高位在edx中,低位在eax中,总共eax=n*m个数
push eax ;保存数据个数在eax中
shl eax,2 ;满足四个数据对齐,粒度为4字节,16字节对齐,最好的情形
;-------------------------------------------------
;申请空间
invoke VirtualAlloc,NULL,eax,MEM_RESERVE or MEM_COMMIT,PAGE_READWRITE
.if eax
mov @lpNewRand,eax ;分配的指针是页对齐的
mov edi,eax
.else
xor eax,eax ;失败则返回NULL
ret
.endif
;**************************************************
;整形转浮点0~1
mov eax,38000100H ;1/7fffh=0.000030518509476
movd xmm0,eax
pslldq xmm0,12
movaps xmm1,xmm0
psrldq xmm1,4
orpd xmm0,xmm1
psrldq xmm1,4
orpd xmm0,xmm1
psrldq xmm1,4
orpd xmm0,xmm1
movaps xmm7,xmm0 ;xmm7=1/7fffh,1/7fffh,1/7fffh,1/7fffh
;整形转浮点
pop eax ;保存数据个数在eax中
mov ecx,eax
cmp eax,32
jl A002 ;整数个数(<32个)
test ecx,1Fh ;是不是32的整数倍?
jz A001
and ecx,-20h
and eax, 1fh ;剩余的整数(<32个)
;每128字节=32个数一次
A001:
cvtdq2ps xmm0,[esi]
cvtdq2ps xmm1,[esi+10h]
mulps xmm0,xmm7
mulps xmm1,xmm7
movaps [edi],xmm0 ;// Write to destination
movaps [edi+10h],xmm1
cvtdq2ps xmm2,[esi+20h]
cvtdq2ps xmm3,[esi+30h]
mulps xmm2,xmm7
mulps xmm3,xmm7
movaps [edi+20h],xmm2 ;// Write to destination
movaps [edi+30h],xmm3
cvtdq2ps xmm4,[esi+40h]
cvtdq2ps xmm5,[esi+50h]
mulps xmm4,xmm7
mulps xmm5,xmm7
movaps [edi+40h],xmm4 ;// Write to destination
movaps [edi+50h],xmm5
movaps xmm0,xmm7
cvtdq2ps xmm6,[esi+60h]
cvtdq2ps xmm7,[esi+70h]
mulps xmm6,xmm0
mulps xmm7,xmm0
movaps [edi+60h],xmm6 ;// Write to destination
movaps [edi+70h],xmm7
movaps xmm7,xmm0
add esi,128
add edi,128
sub ecx,32
jnz A001
;处理<32个整数(16-8-4-2-1)
A002: test eax,10h
jz A003 ;小于16个整数
sub eax,10h
cvtdq2ps xmm0,[esi]
cvtdq2ps xmm1,[esi+10h]
mulps xmm0,xmm7
mulps xmm1,xmm7
movaps [edi],xmm0 ;// Write to destination
movaps [edi+10h],xmm1
cvtdq2ps xmm2,[esi+20h]
cvtdq2ps xmm3,[esi+30h]
mulps xmm2,xmm7
mulps xmm3,xmm7
movaps [edi+20h],xmm2 ;// Write to destination
movaps [edi+30h],xmm3
add esi,64
add edi,64
A003: test eax,8
jz A004
sub eax,8
cvtdq2ps xmm0,[esi]
cvtdq2ps xmm1,[esi+10h]
mulps xmm0,xmm7
mulps xmm1,xmm7
movaps [edi],xmm0 ;// Write to destination
movaps [edi+10h],xmm1
add esi,32
add edi,32
A004: test eax,4
jz A005
sub eax,4
cvtdq2ps xmm0,[esi]
mulps xmm0,xmm7
movaps [edi],xmm0
add esi,16
add edi,16
A005: test eax,2
jz A006
sub eax,2
movlpd xmm1,QWORD PTR [esi]
cvtdq2ps xmm0,xmm1
mulps xmm0,xmm7
movlpd QWORD PTR [edi],xmm0
add esi,8
add edi,8
A006: test eax,1
jz A007
sub eax,1
movd xmm1,DWORD PTR [esi]
cvtdq2ps xmm0,xmm1
mulps xmm0,xmm7
movd DWORD PTR [edi],xmm0
add esi,4
add edi,4
A007:
mov eax,@lpNewRand
ret
Int2FloatRand0_1 endp
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
start:
invoke rand,5,5 ;默认的种子为1,生成5*5=25个随机数
.if eax
mov lpA,eax
.else
invoke MessageBox,NULL,0,0,0
ret
.endif
;---------------------------------------
;种子为2的随机数输出
invoke _srand,2 ;种子取2
invoke rand,5,5 ;生成5*5=25个随机数
.if eax
mov lpB,eax
.else
invoke MessageBox,NULL,0,0,0
ret
.endif
;---------------------------------------
;种子为时间戳
rdtsc
and eax,7fffffffh
invoke _srand,eax ;种子取2
invoke rand,5,5 ;生成5*5=25个随机数
.if eax
mov lpC,eax
.else
invoke MessageBox,NULL,0,0,0
ret
.endif
;---------------------------------------
;将上面生成的随机整数转成0~1之间的单精度浮点
invoke Int2FloatRand0_1,5,5,lpA,0
mov lpFloatA,eax
invoke Int2FloatRand0_1,5,5,lpB,0
mov lpFloatB,eax
invoke Int2FloatRand0_1,5,5,lpB,0
mov lpFloatC,eax
;**********************************************
;输出前8个随机数,种子为1(种子为2的类似)
;包括整型随机数和对应的0~1之间的单精度随机数
;----------------------------------------------
xor ecx,ecx
@@:
push ecx
mov esi,lpA
invoke crt_printf,offset fmt1,DWORD ptr [esi+ecx*4]
pop ecx
push ecx
mov edi,lpFloatA
fld DWORD PTR [edi+ecx*4]
sub esp,8
fstp QWORD PTR [esp]
push offset fmt0
call crt_printf
add esp,12
pop ecx
inc ecx
test ecx,7
jnz @B
;**********************************************
;释放
invoke VirtualFree,lpA,0,MEM_RELEASE
invoke VirtualFree,lpB,0,MEM_RELEASE
invoke VirtualFree,lpC,0,MEM_RELEASE
invoke VirtualFree,lpFloatA,0,MEM_RELEASE
invoke VirtualFree,lpFloatB,0,MEM_RELEASE
invoke VirtualFree,lpFloatC,0,MEM_RELEASE
invoke ExitProcess,0
end start