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; } }