Thuật toán tạo ngẫu nhiên
Trang 1Thuật toán Tạo số ngẫu nhiên và kiểm tra tính ngẫu nhiên
Thu Hương
1 Khái niệm số ngẫu nhiên
Thực tế chúng ta đã gặp rất nhiều số ngẫu nhiên, vậy số ngẫu nhiên là gì?Có thể hiểu một cách đơn giản khi ai đó đòi hỏi một số ngẫu nhiên cũng có nghĩa là không chú ý đó là số mấy và đó là số nào cũng được Ngược lại, Ngẫu nhiên là một khái niệm toán học được định nghĩa chính xác: mọi số đều có khả năng xuất hiện tương đương nhau”
Vậy các số được dùng làm số ngẫu nhiên phải giới hạn vào một phạm vi nhất định Không thể có một số nguyên ngẫu nhiên, chỉ có một số nguyên trong một miền xác định nào đó Không có cách nào để tạo ra các số ngẫu nhiên thực sự từ một máy tính vì thực ra khi chương trình cho chúng ta viết, thì chắc chắn các số tạo ra có thể suy luận được Chỉ có thể
hi vọng viết chương trình tạo ra các chuỗi số có được nhiều thuộc tính giống như số ngẫu nhiên Các số này thường được gọi là các số giả ngẫu nhiên (pseudo-random) Chúng không thực sự ngẫu nhiên nhưng chúng có thể hữu dụng như sự xấp xỉ của các số ngẫu nhiên
2 Một số ứng dụng của số ngẫu nhiên
Một trong những ứng dụng phổ biến nhất với mục đích chính là mã hóa các thông điệp để chỉ người nhận thông điệp mới đọc được chúng Có thể hiểu việc làm điều này là tạo cho thông điệp có dạng như một chuỗi số ngẫu nhiên bằng cách tạo ra một chuỗi giả ngẫu nhiên để mã hóa và cũng với chuỗi đó, người nhận có thể giải mã được.Lĩnh vực ứng dụng
số ngẫu nhiên là lĩnh vực mô phỏng Các số ngẫu nhiên rất thích hợp làm dữ liệu để nhập cho các chương trình mô phỏng lớn Một số lượng dữ liệu phân tích lớn đôi khi chỉ cần xử
lý trên một tập con nhỏ của nó, bằng cách lựa chọn theo kiểu thử ngẫu nhiên là đủ
3 Một số phương pháp tạo số ngẫu nhiên (Phương pháp đồng dư tuyến tính)
Là một thuật toán nổi tiếng tạo số ngẫu nhiên được D.Lehner đưa ra vào những năm 1951 Thuật toán này được thể hiện ngắn gọn qua đoạn chương trình sau tạo N số ngẫu nhiên cho mảng A:
A[0]:=s;For i:=1 to N do A[i]: =A[i-1]*b + 1 mod m;
Với 3 hằng số s, N và m,
Trong thuật toán này, để tạo một số ngẫu nhiên mới, ta lấy số trước đó nhân với b, cộng thêm 1, và lấy phần dư trong phép chia cho m Như vậy, số nhận được luôn nằm từ 0 đến (m-1)
Trang 2Thuật toán này có vẻ thật đơn giản, nhưng phương pháp tạo số ngẫu nhiên Đồng dư tuyến tính này đã là chủ đề của nhiều tập toán khó và chi tiết Bởi việc chọn các hằng số (s, N và m) là thực sự không hề đơn giản
Đầu tiên, m nên lớn, nó có thể là giá trị tối đa của một word, nhưng cũng không cần phải hoàn toàn lớn như vậy nếu không tiện Thường thì chọn m là luỹ thừa của 10 hay của 2 là thuận lợi
Không nên chọn hằng b quá lớn hay quá nhỏ: an toàn hơn cả là chọn b có ít hơn m một chữ
số
Quy luật chọn hằng số trên được phát triển bởi D.E.Knuth Knuth đã chứng minh rằng sự lựa chọn này làm phương pháp đầy đủ tuyến tính làm phương pháp tạo ra số ngẫu nhiên tốt, thoả mãn được nhiều kiểm tra thống kê phức tạp Câu hỏi đặt ra là sẽ thế nào nếu ta tạo
ra một chu kỳ nhỏ so với miền xác định của nó?
Ví dụ 1:
Chọn b=19, m=481, s=0, sẽ tạo ra chuỗi 0, 1, 20, 1,… một chuỗi không ngẫu nhiên trong khoảng từ 0 đến 480
Không phải dễ dàng nhận ra những khó khăn đó Bởi vậy việc sử dụng những chỉ dẫn chọn các hằng số (b, m và s) là nên chú ý
Giá trị khởi động bất kỳ nào cũng có thể sử dụng được trong việc tạo các số ngẫu nhiên mà không có ảnh hưởng gì đặc biệt Thường thì không cần thiết phải lưu trữ cả chuỗi như trong ví dụ nêu trên Ta chỉ cần giữ lại một biến toàn cục a, khởi động với một giá trị nào
đó, rồi cập nhật bằng phép tính a:=(a*b+1) mod m.
Để có thể lập trình được bằng Pascal(hay một số ngôn ngữ lập trình khác), ta phải qua 1 bước nữa vì chúng ta không thể bỏ qua tình trạng tràn: nó đựoc định nghĩa là một trường hợp lỗi mà có thể tạo ra các kết quả không dự đoán được Giả sử máy vi tính của chúng ta
có word 32 bit, mà ta chọn m=1000000000, b=33332245 và a=2345678 Tất cả các giá trị này đều nhỏ hơn giá trị tối đa của một số nguyên, nhưng phép toán đầu tiên (a*b+1) đã làm tràn Kết quả đã gây ra sự tràn thì không có quan hệ với sự tính toán của chúng ta vì chúng
ta chỉ quan tâm đến 8 ký số sau Một thủ thuật để tạo bỏ sự tràn là phân nhỏ phép nhân ra làm nhiều phần
Để nhân p và q, chúng ta viết p=104p1 + p0 và q = 104q1 + q0
Do đó kết quả là:
Q p = (10 4 p 1 + p 0 )(10 4 q1 + q0) = 10 4 q 1 p 1 + 10 4 (p 1 q 0 + p 0 q 1 + p0 q 0)
Trang 3Chúng ta chỉ muốn 8 ký số cho kết quả, vì thế ta có thể bỏ qua số hạng đầu tiên 104 q1p1 và
ở số hạng thứ 2 ta sẽ bỏ qua 4 ký số đầu (04 (p1q0+p0q1) ta sẽ có thực hiện điều này qua hàm
mult sau:
Fuction mult(p,q : integer) :integer ;
Var p1, p0, q1, q0:integer;
Begin
p1:= p div m1;
p0:=p mod m1;
q1:=q div m1;
q0:=q mod m1;
mult:=(((p0*q1+p1*q0)mod m1)*m1 +p0q0) mod m;
End;
End;
Hàm mult tính (p*q mod m), không bị tràn khi m nhỏ hơn ½ giá trị số nguyên tối đa Kỹ thuật này hiển nhiên có thể đáp ứng với các giá trị m1 khác theo nguyên tắc m=m1 * m1
Chương trình tạo số ngẫu nhiên
Program random(input,output);
Const m=100000000; m1=10000; b=3141581;
Var i,a,N:integer;
Function random:integer;
Begin
a:=(mult(a,b)+1) mod m;
random:=a;
End;
Begin
Readln(N,a);
For i:=1 to N do writeln(random);
End
Khi chạy chương trình với dữ liệu nhập n=10 ; q=1234567; chương trình sẽ tạo được 10 số như sau 35884508, 80001069, 63512650,43635651, 1034472, 87181513, 6917174,
209855,67115956,59939877
1 Kiểm tra sự ngẫu nhiên
Thực tế cho thấy, trong một chuỗi ngẫu nhiên gồm 50 số thuộc miền xác định [1,50], một vài số có thể xuất hiện hơn một lần và cũng sẽ có vài số khác có thể không có trong chuỗi
số Nếu không đạt được điều này trong một chuỗi giả ngẫu nhiên thì đã có điều gì đó sai trong việc tạo chuỗi ngẫu nhiên Nhiều phương pháp kiểm tra đặt cơ sở trên các nhận xét kiểu này được áp dụng cho phương pháp tạo số ngẫu nhiên để kiểm tra xem 1 chuỗi giả
Trang 4ngẫu nhiên có các thuộc tính của chuỗi ngẫu nhiên hay không Ta sẽ xét một trong những thuật toán kiểm tra này
Thường thì dễ dàng nhận ra một chuỗi không ngẫu nhiên, nhưng rất khó chứng minhmột chuỗi là ngẫu nhiên, cũng không thể xác định chính xác thuộc tính nào của số ngẫu nhiên
là quan trọng đối với một trình ứng dụng Bởi vậy việc đưa ra những kiểm tra là thực sự cần thiết để đảm bảo không có tình huống suy thoái xảy rạ
Nhiều kiểm tra đã được triển khai để xác định một chuỗi có được nhiều tính chất của chuỗi ngẫu nhiên thực sự hay không Hầu hết các kiểm tra này đều có cơ sở giá trị trong toán học Một trong những kiểm tra đó là phương pháp kiểm tra chi-bình phương, một kiểm tra thống kê, có bản chất cơ bản, dễ cài đặt và hữu dụng trong nhiều ứng dụng.Ý tưởng của phương pháp chi-bình phương là xét các số tạo ra có phân bố một cách hợp lý hay không Nếu chúng tạo ra N số dương có giá trị nhỏ hon r, thì ta mong muốn nhận được khoảng N/r
số cho mỗi giá trị Đây là cốt lõi của vấn đề, số lần xuất hiện của tất cả các giá trị lại không nên lúc nào cũng bằng nhau, vì như thế thì không ngẫu nhiên.Dẫn đến việc cần tính toán xem một chuỗi các số có được phân bố giống như một chuỗi ngẫu nhiên hay không, đơn giản như chương trình sau:
Fuction kiemtraRD(N,r,s:integer):real;
Var I,t:integer; f: array[0 rmax] of integer;
Begin
Raninit(s);
For i:=0 to rmax do f[i]:=0;
For i:=1 to N do
Begin
T:=randomint(r);
f[t]:=f[t]+1;
end;
t:=0;
for i:=0 to r-1 do t:=t+f[i]*f[i];
kiemtraRD:=((r*t/N) - N) ;
End ;
Đơn giản là tính tổng số của bình phương tần số(số lần) xuất hiện của mỗi giá trị, chia cho
tần số mong muốn (N/r), rồi trừ bớt chiều dài chuỗi(N).Giá trị thống kê chi-bình phương
này có thể biểu diễn theo công thức toán như sau:
(0<I< ngẫu không chủng thì r xa nỏ nểu lại ngược nhiên, là đỏ sổ cảc vởi gần càng phương chi-bình trị giả>
Trang 5Đối với phương pháp kiêm tra đơn giản mà chúng ta đang thực hiện, giá trị chi-bình phương chỉ nên lệch một khoảng so với r Điều này chỉ đúng nếu N lớn hơn 10r Để đảm bảo nên thực hiện kiểm tra vài lần, bởi nó có thể sai 1 trong 10 lần kiểm tra
Phương pháp kiểm tra này cài đặt đơn giản nên thích hợp cho kết nó vào bên trong mỗi chương trình tạo số ngẫu nhiên, chỉ để đảm bảo rằng không có điều gì ngoài dự tính có thể gây ra các vấn đề nghiêm trọng