如何根据百分比在数组中分配值

时间:2017-07-06 04:06:54

标签: php arrays distribution

我根据构建制作了DBZ Xenoverse 2统计分发指南,因此您可以在属性中获得一致的游戏玩法。我们的想法是,如果在80级你总共有332个统计/属性点要分发,并且你想在创建你的构建时保持一致的点分配,你需要从收集的总点数中收集属性的百分比并根据每个级别的百分比分配它们。所以,如果我想要一个看起来像的字符构建:

Health - 30
Ki - 42
Stamina - 42
Basic Attacks - 93
Strike Supers - 0 
Ki Blast Supers - 125
Total - 332

百分比看起来像:

Health - 9.03614457831325%
Ki - 12.65060240963855%
Stamina - 12.65060240963855%
Basic Attacks - 28.01204819277108%
Strike Supers - 0%
Ki Blast Supers -37.65060240963855%

所以在第2级(因为你不能获得第一级的统计数据)你得到两个统计点,你的属性看起来像这样:

Health - 0
Ki - 0
Stamina - 0
Basic Attacks - 1
Strike Supers - 0
Ki Blast Supers - 1

而在第20级,您的统计数据将如下所示:

Health - 5
Ki - 7
Stamina - 7
Basic Attacks - 16
Strike Supers - 0
Ki Blast Supers - 22
Total - 57

所以最终结果如下:

LVL 1
stat array

LVL 2
stat array

...

LVL 80
stat array

由于角色每个级别接收到可变数量的统计数据,我们必须拥有一个硬编码数组,并根据它改变我们的分布,以及已经使用的和应该使用的内容。

<?php
class Stats {

    public function show_level_proportions( $build_stats ) {

        $build_stat_labels = [ 'max_health', 'max_ki', 'max_stamina', 'basic_attack', 'strike_supers', 'ki_blast_supers' ];
        $build = array_combine($build_stat_labels, $build_stats);

        $stat_percents = $this->calculate_stat_percents($build);
        $get_stats_per_lvl = $this->get_stats_per_lvl($stat_percents, $build);

        return $get_stats_per_lvl;
    }

    //Stats given from levels 1-20
    private $incremental_points = [
            0, //1
            2, //2
            3, //3
            3, //4
            3, //5
            3, //6
            3, //7
            3, //8
            3, //9
            3, //10
            3, //11
            3, //12
            3, //13
            3, //14
            3, //15
            3, //16
            3, //17
            3, //18
            3, //19
            4, //20 total: 57
        ];

    private function calculate_stat_percents( $build_stats ) {
        $stat_sum = array_sum( $build_stats );

        foreach ( $build_stats as $key => $value ) {
            $calculated_stat = $this->calc_percents($stat_sum, $value);
            $stat_percentages[$key] = $calculated_stat;
        }

        return $stat_percentages;
    }

    private function calc_percents($sum, $stat){
        $product = ( $stat / $sum );
        return round( $product, 8, PHP_ROUND_HALF_UP );
    }

/*================
* This is the problem area
* I can get the percentages for the inputted stats,
* but I don't even know how to start getting and
* distributing the percentages for each level. So
* right now, it's static, only works with the input,
* and doesn't incorporate the $incremental_stats.
================*/
    private function get_stats_per_lvl($percentages, $stats){
        $stats_total = array_sum($this->incremental_points);

        foreach( $percentages as $key => $value ){
            $lvl_twenty_stats[$key] = $stats_total * $value;
            $rounded_lvl_twenty_stats[$key] = round( $lvl_twenty_stats[$key], 0, PHP_ROUND_HALF_UP );
        }

        return $rounded_lvl_twenty_stats;
    }
}

$stat_tracker = new Stats();
print_r( $stat_tracker->show_level_proportions([5, 0, 5, 20, 7, 20]) );

1 个答案:

答案 0 :(得分:2)

好的,那么,回答这个问题,我将首先回顾理论,然后是PHP中的实际实现。

理论

因此,对于任何给定时刻,您都拥有当前的属性值和理想的属性值。

对于两个属性集合,您可以计算每个属性所代表的总数的百分比。

因此,例如,给定一个属性集合,如:

{
    "foo": 2,
    "baz": 1,
    "bar": 3,
    "foobar": 0
}

此处的总分数为6,因此计算出的每个总分的百分比为(伪JSON):

{
    "foo": 33.3%,
    "baz": 16.6%,
    "bar": 50.0%,
    "foobar": 0.0%
}

如果为每个属性集合计算此百分比列表(所需和当前),则可以从当前值中减去所需的每个值,以获得每个当前值的百分比偏移量离开。

因此,负偏移百分比表示与该偏移相关联的给定stat是低于应该是,0偏移意味着它对于该级别是均匀分布的,并且是正的偏移百分比表示偏移量高于应该在的位置。

由于我们无法分配某个点的某些部分,因此总会有添加点过高的点,因此我们可以安全地忽略具有正百分比偏移的属性。

同样,也可以忽略正好为0的属性作为其百分比偏移量,至少在当前时刻,该属性是正确分布的。

所以,我们只关心具有负偏移百分比的属性,因为那些是需要递增的属性。

要将它们全部包装在一起,对于指定的每个点,我们需要计算当前属性的百分比偏移量,使用最低偏移百分比为该属性指定一个点,然后重复该过程直到我们没有积分,记录我们在此过程中分配的点数和数量。

实施例

让我们使用与OP的问题相同的例子。理想的分布属性集合是(属性名称被截断):

{
    basic: 93,
    ss: 0, 
    kbs: 125,
    health: 30,
    ki: 42,
    stamina: 42
}

当前是零的集合(因为还没有分配点):

{
    basic: 0,
    ss: 0, 
    kbs: 0,
    health: 0,
    ki: 0,
    stamina: 0
}

我们有2分。

第1点

对于第一点,我们计算百分比偏移量,如下所示:

{
  basic: -0.28012048192771083,
  ss: 0,
  kbs: -0.37650602409638556,
  health: -0.09036144578313253,
  ki: -0.12650602409638553,
  stamina: -0.12650602409638553
}

如果我们忽略所有零/正值(如前所述),并将负数按最低负数排序,我们得到这个结果:

[
  { name: 'kbs', value: -0.37650602409638556 },
  { name: 'basic', value: -0.28012048192771083 },
  { name: 'ki', value: -0.12650602409638553 },
  { name: 'stamina', value: -0.12650602409638553 },
  { name: 'health', value: -0.09036144578313253 }
]

kbs(Ki Blast Supers)作为最低偏移量,因此,我们为此分配一个点,然后转到下一个点。

第2点

再次,让我们用增加的KBS值计算百分比偏移量。请注意,由于增加,KBS现在正面

{
  basic: -0.28012048192771083,
  ss: 0,
  kbs: 0.6234939759036144,
  health: -0.09036144578313253,
  ki: -0.12650602409638553,
  stamina: -0.12650602409638553
}

然后,再次按最低负数排序,抛出零和正值。请注意,KBS现在不符合条件,因为它与其他值相比略高。

[
  { name: 'basic', value: -0.28012048192771083 },
  { name: 'ki', value: -0.12650602409638553 },
  { name: 'stamina', value: -0.12650602409638553 },
  { name: 'health', value: -0.09036144578313253 }
]

所以现在basic(基本攻击)具有最低的负数,因此我们将最终点分配给它。

因此,我们分配的总分数如下:

{
  kbs: 1,
  basic: 1
}

根据OP的预期结果正确分配。

实施

至于PHP的实现,我不会给出一个100%的工作示例,但基本的想法是你需要一个名为getPointsDistributionForTarget的函数,它取当前属性分数,目标属性分数分布,以及要分配的点数。

function getPointsDistributionForTarget(current, target, pointsToSpend) { ... }

在该功能中,您需要循环花费点数的次数,每次循环执行以下操作:

  1. 计算已排序的偏移百分比数组
  2. 获取顶部(0索引)值和:
  3. 增加关联的当前属性值并将其增加1,
  4. 请注意,在每个属性的总计运行列表中,您将该属性增加了一个
  5. 希望这有帮助!如果您有任何问题,请告诉我。我在Node(Javascript)中有一个工作实现我可以提供我曾经测试过这个理论并在示例中生成数字,或者如果它有帮助我可以尝试将该示例转换为PHP。