= $twoWan) { $prize = $twoWan; } } else if ($chuanNum == 4) { if ($prize >= $fiveWan) { $prize = $fiveWan; } } return $prize; } public function checkCloseTimeOver($closeTime) { ThrowException::isTrue(date('Y-m-d H:i:s') >= $closeTime, '投注已截止'); } /** * @param $fieldData "{"win":1.1,"draw":1.2,"loss":1.3}" * @return array|array[] */ public function maxPrizeSearchMaxOdd($fieldData) { $result = []; $max = -1; foreach ($fieldData as $field => $odd) { if ($odd > $max) { $max = $odd; $result = [$field => $odd]; } } return $result; } public function maxPrizeSumPlayOdd($oddData) { $winOdds = 0; foreach ($oddData as $playData) { foreach ($playData as $field => $odd) { $winOdds+=$odd; } } return $winOdds; } public function betsInfoInOtherBetsInfo($bigBetsInfo, $smallBetsInfo) { if (!$bigBetsInfo || !$smallBetsInfo) { return false; } $arr = array_intersect($bigBetsInfo['keys'], $smallBetsInfo['keys']); return count($arr) == count($smallBetsInfo['keys']); } public function betsInfoToPiaoTags($betsInfo) { $piaoTags = []; foreach ($betsInfo['info'] as $item) { $piaoTags[] = $item['id'] . '-' . $item['play']; } return $piaoTags; } public function betsInfoInPiaoTags($betsInfo, $piaoTagArray) { $betsInfoTag = $this->betsInfoToPiaoTags($betsInfo); $arr = array_intersect($betsInfoTag, $piaoTagArray); return count($arr) == count($betsInfoTag); } public function betsInfoInDanGuanPiaoPlay($betsInfo, $danGuanPlay) { if (count($betsInfo['info']) != 1) { return false; } foreach ($betsInfo['info'] as $item) { return $item['play'] == $danGuanPlay; } return false; } public function getMaxPassModeKey($passModeKeys) { foreach ($passModeKeys as $passKey) { $chuanArr = JclqService::getPassMode($passKey); foreach ($chuanArr as $chang => $ge) { $chuanChange = $chang .'.1'; $keysArray[$chuanChange] = $chuanChange; } } sort($keysArray); return array_pop($keysArray); } public function getZuheListFromPiaos($piaos) { $zuheList = []; foreach ($piaos as $piao) { foreach ($piao['bets'] as $bet) { $betUnique = $bet['unique']; if (@$zuheList[$betUnique]) { @$zuheList[$betUnique]['repeat_num'] += 1; } else { $bet['repeat_num'] = 1; $zuheList[$betUnique] = $bet; } } } return array_values($zuheList); } public function createPiaoV3($oddsData, $passModeKeys, $betsNum, $isJingCai = true) { sort($passModeKeys); $piaoPrepare = []; $piaoDanGuanPrepare = []; $hasDanGuan = in_array('1.1', $passModeKeys); foreach ($oddsData as $id => $oddsItem) { $piaoItem = []; foreach ($oddsItem as $play => $playItem) { $key = $id . '-'. $play; $piaoItem[$key] = $key; if ($hasDanGuan) { $piaoDanGuanPrepare[$play] = $play; } } $piaoPrepare[] = $piaoItem; } $piaoTags = Helps::getCombinationData($piaoPrepare, count($oddsData)); $piaoArray = []; $piaoDanguanArray = []; $piaoDanGuanTags = array_values($piaoDanGuanPrepare); foreach ($passModeKeys as $passModeKey) { $keyBetsData = $this->generateBetsInfo($oddsData, [$passModeKey], $betsNum); foreach ($keyBetsData as $betsItem) { if ($isJingCai && $passModeKey == '1.1') { foreach ($piaoDanGuanTags as $pk => $danGuanPlay) { $in = $this->betsInfoInDanGuanPiaoPlay($betsItem, $danGuanPlay); if ($in) { $piaoDanguanArray[$pk]['bets_list'][] = $betsItem; $piaoDanguanArray[$pk]['pass_mode'][$passModeKey] = $passModeKey; } } } else { foreach ($piaoTags as $pk => $piaoTagArray) { $in = $this->betsInfoInPiaoTags($betsItem, $piaoTagArray); if ($in) { $piaoArray[$pk]['bets_list'][] = $betsItem; $piaoArray[$pk]['pass_mode'][$passModeKey] = $passModeKey; } } } } } // 计算每张票的金额 $lastKey = $this->getMaxPassModeKey($passModeKeys); $piaoResult = []; foreach ($piaoArray as $items) { $maxPrizeBetsInfo = null; $betsInfoMaxPrize = 0; foreach ($items['bets_list'] as $betsInfo) { $betsModeKey = count($betsInfo['ids']) . '.1'; if ($betsModeKey == $lastKey) { $betKItemPrize = $this->getBetsInfoPrize($betsInfo); if ($betKItemPrize > $betsInfoMaxPrize) { $betsInfoMaxPrize = $betKItemPrize; $maxPrizeBetsInfo = $betsInfo; } } } $zhuNum = 0; $piaoPrize = 0; $betsArray = []; foreach ($items['bets_list'] as $betsInfo) { if ($this->betsInfoInOtherBetsInfo($maxPrizeBetsInfo, $betsInfo)) { $betKItemPrize = $this->getBetsInfoPrize($betsInfo); $piaoPrize += $betKItemPrize; } $betsArray[] = $betsInfo; $zhuNum += 1; } $piaoResult[] = [ 'is_single' => false, 'max_prize_bets_info' => $maxPrizeBetsInfo, 'pass_mode' => $items['pass_mode'], 'bets_num' => $betsNum, 'bets' => $betsArray, 'zhang' => ceil($betsNum/50), 'prize' => $piaoPrize, 'zhu_num' => $zhuNum, 'zhu_money' => Helps::floatFormat($zhuNum * Config::lotteryUnitPrice()), ]; } if ($isJingCai && $hasDanGuan) { foreach ($piaoDanguanArray as $items) { $zhuNum = 0; $piaoPrize = 0; $betsArray = []; foreach ($items['bets_list'] as $betsInfo) { $betKItemPrize = $this->getBetsInfoPrize($betsInfo); // $betsInfoKey = $this->getBetsInfoPlayType($betsInfo['keys'][0]); // // if ($betKItemPrize > @$danGuanMaxPrize[$betsInfoKey]) { // $danGuanMaxPrize[$betsInfoKey] = $betKItemPrize; // } $piaoPrize += $betKItemPrize; $betsArray[] = $betsInfo; $zhuNum += 1; } $piaoResult[] = [ 'is_single' => true, 'max_prize_bets_info' => null, 'pass_mode' => $items['pass_mode'], 'bets_num' => $betsNum, 'bets' => $betsArray, 'zhang' => ceil($betsNum/50), 'prize' => $piaoPrize, 'zhu_num' => $zhuNum, 'zhu_money' => Helps::floatFormat($zhuNum * Config::lotteryUnitPrice()), ]; } } return $piaoResult; } private function generateChaiPiaoV3Item($item) { $zhang = $item['zhang'] ; $betsNum = $item['bets_num']; $zhuNum = 0; $piaoPrize = 0; $betsArray = []; foreach ($item['bets'] as $k => &$betsInfo) { $betsInfo['bets_num'] = $item['bets_num']; $betKItemPrize = $this->getBetsInfoPrize($betsInfo); $piaoPrize += $betKItemPrize; $betsArray[] = $betsInfo; $zhuNum += 1; } if ($item['max_prize_bets_info']) { $item['max_prize_bets_info']['bets_num'] = $item['bets_num']; } return [ 'is_single' => $item['is_single'], 'max_prize_bets_info' => $item['max_prize_bets_info'], 'pass_mode' => $item['pass_mode'], 'bets_num' => $betsNum, 'bets' => $betsArray, 'zhang' => $zhang, 'prize' => $piaoPrize, 'zhu_num' => $zhuNum, 'zhu_money' => Helps::floatFormat($zhuNum * Config::lotteryUnitPrice()), ]; } public function chaiPiaoGenV3($piaosV3) { $endResult = []; foreach ($piaosV3 as $item) { if ($item['bets_num'] <= 50) { $endResult[] = $item; continue; } $zhang = intval($item['bets_num']/50); $endItem1 = $item; $endItem1['zhang'] = $zhang; $endItem1['bets_num'] = $zhang * 50; $endResult[] = $this->generateChaiPiaoV3Item($endItem1); if ($item['bets_num']%50 == 0) { continue; } $endItem2 = $item; $endItem2['zhang'] = 1; $endItem2['bets_num'] = $item['bets_num'] - $endItem1['bets_num']; $endResult[] = $this->generateChaiPiaoV3Item($endItem2); } return $endResult; } public function getBetsInfoPlayType($key) { $arr = explode('-', $key); array_pop($arr); return implode('-', $arr); } public function createPiaosV2($data, $passModeKeys, $betsNum, $optimizeZuheList = null) { if (!$optimizeZuheList) { return $this->createPiaoV3($data, $passModeKeys, $betsNum); } $piaos = []; foreach ($optimizeZuheList as $zuhe) { $chuan = count($zuhe['ids']); $tempZhang = intval(floor($zuhe['bets_num']/50)); if ($tempZhang > 0) { $piaos[] = [ 'is_single' => $chuan == 1, 'max_prize_bets_info' => $zuhe, 'pass_mode' => [$chuan . '.' . 1], 'bets_num' => $tempZhang * 50, 'bets' => [$zuhe], 'zhang' =>$tempZhang, 'prize' => $zuhe['all_odds'] * $tempZhang * 50 * Config::lotteryUnitPrice(), 'zhu_num' => 1, 'zhu_money' => Helps::floatFormat(1 * Config::lotteryUnitPrice()), ]; $yuBetsNum = $zuhe['bets_num']%50; $piaos[] = [ 'is_single' => $chuan == 1, 'max_prize_bets_info' => $zuhe, 'pass_mode' => [$chuan . '.' . 1], 'bets_num' => $yuBetsNum, 'bets' => [$zuhe], 'zhang' =>1, 'prize' => $zuhe['all_odds'] * $yuBetsNum * Config::lotteryUnitPrice(), 'zhu_num' => 1, 'zhu_money' => Helps::floatFormat(1 * Config::lotteryUnitPrice()), ]; } else { $tempZhang = 1; $betsNum = $zuhe['bets_num']; $piaos[] = [ 'is_single' => $chuan == 1, 'max_prize_bets_info' => $zuhe, 'pass_mode' => [$chuan . '.' . 1], 'bets_num' => $betsNum, 'bets' => [$zuhe], 'zhang' =>$tempZhang, 'prize' => $zuhe['all_odds'] * $betsNum * Config::lotteryUnitPrice(), 'zhu_num' => 1, 'zhu_money' => Helps::floatFormat(1 * Config::lotteryUnitPrice()), ]; } } return $piaos; } public function getBetsOddFromCombinationData($key, $dataOdd) { foreach ($dataOdd as $combinationData) { foreach ($combinationData as $comKey => $odd) { if ($key == $comKey) { return $odd; } } } Log::error('getBetsOddFromCombinationData error', [ 'key' => $key, 'dataOdd' => $dataOdd ]); ThrowException::run('数据错误,请重试!!'); } /** * 奖金优化 * @param $betsInfoArray * @param $type 默认平均优化 * @return mixed */ public function optimizePrize($betsInfoArray, $money, $optimizeType) { ThrowException::isTrue(!OptimizeType::hasValue($optimizeType),'无效的奖金优化方式'); if ($optimizeType == OptimizeType::AVG) { $result = $this->optimizeAvgPrize($betsInfoArray, $money); } if ($optimizeType == OptimizeType::HOT) { $result = $this->optimizeHotPrize($betsInfoArray, $money); } if ($optimizeType == OptimizeType::COLD) { $result = $this->optimizeColdPrize($betsInfoArray, $money); } usort($result, function($a, $b) { return $a['all_odds'] - $b['all_odds'] > 0 ? 1 : -1; }); return $result; } /** * 不废话,直接上公式。公式一:返奖率等于每个赔率的倒数之和的倒数。即:R% = (sum(oddn)^(-1))^(-1)公式二:胜率等于返还率除以赔率。无论你投注几个选项,都能得出其胜率对的的数组。根据你的投注选项按100%进行加权处理,就得到了实际的投注比例。用本金乘以各自的投注比例,就得到了奖金优化的结果。 作者:知乎用户 链接:https://www.zhihu.com/question/407882370/answer/2210122355 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 * @param $betsInfoArray * @param $money * @return array */ public function optimizeAvgPrize($betsInfoArray, $money) { $optimizeResult = []; foreach ($betsInfoArray as $item) { $item['bets_num'] = 1; $optimizeResult[$item['unique']] = $item; } $allocatedMoney = count($betsInfoArray) * Config::lotteryUnitPrice(); $nextMinResultId = function($array) { $nextResult = []; foreach ($array as $item) { $nextResult[$item['unique']] = $item['bets_num'] * $item['all_odds'] + $item['all_odds']; } $minResultId = array_search(min($nextResult), $nextResult); return $minResultId; }; while ($allocatedMoney < $money) { // 找到下次增加的 $allocateUnique = $nextMinResultId($optimizeResult); $optimizeResult[$allocateUnique]['bets_num'] += 1; // dd($optimizeResult); $allocatedMoney += Config::lotteryUnitPrice(); } return array_values($optimizeResult); } public function optimizeColdPrize($betsInfoArray, $money) { $minUnique = ''; $maxOdd = null; $allBeiNum = 0; foreach ($betsInfoArray as &$item) { $beiNum = ceil($money / ($item['all_odds'] * Config::lotteryUnitPrice())); $allBeiNum += $beiNum; $item['bets_num'] = $beiNum; if ($maxOdd === null) { $maxOdd = $item['all_odds']; $minUnique = $item['unique']; } if ($item['all_odds'] > $maxOdd) { $maxOdd = $item['all_odds']; $minUnique = $item['unique']; } } $useAvg = false; foreach ($betsInfoArray as &$item) { if ($item['unique'] == $minUnique) { $item['bets_num'] += ($money - ($allBeiNum * Config::lotteryUnitPrice())) / 2; if ($item['bets_num'] <= 0) { $useAvg = true; } break; } } if ($useAvg) { return $this->optimizeAvgPrize($betsInfoArray, $money); } return $betsInfoArray; } public function optimizeHotPrize($betsInfoArray, $money) { $minUnique = ''; $minOdd = null; $allBeiNum = 0; foreach ($betsInfoArray as &$item) { $beiNum = ceil($money / ($item['all_odds'] * Config::lotteryUnitPrice())); $allBeiNum += $beiNum; $item['bets_num'] = $beiNum; if ($minOdd === null) { $minOdd = $item['all_odds']; $minUnique = $item['unique']; } if ($item['all_odds'] < $minOdd) { $minOdd = $item['all_odds']; $minUnique = $item['unique']; } } $useAvg = false; foreach ($betsInfoArray as &$item) { if ($item['unique'] == $minUnique) { $item['bets_num'] += ($money - ($allBeiNum * Config::lotteryUnitPrice())) / 2; if ($item['bets_num'] <= 0) { $useAvg = true; } break; } } if ($useAvg) { return $this->optimizeAvgPrize($betsInfoArray, $money); } return $betsInfoArray; } public function avg($array=[]) { $array = [ [ 'odds' => 1,'bets_num' => 100 ] ]; $fanJiangDaoshu = 0; $allBeiShu = 0; foreach ($array as $item) { $onePrize = $item['odds'] * 2; $fanJiangDaoshu += 1/$onePrize; $allBeiShu += $item['bet_num']; } $fanJiangLv = 1/$fanJiangDaoshu; $avgResult = []; foreach ($array as &$item) { $item['avg'] = intval(round($allBeiShu * $fanJiangLv/($item['odds']*2))); } return $array; } public function betsInfoToOdds($betsInfo) { $odds = []; foreach ($betsInfo['info'] as $oddsId => $item) { $odds[$oddsId] = [ $item['play'] => [ $item['result'] => $item['odd'] ] ]; } return $odds; } public function getBetsInfoPrize($betInfo) { $combinationPrizeOdd = 1; foreach ($betInfo['info'] as $infoItem) { $combinationPrizeOdd *= $infoItem['odd']; } $oneBetsPrize = $combinationPrizeOdd * Config::lotteryUnitPrice(); $oneBetsPrize = self::getTiCaiOneBetsPrize($oneBetsPrize, $betInfo['chuan_num']); return $betInfo['bets_num'] * $oneBetsPrize; } public function fadanValid($buyMoney, $minPrize) { ThrowException::isTrue($buyMoney < 100, '发单最低金额100'); return bc_lt($minPrize, $buyMoney * 1.6); } public static function getPassMode($key) { $passMode = self::passModes(); $res = Arr::get($passMode, $key, []); // 兼容8以上的串关 if (!$res) { $tmpArr = explode('.', $key); $tmpChang = Arr::get($tmpArr, 0); if ($tmpChang > 8) { $res = [ $tmpChang => 1]; } } if (!$res) { Log::error('getPassMode error', [ 'key' => $key, ]); ThrowException::run('投注失败,请重试!!'); } return $res; } // 串关规则 public static function passModes() { $data = [ 1 => [ 1 => [1 => 1] ], 2 => [ 1 => [2 => 1,], 3 => [1 => 2, 2 => 1,] ], 3 => [ 1 => [3 => 1,], 3 => [2 => 3,], 4 => [2 => 3, 3 => 1,], 6 => [1 => 3, 2 => 3,], 7 => [1 => 3, 2 => 3, 3 => 1,], ], 4 => [ 1 => [4 => 1], 4 => [3 => 4,], 5 => [3 => 4, 4 => 1,], 6 => [2 => 6,], 10 => [1 => 4, 2 => 6,], 11 => [2 => 6, 3 => 4, 4 => 1,], 14 => [1 => 4, 2 => 6, 3 => 4,], 15 => [1 => 4, 2 => 6, 3 => 4, 4 => 1,], ], 5 => [ 1 => [5 => 1], 5 => [4 => 5], 6 => [4 => 5, 5 => 1], 10 => [2 => 10], 15 => [1 => 5, 2 => 10], 16 => [3 => 10, 4 => 5, 5 => 1], 20 => [2 => 10, 3 => 10], 25 => [1 => 5, 2 => 10, 3 => 10], 26 => [2 => 10, 3 => 10, 4 => 5, 5 => 1], 30 => [1 => 5, 2 => 10, 3 => 10, 4 => 5], 31 => [1 => 5, 2 => 10, 3 => 10, 4 => 5, 5 => 1], ], 6 => [ 1 => [6 => 1], 6 => [5 => 6], 7 => [5 => 6, 6 => 1], 15 => [2 => 15], 20 => [3 => 20], 21 => [1 => 6, 2 => 15], 22 => [4 => 15, 5 => 6, 6 => 1], 35 => [2 => 15, 3 => 20], 41 => [1 => 6, 2 => 15, 3 => 20], 42 => [3 => 20, 4 => 15, 5 => 6, 6 => 1], 50 => [2 => 15, 3 => 20, 4 => 15], 56 => [1 => 6, 2 => 15, 3 => 20, 4 => 15], 57 => [2 => 15, 3 => 20, 4 => 15, 5 => 6, 6 => 1], 62 => [1 => 6, 2 => 15, 3 => 20, 4 => 15, 5 => 6], 63 => [1 => 6, 2 => 15, 3 => 20, 4 => 15, 5 => 6, 6 => 1], ], 7 => [ 1 => [7 => 1], 7 => [6 => 1], 8 => [6 => 1, 7 => 1], 21 => [5 => 21], 35 => [4 => 35], 120 => [2 => 21, 3 => 35, 4 => 35, 5 => 21, 6 => 1, 7 => 1], 127 => [1 => 7, 2 => 21, 3 => 35, 4 => 35, 5 => 21, 6 => 1, 7 => 1], ], 8 => [ 1 => [8 => 1], 8 => [7 => 1], 9 => [7 => 1, 8 => 1], 28 => [6 => 28], 56 => [5 => 56], 70 => [4 => 70], 247 => [2 => 28, 3 => 56, 4 => 70, 5 => 56, 6 => 28, 7 => 1, 8 => 1], 255 => [1 => 8, 2 => 28, 3 => 56, 4 => 70, 5 => 56, 6 => 28, 7 => 1, 8 => 1], ] ]; return $data; } public function canCreateFanDanOrder($customerId) { ThrowException::isTrue(!Helps::genDanEnable(), '系统维护中...'); $fadanNum = Order::usable() ->where('type', OrderType::FADAN) ->where('customer_id', $customerId) ->where('pay_state', PayState::SUCCESS) ->where('odds_late_close_time', '>', date('Y-m-d H:i:s')) ->count(); ThrowException::isTrue($fadanNum >= 3, '发单数量超限,请稍后再试'); } // 是否可以继续创建合买 public function canCreateUnionOrder($customerId) { // $existFadan = Order::where('type', OrderType::UNION) // ->where('customer_id', $customerId) // ->where('customer_id', $customerId) // ->where('pay_state', PayState::SUCCESS) // ->whereIn('lottery_state', [LottState::WAIT, LottState::DRAFT, LottState::PENDING]) // ->count(); // // ThrowException::isTrue($existFadan > 0, '已有未开奖的合买,不可再次创建合买'); } }