PHP 金额均衡分配算法

$Money = array(
    1,2,3,3,4,4,5,5,6,6,7,7,8,8,9,10
);

$Res = AccordingToMPA($Money, array(
		'1'=>'Admin','2'=>'张三','3'=>'庆庆','4'=>'鹏鹏',
		'5'=>'Admin','6'=>'张三','7'=>'庆庆','8'=>'鹏鹏',
	));
	
print_r($Res);

/**
  *函数名称: AccordingToMPA		作者: Dandy.Mu
  *谱写日期: 2014-09-19 14:27:05
  *函数说明: 根据金额和比例进行平均分配
  *参数说明: 
			* @Param
  *返回信息: 
  */
function AccordingToMPA(Array $Amount_pool, Array $Users)
{
	if ( !is_array($Amount_pool) || !count($Amount_pool) || !is_array($Users) || !count($Users) ) return false;
	// 不改变索引情况的排序
	asort($Amount_pool);
	// 声明存储最后返回的数据的数组
	$retData = array();
	// 计算过程中用到的临时数据存放点
	$tmpData = array(
			// 待分配数据的金额的和
			'Total_amount' => array_sum($Amount_pool),
			// 待分配数据的金额总数量 是数量 不是金额的汇总
			'The_total_number_of_money' => count($Amount_pool),
			// 待分配数据的用户总数
			'The_total_number_of_users' => count($Users),
		);
	// 第一次循环的分配是先分大的还是先分小的 true 等于先分大的
	$Direction = true;
	// 人员平均金额最大限额
	$tmpData['Maximum_limit_money'] = ceil($tmpData['Total_amount'] / $tmpData['The_total_number_of_users']);
	while ($Amount_pool)
	{
		// 最大组合数
		$Combination_maximum = floor($tmpData['The_total_number_of_money'] / $tmpData['The_total_number_of_users']);
		// 判断当前这轮循环是求大还是求小
		$Larger_or_smaller = $Direction === true ? 'Large' : 'Smaller';
		// 循环所有待分配数据的用户
		foreach ($Users as $ID => $Name)
		{
			// 预期的最大值 = 人均金额 - 当前用户已经拥有的金额数
			$Expected_maximum = $tmpData['Maximum_limit_money'] - $tmpData[$ID]['Total_amount'];
			// 分析待分配数据的Key
			$tmpAmount_pool_key = Find_similar_value($Amount_pool, $Expected_maximum, $Combination_maximum, $Larger_or_smaller);
			// 这个变成false的原因多数是因为数据已经分配完了,也就是木有了啊。 有木有
			if ( $tmpAmount_pool_key === false ) break;
			// 最终的实际返回的数据
			$retData['iD'][$ID][] = $tmpAmount_pool_key;
			$retData['Money'][$ID] += $Amount_pool[$tmpAmount_pool_key];
			// Keys 当前分配人员分到的所有的Key 	【测试的时候使用】
			// Money 当前分配人员分到的所有的金额值 【测试的时候使用】
			// Total_amount 当前分配人员的总金额	【测试的时候使用】
			$tmpData[$ID]['Keys'][] = $tmpAmount_pool_key;
			$tmpData[$ID]['Money'][] = $Amount_pool[$tmpAmount_pool_key];
			$tmpData[$ID]['Total_amount'] += $Amount_pool[$tmpAmount_pool_key];
			// 清理已经赋值的
			unset($Amount_pool[$tmpAmount_pool_key]);
			// 剩余可分配的数据减1
			--$tmpData['The_total_number_of_money'];
			// 判断如果当前用户的平均金额已经满足 就将其剔除出分配组
			if ( $tmpData[$ID]['Total_amount'] >= $tmpData['Maximum_limit_money'] ) 
			{ unset($Users[$ID]); --$tmpData['The_total_number_of_users']; }
		}
		// 逆转方向
		$Direction = $Direction === true ? false : true;
	}
	unset(
			$Amount_pool, $Users, $tmpData, $Direction, $Combination_maximum, 
			$Larger_or_smaller, $Expected_maximum, $tmpAmount_pool_key
		);
	return $retData;
}
/**
  *函数名称: Find_similar_value		作者: Dandy.Mu
  *谱写日期: 2014-09-19 14:01:07
  *函数说明: 查找符合规则的数值
  *参数说明: 
			* @Amount_pool         => 数据源也就是剩余为分配的数据的数组
			* @Expected_maximum    => 预期的最大值
			* @Combination_maximum => 最大组合数,也就是当前这个人还能分配的最大的个数
			* @Larger_or_smaller   => 期待值 求大 或者 求小,[Large(求能接受的最大值)], 不等于 Large 则为求最小值
			* @Sort                => 是否进行排序,正常情况一定在外面排序好才调用,所以默认为false
  *返回信息: 符合条件的 Amount_pool 的数组的 Key
  */
function Find_similar_value(Array $Amount_pool, $Expected_maximum, $Combination_maximum, $Larger_or_smaller = 'Large', $Sort = false)
{
	if ( !is_array($Amount_pool) || !count($Amount_pool) || !is_numeric($Expected_maximum) || !is_numeric($Combination_maximum) ) return false;
	if ( $Sort === true ) asort($Amount_pool);
	// 如果是求最小值那么返回第一个就完事了
	if ( $Larger_or_smaller !== 'Large' ) { return key($Amount_pool); }
	// 如果预期的最大组合小于2那么就是1了吗 直接返回一个最大的就O了
	if ( $Combination_maximum < 2 ) { end($Amount_pool); return key($Amount_pool); }
	// 克隆一个对象用正向循环
	$Amount_pool_clone = $Amount_pool;
	// 预期的值最大值 也就是 预期值加上组合数
	$Expected_maximum_Puls_Combination = $Expected_maximum + $Combination_maximum;
	// 真实的循环次数 因为第一个循环中已经将最大的值赋值了 所以知识正向的相加最小的而已
	$Real_Combination = --$Combination_maximum;
	while ($retData = end($Amount_pool))
	{
		// 逆向循环数组那么肯定是大到小的顺序,如果当前值大于了预期最大值就跳过循环。
		if ( $retData > $Expected_maximum_Puls_Combination ) { array_pop($Amount_pool); continue; }
		reset($Amount_pool_clone);
		for ($i=0; $i < $Real_Combination; $i++)
		{ $retData += current($Amount_pool_clone); next($Amount_pool_clone); }
		// 上面计算后的值 如果小于期待的值 就证明三个数字组合的最大的数字就是当前这个了
		if ( $retData <= $Expected_maximum )
		{ 
			$retData = key($Amount_pool); 
			unset($i, $Expected_maximum, $Combination_maximum, $Amount_pool, $Larger_or_smaller, $Sort, $Amount_pool_clone, $Expected_maximum_Puls_Combination);
			return $retData;
		}
		array_pop($Amount_pool);
	}
	// 如果以上实在没有匹配就返回一个最小的
	reset($Amount_pool_clone); $retData = key($Amount_pool_clone); 
	unset($i, $Expected_maximum, $Combination_maximum, $Amount_pool, $Larger_or_smaller, $Sort, $Amount_pool_clone, $Average_Puls_Combination);
	return $retData;
}

转载于:https://my.oschina.net/CocoFather/blog/318483

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值