Menu

Số ngẫu nhiên trong R: rnorm, runif, sample và set.seed

Tạo dữ liệu ngẫu nhiên bằng rnorm(), runif(), rbinom() và sample(), giúp kết quả tái tạo được bằng set.seed(), và hiểu hệ thống đặt tên d/p/q/r cho các phân phối của R.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

set.seed() đi trước tiên

Các giá trị ngẫu nhiên thay đổi sau mỗi lần chạy thì vô dụng cho việc dạy học, chấm bài, gỡ lỗi hay nghiên cứu khoa học: nếu mô phỏng của bạn hôm nay cho ra 0,146 và ngày mai cho ra 0,153, con số nào sẽ vào báo cáo? set.seed() cố định điểm bắt đầu của bộ sinh số, khiến toàn bộ dòng "ngẫu nhiên" tiếp theo lặp lại chính xác:

Cùng seed, cùng giá trị rút ra, mọi lần, trên mọi máy. Những con số này là giả ngẫu nhiên: một dãy tất định được thiết kế để vượt qua mọi phép kiểm định thống kê về tính ngẫu nhiên, với seed quyết định bạn bắt đầu từ đâu trong dãy. Bản thân giá trị seed không mang ý nghĩa gì, 42, 7 hay 20260807 đều được; chỉ cần ghi lại. Thói quen cần xây dựng: một lệnh set.seed() ở đầu mọi script có dùng tính ngẫu nhiên. (Lưu ý rằng mỗi lần rút giá trị đều làm trạng thái tiến lên, nên thứ tự các lệnh gọi cũng ảnh hưởng đến khả năng tái tạo.)

Hệ thống d/p/q/r: một bảng giải mã cả thư viện

R đặt tên mọi hàm phân phối theo dạng tiền tố + họ phân phối, và khi đã thấy bảng này, bạn đọc được toàn bộ thư viện thống kê:

Tiền tốCâu hỏi nó trả lờiVí dụ với phân phối chuẩn
rCho tôi các giá trị ngẫu nhiênrnorm(5)
dMật độ: đường cong cao bao nhiêu tại x?dnorm(0)
pXác suất: P(X ≤ x) bằng bao nhiêu?pnorm(1.96)
qPhân vị: giá trị x nào nằm ở phân vị này?qnorm(0.975)

p và q là hàm ngược của nhau, và đây chính là cặp bạn đã gặp trong khoảng tin cậy dưới dạng qt(0.975, df):

Thay tên họ phân phối và mọi thứ vẫn giữ nguyên: runif/dunif/punif/qunif, rbinom/..., rpois/..., rt/..., rexp/.... Học bốn tiền tố, dùng được hàng chục phân phối.

rnorm(): rút giá trị từ phân phối chuẩn

rnorm(n, mean, sd) rút giá trị từ một đường cong hình chuông, công cụ chủ lực để mô phỏng dữ liệu kiểu số đo:

Trung bình và độ lệch chuẩn của mẫu rơi vào gần 100 và 15, chứ không đúng bằng: khoảng chênh đó chính là nhiễu lấy mẫu, và nó thu hẹp khi n tăng. Dòng cuối là một mẹo đáng học: mean() của một vector logic là tỷ lệ các giá trị TRUE, và tỷ lệ vượt quá 130 ra gần với giá trị lý thuyết 1 - pnorm(130, 100, 15) ≈ 2,3%. Giá trị mặc định là mean = 0, sd = 1 (phân phối chuẩn tắc). Một histogram của iq cho thấy hình chuông quen thuộc.

runif(), rbinom(), rpois()

Ba họ phân phối nữa đáp ứng hầu hết nhu cầu mô phỏng:

runif() rải các giá trị đều trên một khoảng ("uniform", không phải "run if", ai cũng từng đọc nhầm một lần). rbinom(n, size, prob) mô phỏng n thí nghiệm, mỗi thí nghiệm gồm size lần thử, và trả về số lần thành công của mỗi thí nghiệm; vậy mỗi giá trị ở trên là số lần ra mặt ngửa trong 10 lần tung đồng xu. rpois(n, lambda) tạo ra số lần xảy ra của các sự kiện độc lập với tốc độ trung bình đã biết: số yêu cầu hỗ trợ mỗi giờ, số lỗi chính tả mỗi trang.

sample(): lấy mẫu và xáo trộn

Trong khi các hàm r* tạo ra giá trị mới từ một phân phối, sample() rút từ những giá trị bạn đã có sẵn:

Đối số replace là mấu chốt: FALSE (mặc định) giống như chia bài, mỗi giá trị chỉ xuất hiện một lần, và yêu cầu nhiều hơn số bạn có sẽ báo lỗi; TRUE giống như gieo xúc xắc, mỗi lần rút đều bắt đầu lại. Lấy mẫu có hoàn lại từ chính dữ liệu của bạn là cỗ máy của bootstrap. Khi chỉ truyền một vector, sample(x) trả về một hoán vị ngẫu nhiên: đây là cách viết thường dùng để xáo trộn.

Lấy mẫu các hàng của một data frame thì dùng sample() bên trong phép lập chỉ mục hàng:

sample(nrow(mtcars), 5) chọn 5 số thứ tự hàng ngẫu nhiên; phép lập chỉ mục lấy ra những hàng đó. Đây là thao tác chuẩn để kiểm tra nhanh một bộ dữ liệu lớn hoặc chia tập train/test.

Một mô phỏng Monte Carlo nhỏ

Đây là thành quả của cả bộ công cụ. Câu hỏi: một quy trình cho ra các số đo có phân phối N(100, 15); bạn lấy trung bình của 10 số đo, xác suất để trung bình đó vượt quá 105 là bao nhiêu? Thay vì suy ra đáp án, hãy mô phỏng nó: làm thí nghiệm 10.000 lần rồi đếm:

Kết quả mô phỏng chỉ lệch vài phần nghìn so với giá trị chính xác (khoảng 0,146). Đó là Monte Carlo trong một hơi thở: viết một lần thử thành một hàm, replicate() nó hàng nghìn lần, lấy mean() của các lần thành công. Ở đây có đáp án chính xác vì bài toán đơn giản như trong sách giáo khoa; ngay khi câu hỏi trở nên phức tạp (phân phối kỳ lạ, giá trị lớn nhất của các lần rút có tương quan, một trò chơi theo luật), con đường giải tích khép lại còn công thức mô phỏng vẫn chạy nguyên vẹn. Để ý 15 / sqrt(10) trong phép kiểm tra: giá trị trung bình dao động ít hơn từng giá trị riêng lẻ, cũng chính là quy luật căn bậc hai quyết định độ rộng của khoảng tin cậy.

Đủ tốt cho mô phỏng, không đủ cho bảo mật

Một giới hạn cần tôn trọng: bộ sinh số mặc định của R (Mersenne Twister) được xây dựng cho chất lượng thống kê và tốc độ, không phải cho bí mật. Đầu ra của nó là tất định khi biết seed, và trạng thái bên trong có thể được khôi phục từ đầu ra quan sát được: những lỗ hổng chết người với mật khẩu, token hay bất cứ thứ gì liên quan đến bảo mật. Cho mô phỏng, bootstrap và dạy học thì nó rất tuyệt; cho mật mã học, hãy dùng thư viện chuyên dụng (ví dụ package openssl), không bao giờ dùng sample() hay runif().

Những điều cần nhớ

  • Một lệnh set.seed() ở đầu script giúp mọi kết quả "ngẫu nhiên" tái tạo được: cùng seed, cùng giá trị.
  • Các tiền tố d/p/q/r giải mã cả thư viện phân phối: rút ngẫu nhiên, mật độ, xác suất tích lũy, phân vị.
  • rnorm(n, mean, sd), runif(n, min, max), rbinom(n, size, prob), rpois(n, lambda) đáp ứng hầu hết nhu cầu mô phỏng.
  • sample() rút từ chính các giá trị của bạn: replace = TRUE cho xúc xắc, mặc định cho chia bài, không có size để xáo trộn; df[sample(nrow(df), k), ] lấy mẫu các hàng.
  • Monte Carlo = hàm một lần thử + replicate() + mean(): công thức trả lời những câu hỏi xác suất mà toán học khó tiếp cận thuận tiện.
  • Bộ sinh số ngẫu nhiên của R dành cho mô phỏng, không dành cho mật mã học.

Tiếp theo: gỡ lỗi, ý nghĩa thật sự của các thông báo lỗi trong R và cách đọc traceback.

Câu hỏi thường gặp

set.seed() trong R dùng để làm gì?

Hàm này cố định điểm bắt đầu của bộ sinh số ngẫu nhiên trong R, nên các giá trị "ngẫu nhiên" rút ra sau đó giống hệt nhau mỗi lần chạy. Gọi nó một lần ở đầu mọi script có dùng tính ngẫu nhiên, ví dụ set.seed(42), và mô phỏng của bạn sẽ tái tạo được: đồng nghiệp, người chấm bài và chính bạn sau này đều thấy cùng những con số.

Làm sao để tạo số ngẫu nhiên trong R?

Chọn phân phối: rnorm(n, mean, sd) cho phân phối chuẩn, runif(n, min, max) cho phân phối đều, rbinom(n, size, prob) cho số lần thành công, rpois(n, lambda) cho số lần xảy ra sự kiện. Để lấy mẫu từ các giá trị có sẵn, hãy dùng sample(x, size).

rnorm, dnorm, pnorm và qnorm khác nhau thế nào?

Một phân phối, bốn tiền tố: r rút giá trị ngẫu nhiên, d cho độ cao của đường cong mật độ, p cho xác suất tích lũy P(X ≤ x), và q là hàm ngược của nó, tức giá trị tại một phân vị cho trước. Bốn tiền tố này dùng được cho mọi phân phối mà R biết: runif/dunif/punif/qunif, rbinom/dbinom/pbinom/qbinom, và cứ thế.

Làm sao để lấy mẫu ngẫu nhiên các hàng từ một data frame trong R?

Lập chỉ mục các hàng bằng sample(): df[sample(nrow(df), 5), ] chọn 5 hàng không hoàn lại. Thêm replace = TRUE để lấy mẫu có hoàn lại (thao tác đứng sau phương pháp bootstrap).

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu