AlliedModders

AlliedModders (https://forums.alliedmods.net/index.php)
-   Snippets and Tutorials (https://forums.alliedmods.net/forumdisplay.php?f=112)
-   -   [INC] alias method random (https://forums.alliedmods.net/showthread.php?t=332060)

arthurdead 04-20-2021 19:41

[INC] alias method random
 
i needed a way the get a random idx from a array based on weights and i found this blog post by some random dude:
https://blog.bruce-hill.com/a-faster...-random-choice
and ported the alias method to sourcepawn

include:
https://github.com/arthurdead/sm-plu...liasrandom.inc

seems to work good enough for me:
https://i.imgur.com/keVkgNy.png

Code:

#include <sourcemod>
#include <aliasrandom>

public void OnPluginStart()
{
        ArrayList randomnames = new ArrayList(ByteCountToCells(64));
        randomnames.PushString("test0");
        randomnames.PushString("test1");
        randomnames.PushString("test3");
        randomnames.PushString("test4");
        randomnames.PushString("test5");

        int len = randomnames.Length;
        ArrayList weights = new ArrayList(1, len);
        weights.Set(0, 15.0);
        weights.Set(1, 2.0);
        weights.Set(2, 70.5);
        weights.Set(3, 40.0);
        weights.Set(4, 5.0);

        AliasRandom test = new AliasRandom(weights);

        int attempts = 500;

        len = randomnames.Length;
        ArrayList nums = new ArrayList(1, len);
        for(int i = 0; i < len; ++i) {
                nums.Set(i, 0);
        }

        int fail = 0;

        char name[64];
        if(test != null) {
                for(int i = 0; i < attempts; ++i) {
                        int idx = test.Get();
                        if(idx == -1) {
                                ++fail;
                                continue;
                        }

                        int num = nums.Get(idx);
                        ++num;
                        nums.Set(idx, num);
                }
        }

        PrintToServer("ran %i times %i failed:", attempts, fail);

        for(int i = 0; i < len; ++i) {
                int num = nums.Get(i);
                randomnames.GetString(i, name, sizeof(name));
                float weight = weights.Get(i);
                PrintToServer("  got %s %i times (weight was %.1f)", name, num, weight);
        }

        delete nums;
        delete test;
        delete weights;
}


eyal282 04-22-2021 00:30

Re: [INC] alias method random
 
Duck duck go my store jackpot plugin. I think mine is more efficient, and I doubt arraylists are necessary.

Lux 04-23-2021 08:02

Re: [INC] alias method random
 
Quote:

Originally Posted by eyal282 (Post 2744769)
Duck duck go my store jackpot plugin. I think mine is more efficient, and I doubt arraylists are necessary.

-Post link to whatever code you are talking about.
-Perf test it whatever you are talking about and show results.
-Why not contribute with the more efficient code so everyone benefits using this include.


EDIT:I'v never used a weighting system tbh the blog was interesting, thanks for the include i will use it :)

Desktop 04-23-2021 14:14

Re: [INC] alias method random
 
Quote:

Originally Posted by eyal282 (Post 2744769)
Duck duck go my store jackpot plugin. I think mine is more efficient, and I doubt arraylists are necessary.


Yeah, if you could submit the code, it would be really nice, since these funcs are not often seen here and they're very useful

eyal282 05-11-2021 12:25

Re: [INC] alias method random
 
Quote:

Originally Posted by Desktop (Post 2744921)
Yeah, if you could submit the code, it would be really nice, since these funcs are not often seen here and they're very useful

https://github.com/eyal282/Alliedmod...ore-jackpot.sp

Localia 05-18-2023 06:36

Re: [INC] alias method random
 
I got an error at 'target.Push(i)' .
I guess in extreme cases, the result of "GetRandomFloat (-1.0,1.0) " can be 0.0, so the 'ArrayList target' will remain as "null",which can cause error when "target.Push(i)"


Code:

        ArrayList smalls = new ArrayList(2);
        ArrayList bigs = new ArrayList(2);

        int small_i = 0;
        int big_i = 0;

        for(int i = 0; i < len; ++i) {
                float weight = weights.Get(i);

                if(weight == avg)
                {
                        weight += GetRandomFloat(-1.0, 1.0);
                }


                bool is_small = (weight < avg);
                bool is_big = (weight > avg);

                ArrayList target = null;
                int target_i = -1;
                if(is_small)
                {
                        target = smalls;
                        target_i = small_i;
                }
                else if(is_big)
                {
                        target = bigs;
                        target_i = big_i;
                }

                target.Push(i);
                if(weight > 0.0 && avg > 0.0) {
                        weight = weight/avg;
                } else {
                        weight = 0.0;
                }
                target.Set(target_i, weight, 1);

                if(is_small) {
                        ++small_i;
                } else if(is_big) {
                        ++big_i;
                }
        }


I made a simple fix.
Code:

        if(weight == avg)
        {
                weight += GetRandomInt(0,99) < 50? 0.00001:-0.00001;
        }



All times are GMT -4. The time now is 04:15.

Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.