1

I need to generate 238 numbers, with a range of 1-4, but I want to weight them, so there's say 35% chance of getting 3, 28% chance of getting 2, 18% chance of getting 4m, and 19% chance of getting 1.

I found this..

def select( values ):
 variate = random.random() * sum( values.values() )
 cumulative = 0.0 
for item, weight in values.items():
     cumulative += weight
     if variate < cumulative:             return item
 return item # Shouldn't get here, but just in case of rounding...  print select( { "a": 70, "b": 20, "c": 10 } )

But I don't see how to convert that to AS3?

Phil
  • 2,995
  • 6
  • 40
  • 67

3 Answers3

2

I would do something like this:

var values:Array = [1,2,3,4];
var weights:Array = [35, 28, 18, 19];

var total:Number = 0;
for(var i in weights) {
    total += weights[i];
}

var rndNum:Number = Math.floor(Math.random()*total);

var counter:Number = 0;
for(var j:Number = 0; j<weights.length; j++) {
    counter += weights[j];
    if( rndNum <= counter ) return values[j]; //This is the value
}

(untested code, but the idea should work)

Jonatan Hedborg
  • 4,382
  • 21
  • 30
1

Here for you:

/**
 * random due to weighted values
 *
 * @param {{}} spec such as {'a':0.999, 'b':0.001}
 * @return {*} return the key in object
 */
public static function weightedRand(spec:Object):* {
    var i:String, j:int, table:Array = [];
    for (i in spec) {
        // from: https://stackoverflow.com/questions/8435183/generate-a-weighted-random-number
        // The constant 10 below should be computed based on the
        // weights in the spec for a correct and optimal table size.
        // E.g. the spec {0:0.999, 1:0.001} will break this impl.
        for (j=0; j<spec[i]*10; j++) {
            table.push(i);
        }
    }

    return table[Math.floor(Math.random() * table.length)];
}

Then you could test with this code:

public static function main():void {

    // test calculate weighted rand
    // random weighted
    var result:Array = [];
    for (var k:int = 0; k < 100; k++) {
        var rand012:String = MyUtil.weightedRand({'y': 0.8, 'n1': 0.1, 'n2': 0.1});
        result.push(rand012); // random in distribution...
    }

    logger.traceObject('result: ', result);

    // counts
    var counts:Object = {};
    var totalCounts:int = 0;
    for (var i:int = 0; i < result.length; i++) {
        counts[result[i]] = 1 + (counts[result[i]] || 0);
        totalCounts++;
    }

    logger.traceObject('counts: ', counts);

    // ratios
    var ratios:Object = {};
    for (var c:String in counts) {
        ratios[c] = counts[c] / totalCounts;
    }

    logger.traceObject('ratios: ', ratios);
}
  • logger.traceObject: public function traceObject(message:String, o:Object):void { message = message ? message : 'tracing object'; this.traceLog(message); for (var key:String in o) { this.traceLog('key: ' + key + '; value=' + o[key]); } } – 12654041 lt xem Aug 19 '17 at 05:31
1

Take a look at this article:

http://uihacker.blogspot.com/2009/09/actionscript-3-choose-random-item-from.html

You can also use Rnd.bit() to get a weighted 1 or 0 and adapt it to your situation.

rid
  • 61,078
  • 31
  • 152
  • 193