生成随机加权值

Kev*_*eno 10 php c# random

编辑:我已经重写了这个问题,希望目标更加清晰.

这是对这个问题的扩展问题在这里,我真的很喜欢在所提供的功能,这个答案.

在上面的答案中,人们能够设定击中极端的概率,较高的数字产生较高数字的概率,反之亦然.问题是我必须设置3组的概率.这些组是最低值(LV),最高值(HV)和中间值(MV).但是,为了简化请求,我们可以考虑EVP=HVP=LVP.

给定任何范围,HV/LV应基于指定的EVP出现,并且当您从每个极端的范围内进展/下降时,该范围中下一个值的概率将根据EVP之间的距离增加或减少.和MVP.

使用1-6的示例范围,1和6加权为5%(EVP),概率差为1/6为5%,2/4为15%,3/4为30%(MVP) ),总计100%.反过来也应该是可能的,交换EVP和MVP应该产生下图的反转.

这是一个我希望将传达给定示例预期结果的图像.

中加权:

中等加权图

额外奖励:如果我能够单独设置HVP和LVP产生类似于下图的结果,那将是最优秀的(注意:图表不符合上述规范).

中等加权(奖金):

中等加权奖金图

谢谢!

Dav*_*nco 18

因为我今天因为流感而被困在家里:(我决定尝试为你解决这个问题.基本上你要求的是某种插值.我使用最简单的(线性的)这些是我的结果和代码.代码有点混乱,我可能会在即将到来的日子里修复它.

<?php

// this function interpolates $a to $b over $steps steps, starting from key $k
// this can be cleaned up significantly
function interpolate($a, $b, $steps, $k) {
    @$per_step = abs($a - $b)/$steps; // suppress warnings in case of division by zero
    if ($a > $b)
        $decreasing = true;
    else
        $decreasing = false;
    $final = array();
    for ($i = 1; $i <= $steps-1; ++$i) {
        if ($decreasing)
            $final[$i+$k] = $a-=$per_step; // linear interpolation
        else
            $final[$i+$k] = $a+=$per_step; // linear interpolation
    }
    return $final;
}

// this function combines probability arrays after the interpolation occurs
// this may happen multiple times, think about 1, 3, 5. interpolation would have to occur
// from 1 -> 2 -> 3, and from 3 -> 4 -> 5.
function interpolateProbabilities ($nodes) {
    $pNodes = array();
    $pNodes = $nodes;
    $keys = array_keys($nodes);
    for ($i = 0; $i < count($keys); $i++) {
        if ($keys[$i+1] - $keys[$i] != 1) {
            $pNodes += interpolate($nodes[$keys[$i]], $nodes[$keys[$i+1]], $keys[$i+1] - $keys[$i], $keys[$i]);
        }
    }
    ksort($pNodes);
    return $pNodes;
}

// this generates a weighed random value and is pretty much copy-pasted from:
// http://w-shadow.com/blog/2008/12/10/fast-weighted-random-choice-in-php/
// it's robust and re-writing it would be somewhat pointless
function generateWeighedRandomValue($nodes) {
    $weights = array_values($nodes);
    $values = array_keys($nodes);
    $count = count($values);
    $i = 0;
    $n = 0;
    $num = mt_rand(0, array_sum($weights));
    while($i < $count) {
        $n += $weights[$i];
        if($n >= $num) {
            break;
           }
        $i++;
       }
    return $values[$i];
}

// two test cases
$nodes = array( 1 => 12, 5 => 22, 9 => 31, 10 => 35); // test 1
$nodes = array( 1 => 22, 3 => 50, 6 => 2, 7 => 16, 10 => 10); // test 2
$export = array();

// run it 1000 times
for ($i = 0; $i < 1000; ++$i) {
    $export[generateWeighedRandomValue(interpolateProbabilities($nodes))]++;
}

// for copy-pasting into excel to test out distribution
print_r($export);

?>
Run Code Online (Sandbox Code Playgroud)

我认为,结果正是您正在寻找的.如果是:

$nodes = array( 1 => 12, 5 => 22, 9 => 31, 10 => 35); // test 1
Run Code Online (Sandbox Code Playgroud)

我得到了以下(最终)数组:

Array
(
    [5] => 92
    [7] => 94
    [10] => 162
    [8] => 140
    [3] => 71
    [6] => 114
    [2] => 75
    [4] => 69
    [9] => 131
    [1] => 52
)
Run Code Online (Sandbox Code Playgroud)

也就是说,1应该发生12%的时间,522%,931%和1035%的时间.让它图表: 图1

它看起来很有希望,但让我们尝试更疯狂的东西......

$nodes = array( 1 => 22, 3 => 50, 6 => 2, 7 => 16, 10 => 10); // test 2
Run Code Online (Sandbox Code Playgroud)

在这种情况下,3应该发生50%的时间,并急剧减少到6.让我们看看发生了什么!这是数组(回想起来,我应该对这些数组进行排序):

Array
(
    [4] => 163
    [7] => 64
    [2] => 180
    [10] => 47
    [1] => 115
    [5] => 81
    [3] => 227
    [8] => 57
    [6] => 6
    [9] => 60
)
Run Code Online (Sandbox Code Playgroud)

让我们看看图片:

替代文字

看起来很有效:)

我希望我能够解决你的问题(或者至少指出你正确的方向).请注意,我的代码目前有许多规定.也就是说,您提供的初始节点必须具有高达100%的概率,否则您可能会获得一些不稳定的行为.

此外,代码有点乱,但概念相对简单.其他一些很酷的东西是尝试而不是使用线性插值,使用其他类型,这将给你更有趣的结果!


算法

为了避免混淆,我将只展示算法的工作原理.我给PHP一个$node数组的形式,integer => frequency in percentage最终看起来就像array( 1 => 22, 3 => 50, 6 => 2, 7 => 16, 10 => 10)test 2从上面来的.

Test 2基本上说你想要5个控制节点分别1, 3, 6, 7, and 10以频率放置22%, 50%, 2%, 16%, and 10%.首先,我要看看到底哪里,我需要做插值.例如,我不需要在6和之间进行7,但我确实需要在13(我们需要插值2)710(我们需要插值89)之间进行.

1 -> 3具有(3 - 1) - 1 = 1步骤之间的插值应插入key[2]原始数组中.值(%对于)1 -> 3插值是abs($a - $b) / $steps它转换到的绝对值%1减去%2,除以steps + 1它,对我们来说,恰好相等14.我们需要看看函数是增加还是减少(你好微积分).如果函数增加,我们继续步骤添加%到新的插值数组,直到我们填充所有空点(如果函数正在减少,我们减去该步骤% value.因为我们只需要填充一个点,我们返回2 => 36(22 + 14 = 36).

我们结合数组,结果是(1 => 22, 2 => 36, 3 => 50, 6 => 2, 7 => 16, 10 => 10).程序内插2,这是我们没有明确声明的百分比值.

在这种情况下7 -> 10,有两个步骤,步骤百分比2来自(16-10) / (3 + 1) = 2.函数正在减少,所以我们需要2重复减去.最终的插值数组是(8 => 14, 9 => 12).我们结合了所有阵列和瞧.

下图显示绿色(初始值)和红色(插值).您可能需要"查看图像"才能清楚地看到整个事物.你会注意到我使用,±因为算法需要弄清楚我们是否应该在一段时间内增加或减少.

替代文字


这段代码应该用更多的OOP范例编写.我使用数组键玩了很多(例如,我需要传递,$k所以一旦我将它们返回它们就更容易组合数组,interpolate($a, $b, $steps, $k)因为它们会自动拥有正确的键.这只是一个PHP的特性而且回想起来,我应该可以使用它一个更易读的OOP方法.


这是我的最后一次编辑,我保证:)由于我喜欢使用Excel,这显示了一旦插入数字后百分比如何标准化.这一点很重要,特别是考虑到你的第一张照片,你所展示的内容在某种程度上是不可能的.

Test 1 替代文字 Test 2 替代文字

您会注意到百分比显着衰减以适应插值.你现实中的第二张图看起来更像是这样的:

替代文字

在这张图中,我称重1 = > 1, 5 => 98, 10 => 1,你看到了阻尼效果的极端情况.毕竟,根据定义,百分比必须加起来为100!重要的是要意识到阻尼效应与极端之间的步数成正比.