PHP Задачка: распределить поинты между пользователями
Условия задачи:
Есть такой список очков у пользователей (список отсортирован).
$list = [ 'ХОТАБЫЧ' => 600, 'ZaHaria' => 592, 'Arken' => 568, 'FERZ' => 500, 'КАМПУЧИТА' => 460, 'CatAndOr' => 452, 'МРАК' => 450, 'ShuricANik' => 450, '!kostolom!' => 407, 'ЛИГА ТЕНЕЙ' => 400, 'БАЛАСТ' => 400, 'ogirchuk' => 347, 'Хельга' => 315, 'Banan_tyan' => 313, 'ВАЛАДИУС' => 312, 'ХАНС' => 311, 'IzoLenta' => 208, 'Kugacu' => 77, ]
Есть еще 1838 «свободных» очков. Их нужно распределить так, чтобы максимально выровнять по очкам всех пользователей.
т.е. нужно получить в результате такой массив:
Array ( [ХОТАБЫЧ] => 600 [ZaHaria] => 592 [Arken] => 568 [FERZ] => 500 [КАМПУЧИТА] => 482 [CatAndOr] => 482 [МРАК] => 482 [ShuricANik] => 482 [!kostolom!] => 482 [ЛИГА ТЕНЕЙ] => 482 [БАЛАСТ] => 481 [ogirchuk] => 481 [Хельга] => 481 [Banan_tyan] => 481 [ВАЛАДИУС] => 481 [ХАНС] => 481 [IzoLenta] => 481 [Kugacu] => 481 )
Код должен уметь работать с большим количеством распределяемых очков и большим массивом пользователей.
Решение:
Самое простое решение выглядит так, подойдет если изначально дается не большой массив в который нужно распределить не большое кол-во очков (автор Campusboy):
function normilize_points( & $list, $points ){
for ( $i = 1; $i <= $points; $i ++ ) {
$index = array_search( min( $list ), $list );
++ $list[ $index ];
}
}
Решение посложнее, которое может работать с большим объемом данных (автор Kama):
function normilize_points( & $list, $points ){
$count = count( $list );
// уровняем
for( $index = 0; $index < $count; $index++ ){
$sub_array = array_slice( $list, $index );
$root_num = reset( $sub_array );
$sub_array = array_map( fn( $val ) => $root_num - $val, $sub_array );
$sum = array_sum( $sub_array );
if( $sum <= $points ){
break;
}
}
// распределим оставшееся равномерно
$left_points = $points - $sum;
$fill_evenly__fn = static function( $to_add ) use ( & $sub_array, & $left_points ){
foreach( $sub_array as & $num ){
$num += $to_add;
$left_points -= $to_add;
if( ! $left_points ){
break;
}
}
};
// пачками
if( $left_points ){
$to_add = (int) ( $left_points / count( $sub_array ) );
$fill_evenly__fn( $to_add );
}
// по 1
if( $left_points ){
$fill_evenly__fn( 1 );
}
// заполняем результат
foreach( $sub_array as $key => $add ){
$list[ $key ] += $add;
}
}