Picking random element by user defined weights [du

2020-02-13 21:56发布

问题:

This question already has answers here:
Closed 7 years ago.

Possible Duplicate:
Generating random results by weight in PHP?

I have a web application where users can add 1-20 strings of text and assign a weight to them of how often it should show up. The system would then choose a random string based on the defined weights. What is the best way to go about this? Do the range values for the weight for each string matter? Could I just have the user assign a number (0-100) for each string? How would you go about choosing a random string? (Each choice doesn't worry about what was chosen before, every string has the same odds (based on weight) of being chosen at the start of each call).

回答1:

I use this function in several PHP game engines:

<?php
/**
 * @param array $values - just the weights
 * @return integer A number between 0 and count($values) - 1
 */
function getBucketFromWeights($values) {
    $total = $currentTotal = $bucket = 0;
    $firstRand = mt_rand(1, 100);

    foreach ($values as $amount) {
        $total += $amount;
    }

    $rand = ($firstRand / 100) * $total;

    foreach ($values as $amount) {
        $currentTotal += $amount;

        if ($rand > $currentTotal) {
            $bucket++;
        }
        else {
            break;
        }
    }

    return $bucket;
}

Usage

Suppose I have the user weights in an associative array where each string points to its weight:

$weighted_strings = array(
    "important string" => 100,
    "terrible string" => 10,
    "never string" => 0,
    // etc
);

If I wanted to pull a string based on weight, I'd do this:

$weights = array_values($weighted_strings);
$strings = array_keys($weighted_strings);
$index = getBucketFromWeights($weights);
$selectedString = $strings[$index];


回答2:

Here is a simple implementation:

function Probability($data, $number = 1)
{
    $result = array();

    if (is_array($data) === true)
    {
        $data = array_map('abs', $data);
        $number = min(max(1, abs($number)), count($data));

        while ($number-- > 0)
        {
            $chance = 0;
            $probability = mt_rand(1, array_sum($data));

            foreach ($data as $key => $value)
            {
                $chance += $value;

                if ($chance >= $probability)
                {
                    $result[] = $key; unset($data[$key]); break;
                }
            }
        }
    }

    return $result;
}

With this function you can specify how many unique weighted random elements you want (IDEOne).



回答3:

Nice answer is provided here, but there is a way to save on loockup loop. Faster way to select random value from array. Actually Idea is the same, just works faster as a simple loop.