Phân phối ngẫu nhiên có trọng số liên tục, thiên về một đầu


28

Tôi hiện đang đóng góp cho một hệ thống hạt cho trò chơi của chúng tôi và phát triển một số hình dạng phát.

Phân phối ngẫu nhiên thống nhất của tôi dọc theo một dòng hoặc dọc theo một khu vực hình chữ nhật hoạt động tốt - không có vấn đề.

Nhưng bây giờ tôi muốn có một cái gì đó giống như độ dốc 1 chiều trong bản phân phối này. Điều này có nghĩa là các giá trị thấp hơn phổ biến hơn các giá trị cao hơn.

Tôi không biết điều gì sẽ là thuật ngữ toán học phù hợp cho vấn đề này, vì vậy kỹ năng tìm kiếm của tôi khá vô dụng với vấn đề này. Tôi cần một cái gì đó đơn giản về mặt tính toán, vì hệ thống hạt cần phải hiệu quả.



Không ai sẽ đề cập đến tính toán?
Alec Teal

Câu trả lời:


42

Hãy nhìn vào bức ảnh này:

Ánh xạ đường cong

Nó cho thấy quá trình ánh xạ một giá trị (ngẫu nhiên) vào một đường cong. Giả sử bạn tạo một giá trị ngẫu nhiên X được phân phối đồng đều, từ 0 đến 1. Bằng cách ánh xạ giá trị này thành một đường cong - hay nói cách khác là sử dụng f (X) thay vì X - bạn có thể làm lệch phân phối của mình theo bất kỳ cách nào bạn muốn .

Trong bức tranh này, đường cong đầu tiên làm cho các giá trị cao hơn có nhiều khả năng hơn; thứ hai làm cho giá trị thấp hơn có nhiều khả năng; và cái thứ ba làm cho cụm giá trị ở giữa. Công thức chính xác của đường cong không thực sự quan trọng và có thể được chọn theo ý muốn.

Ví dụ, đường cong thứ nhất trông hơi giống căn bậc hai và hình vuông thứ hai. Thứ ba là một chút giống như khối, chỉ dịch. Nếu bạn coi căn bậc hai là quá chậm, đường cong đầu tiên cũng trông giống như f (X) = 1- (1-X) ^ 2 - đảo ngược hình vuông. Hoặc một hyperbole: f (X) = 2X / (1 + X).

Như một đường cong thứ tư cho thấy, bạn chỉ cần sử dụng bảng tra cứu được tính toán trước. Có vẻ xấu như một đường cong, nhưng có lẽ sẽ đủ tốt cho một hệ thống hạt.

Kỹ thuật chung này rất đơn giản và mạnh mẽ. Bất cứ phân phối nào bạn cần, chỉ cần tưởng tượng một ánh xạ đường cong và bạn sẽ nghĩ ra một công thức ngay lập tức. Hoặc, nếu công cụ của bạn có trình chỉnh sửa, chỉ cần tạo trình chỉnh sửa trực quan cho đường cong!


cảm ơn bạn rất nhiều vì lời giải thích rất kỹ lưỡng và dễ hiểu của bạn. tất cả các bài viết khác cũng rất hữu ích, nhưng tôi thực sự có thể hiểu bài đăng của bạn dễ nhất và nhanh nhất. nó dính ra bc nó thực sự đạt đến chỗ cho cách hiểu của tôi. và các khía cạnh bạn đang giải thích là chính xác những gì tôi đang tìm kiếm (hoặc đi lang thang)! nó sẽ cho phép tôi sử dụng nó trong rất nhiều trường hợp trong tương lai. vậy thx nữa !!! btw, tôi đã chơi xung quanh với một số đường cong của bạn về chúng và nó hoạt động như sự quyến rũ.
didito

5
FYI: Chúng được gọi là các hàm lượng tử: en.wikipedia.org/wiki/Quantile_feft
Neil G

8

Một lời giải thích dài hơn:

Nếu bạn có phân phối xác suất mong muốn, chẳng hạn như gradient @didito được yêu cầu, bạn có thể mô tả là dưới dạng hàm. Giả sử bạn muốn phân phối hình tam giác, trong đó xác suất ở 0 là 0,0 và bạn muốn chọn một số ngẫu nhiên từ 0 đến 1. Chúng tôi có thể viết nó là y = x.

Bước tiếp theo là tính tích phân của hàm này. Trong trường hợp này, đó là x= =1x2 . Được đánh giá từ 0 đến 1, đó là ½. Điều đó có ý nghĩa - đó là một hình tam giác với cơ sở 1 và chiều cao 1, vì vậy diện tích của nó là.

Sau đó, bạn chọn một điểm ngẫu nhiên đồng đều từ 0 đến khu vực (trong ví dụ của chúng tôi). Hãy gọi đây là z. (Chúng tôi đang chọn thống nhất từ phân phối tích lũy .)

x= =1x21x̂2= =zx̂= =2z

2zrmộtnd(0,1)


thx cho đầu vào có giá trị của bạn. tôi luôn muốn nghe cách những người có kỹ năng giải quyết vấn đề. nhưng tôi vẫn cần phải quấn đầu xung quanh nó để thành thật ...
didito

điều này thật tuyệt. Tôi luôn luôn làm sqrt(random())cho toàn bộ cuộc sống của tôi nhưng tôi đã đến với nó theo kinh nghiệm. Cố gắng buộc một số ngẫu nhiên vào một đường cong, và nó đã làm việc. Bây giờ tôi đã thành thạo toán hơn một chút, biết tại sao nó hoạt động rất có giá trị!
Gustavo Maciel

5

Bạn có thể có được một xấp xỉ gần đúng với những gì bạn muốn bằng cách sử dụng một hệ thống theo cấp số nhân.

Tạo x dựa trên thứ gì đó như 1- (giá trị rnd ^) (Giả sử rnd nằm trong khoảng từ 0 đến 1) và bạn sẽ có một vài hành vi khác nhau từ lệch sang trái sang phải dựa trên những gì bạn sử dụng. Giá trị cao hơn sẽ giúp bạn phân phối sai lệch hơn

Bạn có thể sử dụng một công cụ đồ họa trực tuyến để có được một số ý tưởng sơ bộ về các hành vi mà các phương trình khác nhau sẽ cung cấp cho bạn trước khi đặt chúng vào, hoặc bạn có thể chỉ cần sử dụng các phương trình trực tiếp trong hệ thống hạt của mình, tùy thuộc vào phong cách nào phù hợp với sở thích của bạn.

CHỈNH SỬA

Đối với một cái gì đó giống như một hệ thống hạt trong đó thời gian CPU trên mỗi hạt là rất quan trọng, sử dụng Math.Pow (hoặc ngôn ngữ tương đương) trực tiếp có thể dẫn đến giảm hiệu suất. Nếu muốn có nhiều hiệu suất hơn và giá trị không bị thay đổi trong thời gian chạy, hãy xem xét chuyển sang một chức năng tương đương, chẳng hạn như x * x thay vì x ^ 2.

(Số mũ phân số có thể là một vấn đề, nhưng ai đó có nền tảng toán học mạnh hơn tôi có thể đưa ra một cách tốt để tạo ra một hàm xấp xỉ)


1
Thay vì sử dụng chương trình vẽ đồ thị, bạn chỉ có thể vẽ sơ đồ phân phối Beta vì đây là trường hợp đặc biệt. Đối với một cái nhất định value, đây là Beta (giá trị, 1).
Neil G

cám ơn. tôi đã thử vẽ một số đồ thị và tôi nghĩ nó có thể đưa tôi đến nơi tôi muốn.
didito

@Neil G cảm ơn vì mẹo với "phân phối beta" - điều này nghe có vẻ thú vị và hữu ích ... tôi sẽ thực hiện một số nghiên cứu về chủ đề đó
didito

3

Thuật ngữ bạn đang tìm kiếm là Weighted Random Numbers, hầu hết các thuật toán tôi đã thấy sử dụng các hàm trig, nhưng tôi nghĩ rằng tôi đã tìm ra một cách sẽ hiệu quả:

Tạo một bảng / mảng / Danh sách (bất cứ thứ gì) chứa giá trị số nhân cho hàm ngẫu nhiên. Điền nó bằng tay hoặc lập trình ...

randMulti= {.1,.1,.1,.1,.1,.1,.2,.2,.3,.3,.9,1,1,1,} 

... sau đó Nhân randomvới một lựa chọn ngẫu nhiên randMultivà cuối cùng là Giá trị tối đa của phân phối ...

weightedRandom = math.random()*randMulti[Math.random(randMulti.length)]*maxValue

Tôi tin rằng điều này sẽ nhanh hơn nhiều so với việc sử dụng sqrthoặc các hàm phức tạp hơn về mặt tính toán khác và sẽ cho phép các mẫu nhóm tùy chỉnh hơn.


2
Nếu bạn có thể hy sinh bộ nhớ, một bảng gồm 100 giá trị được tính toán trước sẽ nhanh hơn (và chính xác hơn một chút). Tôi nghi ngờ người dùng sẽ có thể phân biệt giữa các phiên bản đầy đủ và được tính toán trước.
Daniel Blezek

@Daniel nó sẽ nhanh hơn, nhưng với 100 giá trị ngẫu nhiên, khá dễ dàng để thấy các mẫu lặp lại.
Tấn côngHobo

Chỉ vì dường như có một mẫu lặp lại không có nghĩa là nó không ngẫu nhiên. Bản chất của tính ngẫu nhiên là tính không thể đoán trước của nó, điều đó có nghĩa đen là nhiều như người ta không thể dự đoán rằng sẽ không có một mô hình, người ta cũng không thể dự đoán rằng có thể có một (ít nhất là trong một thời gian ngắn). Bạn sẽ phải thực hiện một số thử nghiệm, nhưng nếu bạn tìm thấy các mẫu có nhiều thử nghiệm sử dụng các hạt giống khác nhau, thì thuật toán của bạn để tạo các số giả ngẫu nhiên có thể cần được xem xét.
Randolf Richardson

@AttackingHobo thx cho mánh khóe đó. tôi thích việc sử dụng LUT. và công thức khá dễ hiểu. tôi đã không nghĩ về nó theo cách này trước đây. không nhìn thấy gỗ cho cây ... :) tôi cũng nghĩ rằng nên tránh các kiểu lặp lại nhưng có lẽ sẽ không được nhận ra trong trường hợp này. Tuy nhiên, tính toán trước tất cả các giá trị sẽ làm tổn thương trải nghiệm hình ảnh. Dù sao đi nữa, thx đã nhắc nhở tôi rằng đây là một yếu tố cần xem xét về chủ đề ngẫu nhiên ...
didito

cũng cảm ơn vì đã đưa ra thuật ngữ "Số ngẫu nhiên" có trọng số!
didito

2

Tôi nghĩ những gì bạn yêu cầu là phân phối đạt được bằng cách sử dụng hàm căn bậc hai.

[position] = sqrt(rand(0, 1))

Điều này sẽ đưa ra một phân phối trong trường thứ nguyên đơn [0, 1]trong đó xác suất cho một vị trí tương đương với vị trí đó, tức là "phân phối tam giác".

Thế hệ squareroot-free thay thế:

[position] = 1-abs(rand(0, 1)-rand(0, 1))

Một căn bậc hai trong việc thực hiện tối ưu chỉ là một vài lệnh nhân và tổng không có nhánh. (Xem: http://en.wikipedia.org/wiki/Fast_inverse_sapes_root ). Mà một trong hai chức năng này nhanh hơn có thể khác nhau tùy thuộc vào nền tảng và trình tạo ngẫu nhiên. Ví dụ, trên nền tảng x86, chỉ cần một vài nhánh không thể đoán trước trong trình tạo ngẫu nhiên để làm cho phương thức thứ hai chậm hơn.


Xác suất của một vị trí sẽ không bằng vị trí (điều đó là không thể về mặt toán học - tầm thường, miền và phạm vi của hàm bao gồm cả 0,50 và 0,51), cũng không phải là phân bố tam giác. ( en.wikipedia.org/wiki/Triangular_distribution )

1
Mặc dù sqrt đưa ra một số mẫu thú vị, nhưng các hệ thống hạt thường cần rất nhẹ CPU cho mỗi hạt, vì vậy tôi khuyên bạn nên tránh căn bậc hai (chậm tính toán) khi có thể. Đôi khi bạn có thể thoát khỏi việc chỉ cần tính toán trước chúng, nhưng nó có thể làm cho các hạt của bạn có các khối đáng chú ý theo thời gian.
Âm lịch

1
@Joe Wreschnig, bạn đã tự mình đọc bài viết Wikipedia đó, nhét a = 0, b = 1, c = 1 vào công thức thế hệ và bạn có được công thức trong bài viết của tôi.
aaaaaaaaaaaa

3
@Lunin, tại sao bạn lại phàn nàn về căn bậc hai khi bạn có số mũ trong câu trả lời của bạn?
aaaaaaaaaaaa

1
@Lunin: Lý thuyết hiệu suất là một lĩnh vực bị bỏ quên khá nhiều, rất nhiều điều mà mọi người nghĩ rằng họ biết chính xác khoảng 30 năm trước khi ALU đắt đỏ và chậm chạp. Ngay cả hàm số mũ mà bạn vừa phát hiện là hàm số học khá chậm cũng hiếm khi là một tội nhân có hiệu suất rất đáng kể. Việc phân nhánh (sử dụng câu lệnh if) và lỗi bộ nhớ cache (đọc một đoạn dữ liệu hiện không lưu trong bộ đệm) thường là chi phí hiệu năng cao nhất.
aaaaaaaaaaaa

1

Chỉ cần sử dụng bản phân phối Beta:

  • Beta (1,1) không thay đổi
  • Beta (1,2) là một gradient tuyến tính
  • Beta (1,3) là bậc hai

v.v.

Hai tham số hình dạng không cần phải là số nguyên.


thx giúp đỡ của bạn. như đã nêu ở trên, bản phân phối beta nghe có vẻ thú vị. nhưng tôi không thể hiểu nội dung của trang wikipedia. hoặc một công thức / mã. tốt, tôi cũng không có thời gian để điều tra thêm: si thấy rằng boost có mã cho các bản phân phối beta, nhưng điều này sẽ là quá mức cần thiết. tốt, tôi đoán tôi cần phải trải qua nó trước và sau đó viết phiên bản đơn giản hóa của riêng tôi.
didito

1
@didito: Không khó lắm đâu. Bạn chỉ cần thay thế uniform_generator()cuộc gọi của bạn với gsl_ran_beta(rng, a, b). Xem tại đây: gnu.org/software/gsl/manual/html_node/ Kẻ
Neil G

thx cho gợi ý. Tôi không sử dụng GSL (thực sự chưa từng nghe về nó trước đây), nhưng cuộc gọi tốt. tôi sẽ kiểm tra nguồn!
didito

@didito: Trong trường hợp đó, tôi sẽ đi với giải pháp của Lunin. Chúc may mắn.
Neil G

0

Thậm chí đơn giản hơn, tùy thuộc vào tốc độ của trình tạo ngẫu nhiên của bạn, bạn chỉ có thể tạo hai giá trị và tính trung bình cho chúng.

Hoặc, thậm chí đơn giản hơn, trong đó X là kết quả của rng, đầu tiên double y = double(1/x);, x = y*[maximum return value of rng];. Điều này sẽ cân số lượng theo cấp số nhân với số thấp hơn.

Tạo và trung bình nhiều giá trị hơn để tăng khả năng nhận các giá trị gần trung tâm hơn.

Tất nhiên, điều này chỉ hoạt động đối với các bản phân phối đường cong chuông tiêu chuẩn hoặc các phiên bản "gấp" của chúng *, nhưng với trình tạo nhanh, nó có thể nhanh hơn và đơn giản hơn so với sử dụng các hàm toán học khác nhau như sqrt.

Bạn có thể tìm thấy tất cả các loại nghiên cứu về điều này cho các đường cong xúc xắc chuông. Trên thực tế, Anydice.com là một trang web tốt tạo ra các biểu đồ cho các phương pháp gieo xúc xắc khác nhau. Mặc dù bạn đang sử dụng RNG, tiền đề là như nhau, cũng như kết quả. Vì vậy, nó là một điểm tốt để xem phân phối trước khi mã hóa nó.

* Ngoài ra, bạn có thể "gấp" phân phối kết quả dọc theo một trục bằng cách lấy trục và trừ kết quả trung bình sau đó thêm trục. Ví dụ: bạn muốn các giá trị thấp hơn trở nên phổ biến hơn và giả sử bạn muốn 15 là giá trị tối thiểu của bạn và 35 là giá trị tối đa của bạn, phạm vi 20. Vì vậy, bạn tạo và trung bình hai giá trị với phạm vi 20 ( gấp đôi phạm vi bạn muốn), sẽ đưa ra một bellcurve tập trung vào 20 (chúng tôi trừ năm ở cuối để chuyển phạm vi từ 20 đến 40, thành 15 đến 35). Lấy các số X và Y được tạo.

Số cuối cùng,

z =(x+y)/2;// average them
If (z<20){z = (20-z)+20;}// fold if below axis
return z-5;// return value adjusted to desired range

Nếu số 0 là mức tối thiểu của bạn, thậm chí tốt hơn, hãy làm điều này thay vào đó,

z= (x+y)/2;
If (z<20){z = 20-z;}
else {z = z - 20;}
return z;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.