記一次優惠券最優使用算法
先說一下業務背景。公司做的一個投資的APP,投資金額可以用優惠券抵扣。紅包面額(100,50,30,10)
優惠券使用規則:
- 優先使用大面額的紅包,即優先使用張數最少的紅包組合
- 優先使用有限制的紅包,即優先使用有限制紅包張數占比最大的組合
- 優先使用即將過期的紅包,即優先使用平均有效期最短的組合
- 選擇紅包面額之和最大的組合(面額總值≤投資額*1%)
算法嘗試
前面三個都可以通過數據庫檢索排序實現生成一個數組,最后一個就要使用程序算法實現了。
一開始有想過背包法、窮舉法。
- 背包法:說實在的,沒看懂(比較尷尬),實現是可以實現。但是無法獲取具體使用了哪張優惠券(簡單就是很難獲得優惠券的Id)
- 窮舉法:數據太多,不可控。
優惠券最優算法1.0
算出用戶本次投資最大使用優惠券總額(redAmount)、獲取用戶的優惠券列表(redCoupons)(已按前三規則排序)直接上代碼:
public function dealCoupon()
{
$redCoupons =[]; $restRedAmount = 100;
$redCouponIdLists = []; $restRedAmounts = [];
$arrCount = count($redCoupons);
for ($i=0; $i<$arrCount; $i++) {
list($redCouponIdList, $restRedAmount) = getBestCoupon($redCoupons, $restRedAmount);
if ($restRedAmount == 0) {
$bestCouponIdList = $redCouponIdList;
break;
}
$redCouponIdLists[] = $redCouponIdList;
$restRedAmounts[] = $restRedAmount;
array_shift($redCoupons);
}
if (empty($bestCouponIdList)) {
$pos = array_search(min($restRedAmounts), $restRedAmounts);
$bestCouponIdList = $redCouponIdLists[$pos];
}
}
/**
* 紅包最優算法
*/
private function getBestCoupon($redCoupons, $restRedAmount)
{
$redCouponAmount = 0;
foreach ($redCoupons as $redCoupon) {
if ($restRedAmount >= $redCoupon->getAmount()) {
$redCouponAmount = $redCouponAmount + $redCoupon->getAmount());
$redCouponIdList[] = $redCoupon->getCouponId();
$restRedAmount = $restRedAmount - $redCoupon->getAmount();
if ($restRedAmount == 0) break;
}
}
return [$redCouponIdList, $restRedAmount];
}
實例解析:用戶投資9000,用戶有優惠券(50,30,30,30,30)
- 用50塊嘗試,最多80
- 用第一個30嘗試,最多90,已經最優,中斷程序(否則繼續向下類推)
問題
- 因為每次算完就把該紅包從數組中推出,這樣還是存在問題。例如投資1600,用戶有(100,50,30,30)。這樣就不會出現最優。
解決(算法2.0)
- 不把計算過優惠券推出,需要每次把要計算那張優惠券單獨拿出來
- 這樣程序復雜了很多,但還是可以實現的
- 這樣還是會出現算法1.0的問題,不過這種按照順序取優惠券很難不出現問題。但是這種面額的優惠券出現幾率幾乎沒有
請教
- 期待有大神給出更好的算法