Giả sử ta có đoạn code sau đây được compile bằng gcc -o rand rand.c
Mục đích của ta là làm sao để input bằng output của rand()
trong C. Để giải quyết được bài toán này, ta sẽ tìm hiểu sơ lược về pseudorandom number generator (PRNG) cũng như cách hàm rand()
được implement trong glibc.
Hiểu một cách đơn giản, số random thực sự là một số được tạo ra hoàn toàn ngầu nhiên, không sinh ra dựa trên quy luật hay bất kỳ mục đích nào cả. Ví dụ đơn giản nhất chính là mật độ khí hiện tại trong phòng bạn, tiếng ồn ở một nơi bất kỳ, tung xí ngầu,… Và vì thế, các trường hợp mà bạn có khả năng điều khiển các dữ kiện gốc hay nói một cách khác là tạo ra dựa trên một quy luật, một thuật toán sinh nào đó thì được gọi là PRNG.
Để phục vụ nhu cầu tạo số random trên máy tính người ta đã đến với một giải pháp là sử dụng deterministic algorithm để tạo ra một số trong có vẻ là random nhưng thực chất là không random. Một trong những cách để implement nó chính là sử dụng linear congruential generator, và cách này được sài ở hàm rand() trong C.
Thuật toán này nhằm mục đích tạo ra một số random dựa trên một seed cho trước. Điều này đồng nghĩa với việc nếu ta lấy cùng một seed đó để sinh ra trong mỗi lần chạy chương trình thì vẫn được các số random ra y chang nhau.
Công thức truy hồi của LCG như sau:
Trong đó :
Hàm rand()
trong C sẽ gọi tới__random()
và __random_r()
sẽ đảm nhận việc tạo ra số random
Trong đó, __random_r()
sử dụng 2 cơ chế để random, single state (khúc trong if TYPE_0) và khúc multistate (mình tự gọi).
Single state là thuật toán đơn giản vì chỉ sử dụng duy nhất một "kiểu" sinh. Thuật toán này có khuyết điểm là với một số nào đó được sinh ra thì ta sẽ gặp lại số đó sau lần gọi rand()
. Cách này được gọi là TYPE_0
trong source glibc.
Multistate cho phép ta gặp lại số trùng nhau do đã có một vài cải tiến so với thuật toán trên. State này hoạt động như sau :
Với một seed s, và mảng , số được sinh ra sẽ thoả:
Từ trở đi thuật toán sẽ thành:
Kết quả hàm rand() thứ i sẽ là:
Khi ta set seed bằng srand()
thì sẽ mặc định sài cái multistate
Ta có code chạy multistate được viết lại như sau:
Nếu bạn compile rồi chạy thử code trên thì số được tạo ra sẽ y chang khi sài rand()
Vậy ta đã biết sơ lược về cách hàm rand()
hoạt động trong C. Với code đề bài đưa ra, ta có nhận xét là seed được tạo ra chính là thời điểm ta kết nối với server. Trong python có một thư viện hữu ích Ctypes, cho phép sử dụng các hàm có sẵn trong C (nếu bạn không thích sài thì code lại nguyên hàm time()
cũng như rand()
cũng được). Ở đây ta thấy time()
sẽ được tính kể từ thời điểm chương trình gọi nó, do vậy khi code python ta chạy thì hên xui sẽ có một độ delay nhất định so với server. Do vậy nếu không được ta sẽ thử từng time+1, time+2,...
để đồng bộ.
Script :
Ở đây hên là code mình đồng bộ với server luôn. Chạy và ta có được shell