Php 如何根据百分比在数组中分布值
我正在根据构建制作一个DBZ Xenoverse 2统计分布指南,以便您在属性中获得一致的游戏性。我们的想法是,如果到了80级,您总共有332个统计/属性点要分配,并且您希望在创建构建时保持一致的点分配,那么您需要从收集的总分中收集属性的百分比,并根据每个级别的百分比分配它们。所以如果我想要一个像这样的角色:Php 如何根据百分比在数组中分布值,php,arrays,distribution,Php,Arrays,Distribution,我正在根据构建制作一个DBZ Xenoverse 2统计分布指南,以便您在属性中获得一致的游戏性。我们的想法是,如果到了80级,您总共有332个统计/属性点要分配,并且您希望在创建构建时保持一致的点分配,那么您需要从收集的总分中收集属性的百分比,并根据每个级别的百分比分配它们。所以如果我想要一个像这样的角色: Health - 30 Ki - 42 Stamina - 42 Basic Attacks - 93 Strike Supers - 0 Ki Blast Supers - 125 T
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%
LVL 1
stat array
LVL 2
stat array
...
LVL 80
stat array
因此,在第二级(因为你没有获得第一级的统计数据),你会得到两个统计点,你的属性如下所示:
Health - 0
Ki - 0
Stamina - 0
Basic Attacks - 1
Strike Supers - 0
Ki Blast Supers - 1
Health - 5
Ki - 7
Stamina - 7
Basic Attacks - 16
Strike Supers - 0
Ki Blast Supers - 22
Total - 57
而在20级,你的统计数据如下所示:
Health - 0
Ki - 0
Stamina - 0
Basic Attacks - 1
Strike Supers - 0
Ki Blast Supers - 1
Health - 5
Ki - 7
Stamina - 7
Basic Attacks - 16
Strike Supers - 0
Ki Blast Supers - 22
Total - 57
因此,最终结果如下所示:
Health - 9.03614457831325%
Ki - 12.65060240963855%
Stamina - 12.65060240963855%
Basic Attacks - 28.01204819277108%
Strike Supers - 0%
Ki Blast Supers -37.65060240963855%
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]) );
好的,为了回答这个问题,我将首先介绍理论,然后介绍PHP的实际实现
理论
因此,对于任何给定的时刻,都有当前属性值和理想属性值
对于两个属性集合,可以计算每个属性所代表的总百分比
例如,给定一个属性集合,如:
{
"foo": 2,
"baz": 1,
"bar": 3,
"foobar": 0
}
这里的总分是6,因此计算得出的每个总分的百分比为(伪JSON):
如果为每个属性集合(所需和当前)计算此百分比列表,则可以从当前中减去所需的每个值,以获得每个当前值的偏移百分比
因此,负偏移百分比表示与该偏移相关联的给定统计值低于其应位于的位置,0偏移表示其在该级别上完全均匀分布,而正偏移百分比表示偏移值高于其应位于的位置
因为我们不能分配点的一部分,所以总会有额外的点使其过高,所以我们可以安全地忽略具有正百分比偏移的属性
类似地,也可以忽略偏移百分比正好为0的属性,因为至少在当前时刻,该属性是正确分布的
所以,我们只关心具有负偏移百分比的属性,因为这些属性需要增加
要将其全部封装在一起,对于每个指定的点,我们需要计算当前属性的偏移百分比,为偏移百分比最低的属性指定一个点,并重复该过程,直到我们没有点为止,记录在该过程中指定的点的位置和数量
例子
让我们使用与OP的问题相同的示例。理想的分发属性集合是(属性名称被截断):
当前是零的集合(因为尚未指定任何点):
我们有2个点要分配
第1点
对于第一点,我们计算百分比偏移,如下所示:
{
basic: -0.28012048192771083,
ss: 0,
kbs: -0.37650602409638556,
health: -0.09036144578313253,
ki: -0.12650602409638553,
stamina: -0.12650602409638553
}
{
kbs: 1,
basic: 1
}
如果我们忽略所有的零/正值(如前所述),并按最低的负值对负值进行排序,我们将得到以下结果:
[
{ 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
(kiblast 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
(基本攻击)的负面效果最低,所以我们给它指定了最后一点
因此,我们分配的总点数如下所示:
{
basic: -0.28012048192771083,
ss: 0,
kbs: -0.37650602409638556,
health: -0.09036144578313253,
ki: -0.12650602409638553,
stamina: -0.12650602409638553
}
{
kbs: 1,
basic: 1
}
根据OP的预期结果正确分配
实施
至于PHP中的实现,我不会给出100%的工作示例,但基本思想是您需要一个名为getPointsDistributionForTarget
的函数,它获取当前属性分数、目标属性分数分布和要分配的点数
function getPointsDistributionForTarget(current, target, pointsToSpend) { ... }
在该函数中,您需要循环有点数可花费的次数,每次循环执行以下操作:
计算偏移百分比的排序数组
取顶部(0
th索引)值并:
增加关联的当前属性值并将其增加1,以及
请注意,在每个属性的汇总列表中,您将该属性增加了一个
希望这有帮助!如果您有任何问题,请告诉我。我在Node(Javascript)中有一个有效的实现,我可以提供这个实现来测试这个理论并生成示例中的数字,或者如果有帮助的话,我可以尝试将该示例转换为PHP 我怀疑有一个非常聪明的解决办法,但我想不出是什么。类似于每个级别的循环应用1*每个类别中的百分比可能向下舍入。希望有人能给你一个很好的答案。@mkaatman不幸的是,舍入将提供不准确的结果,但是,Aeolingamenfel能够快速解决这个问题。做得很好!我将在适当的时候编辑答案以提供工作示例。