Jump to content

Forums Announcement

Read-Only Mode for Announcements & Changelogs

Dear Survivors, we'd like to inform you that this forum will transition to read-only mode. From now on, it will serve exclusively as a platform for official announcements and changelogs.

For all community discussions, debates, and engagement, we encourage you to join us on our social media platforms: Discord, Twitter/X, Facebook.

Thank you for being a valued part of our community. We look forward to connecting with you on our other channels!

Stay safe out there,
Your DayZ Team

Sign in to follow this  
Publik

fn_selectRandomWeighted

Recommended Posts

fn_selectRandomWeighted is bad. It's inefficient. It makes working with the spawn system terrible.

//Created weighted array of indices.
_weighted = [];
for "_i" from 0 to ((count _weights) - 1) do
{
private ["_weight"];
_weight = _weights select _i;

//Ensure the weight is a Number.
//If it's not, set weight to 0 to exclude it.
if ((typeName _weight) != (typeName 0)) then {diag_log "Log: [selectRandomWeighted] Weights should be Numbers; weight set to 0!"; _weight = 0};

//The weight should be a Number between 0 and 1.
if (_weight < 0) then {diag_log "Log: [selectRandomWeighted] Weights should be more than or equal to 0; weight set to 0!"; _weight = 0};
//if (_weight > 1) then {debugLog "Log: [selectRandomWeighted] Weights should be less than or equal to 1; weight set to 1!"; _weight = 1};

//Normalize the weight for a precision of hundreds.
_weight = round(_weight * 100);

for "_k" from 0 to (_weight - 1) do
{
_weighted = _weighted + [_i];
};
};

If you don't program, this solution is like this:

You've got a bunch of values, and they need to be selected at different frequencies. For example, you want beans to spawn twice as often as pasta. Beans would have a weight of 2 and pasta would have a weight of 1. This function attempts to select beans twice as often as pasta by creating a list with 2 beans and a pasta, like this:

[beans, Beans, Pasta]

If you randomly select one of these 3 values, you will statistically select beans twice as often as pasta. It works, right?

Sorta. What if you have 1000 items to chose from? And your weight ranges anywhere from 0 to 100? That's 10,000 items at max. What if you want 100 items, but you want the weight range to be more granular, like say 0 to 1000? You've still got 10,000 items in your resulting array!

A better solution?

Tally the total number of weights. Pick a number between 1 and the total. For each item, add its weight to a counter. If the counter passes the random number at any point, you've selected the weighted item.

Example time. You want Coke to spawn twice as often as Pepsi. Coke has a weight of 6, and Pepsi has a weight of 3. The total weight is 9, so pick a number between 1 and 9. Let's say you select 7 (a Pepsi). Add 6 (the weight of Coke) to your counter. 6 < 7, so keep going. Add 3 (the weight of Pepsi) to your counter. 9 > 7! You've passed 7! Your selected item is a Pepsi.

With this change, you don't have a (possibly) huge array to lug around. You've only got a counter. It doesn't matter what the weight is either. You could have a weight of 10000 and a weight of 0.00000001 in the same list, and it wouldn't make a single difference.

If I can get my own version working using a less inefficient algorithm I'll post it as an edit.

  • Like 1

Share this post


Link to post
Share on other sites

If anyone would like to help, a function for getting the sum of a number array and a function for selecting a random number (with decimals) would be appreciated.

Edited by Publik

Share this post


Link to post
Share on other sites
Sign in to follow this  

×