大乐透模块随机 12w 条数据 3 秒这件事!
看到这篇文章关于大乐透,对于随机数所产生的中奖概率还是很有兴趣的,但是作者说超时,这个肯定可以再优化,加上自己看了之后觉得可以用“空间换时间”解决!
具体代码如下:
测试结果大概 3秒左右,12w 条数据大概 30MiB 可以接受!
结果“”一等奖:0 二等奖:0 三等奖:0 四等奖:1 五等奖:22 六等奖:26 七等奖:48 八等奖:516 九等奖:6533 “” 可见概率还是很低的。
helper 函数
增加随机数和快速排序方法
if (!function_exists("range_num")) {
function range_num($start_num = 1, $end_num = 12, $num = 6)
{
$numbers = range($start_num, $end_num);
//shuffle 将数组顺序随即打乱
shuffle($numbers);
//array_slice 取该数组中的某一段
$result = array_slice($numbers, 0, $num);
return $result;
}
}
if (!function_exists("sortQuickArr")) {
function sortQuickArr($array)
{
if (count($array) < 1) {
return $array;
}
//快速排序理论上可以选取任意一点作为 pivot,但是那是理论,这个案例只能取最后1个或者最后一个,因为如果无限二分的时候出现数组个数小于坐标就会报错!
$k = $array[0];
$count = count($array);
$left = $right = [];
$center = [$k];
for ($i = 0; $i < $count; $i++) {
if ($array[$i] < $k) {
$left[] = $array[$i];
} elseif ($array[$i] > $k) {
$right[] = $array[$i];
}
}
$left = sortQuickArr($left);
$right = sortQuickArr($right);
$sum = array_merge($left, $center, $right);
return $sum;
}
}
数据表
CREATE TABLE `lottery_ball`(
`id` int(11) NOT NULL AUTO_INCREMENT,
`lottery_num` varchar(30) NOT NULL COMMENT '期号' ,
`user_id` int (11) DEFAULT NULL COMMENT '用户id',
`front_area` varchar (30) NOT NULL COMMENT '前区',
`after_area` varchar (30) NOT NULL COMMENT '后区',
`is_open` tinyint(1) NOT NULL DEFAULT 0 COMMENT '0未开奖 1开奖' ,
`level` tinyint(1) NOT NULL DEFAULT 0 COMMENT '0未中奖1-等奖 2二等奖 3三等奖 4四等奖 5五等奖 6六等奖 7七等奖 8八等奖 9九等奖 ',
`date` date NOT NULL,
`red1` tinyint(1) NOT NULL DEFAULT 0 COMMENT '红球1' ,
`red2` tinyint(1) NOT NULL DEFAULT 0 COMMENT '红球2' ,
`red3` tinyint(1) NOT NULL DEFAULT 0 COMMENT '红球3' ,
`red4` tinyint(1) NOT NULL DEFAULT 0 COMMENT '红球4' ,
`red5` tinyint(1) NOT NULL DEFAULT 0 COMMENT '红球5' ,
`blue1` tinyint(1) NOT NULL DEFAULT 0 COMMENT '篮球1' ,
`blue2` tinyint(1) NOT NULL DEFAULT 0 COMMENT '篮球2' ,
`blue_num` tinyint(1) NOT NULL DEFAULT 0 COMMENT '开奖篮球数目' ,
`red_num` tinyint(1) NOT NULL DEFAULT 0 COMMENT '开奖红球数据' ,
`created_at` timestamp NOT NULL,
`updated_at` timestamp NOT NULL,
PRIMARY KEY(`id`),
KEY `front_area` (`front_area`),
KEY `after_area` (`after_area`),
KEY `front_area_2` (`front_area`,`after_area`),
KEY `user_id`(`user_id`),
KEY `date` (`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='随机大乐透号码';
模型
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class LotteryBall extends Model
{
use HasFactory;
protected $table = 'lottery_ball';
protected $fillable = ['lottery_num','user_id','front_area','after_area','is_open','level','red1','red2','red3','red4','red5'
,'blue1','blue2','blue_num','red_num','created_at','updated_at'];
}
假数据
factory
<?php
namespace Database\Factories;
use App\Models\LotteryBall;
use Illuminate\Database\Eloquent\Factories\Factory;
class LotteryBallFactory extends Factory
{
protected $model = LotteryBall::class;
public function definition()
{
$sentence = $this->faker->sentence();
$red = range_num(1, 35, 5);
$blue = range_num(1, 12, 2);
$red = sortQuickArr($red);
$blue = sortQuickArr($blue);
return [
'lottery_num' => '16546fd1fd51g',
'user_id' => $this->faker->randomElement([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
'front_area' => implode(" ", $red),
'after_area' => implode(" ", $blue),
'red1' => $red[0],
'red2' => $red[1],
'red3' => $red[2],
'red4' => $red[3],
'red5' => $red[4],
'blue1' => $blue[0],
'blue2' => $blue[1],
'date' => date("Y-m-d"),
];
}
}
批量
<?php
namespace Database\Seeders;
use App\Models\LotteryBall;
use Illuminate\Database\Seeder;
/**
* php artisan db:seed --class=LotteryBallSeeder
* Class LotteryBallSeeder
* @package Database\Seeders
*/
class LotteryBallSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
//
foreach (range(1, 90) as $index) {
$users = LotteryBall::factory()->times(1000)->make();
\App\Models\LotteryBall::insert($users->toArray());
}
}
}
测试服务
<?php
namespace App\Services;
use App\Models\LotteryBall;
/**
* 大乐透逻辑
*
* Class LotteryBallService
* @package App\Services
*/
class LotteryBallService
{
/**
* 模拟开奖
*/
public function runPrice()
{
$red = [13, 20, 27, 29, 30];
$blue = [01, 07];
// 如果多次测试就加上这句,初始化计数,多 0.5 秒时间
$this->initNum();
$this->draw_prize($red, $blue);
}
/**
* 开奖
* @param array $red
* @param array $blue
*/
public function draw_prize($red = [], $blue = [])
{
for ($j = 1; $j <= 5; $j++) {
$n1 = "red" . strval($j);
LotteryBall::whereIn($n1, $red)->increment("red_num");
}
for ($j = 1; $j <= 2; $j++) {
$n1 = "blue" . strval($j);
LotteryBall::whereIn($n1, $blue)->increment("blue_num");
}
}
/**
* 中奖结果设置
*/
public function setLevel()
{
$getPrice = [
['red_num' => 5, 'blue_num' => 2, 'level' => 1],
['red_num' => 5, 'blue_num' => 1, 'level' => 2],
['red_num' => 5, 'blue_num' => 0, 'level' => 3],
['red_num' => 4, 'blue_num' => 2, 'level' => 4],
['red_num' => 4, 'blue_num' => 1, 'level' => 5],
['red_num' => 3, 'blue_num' => 2, 'level' => 6],
['red_num' => 4, 'blue_num' => 0, 'level' => 7],
['red_num' => 3, 'blue_num' => 1, 'level' => 8],
['red_num' => 3, 'blue_num' => 0, 'level' => 9],
['red_num' => 2, 'blue_num' => 1, 'level' => 9],
['red_num' => 1, 'blue_num' => 2, 'level' => 9],
];
foreach ($getPrice as $value) {
LotteryBall::where("red_num", $value['red_num'])->where("blue_num", $value['blue_num'])->update(["level" => $value['level']]);
}
}
/**
* 中奖结果查询
*/
public function show_getPrice()
{
$arr = ['一', '二', '三', '四', '五', '六', '七', '八', '九'];
$res = '';
for ($j = 1; $j <= 9; $j++) {
$co = LotteryBall::where("level", $j)->count();
$res .= $arr[$j - 1] . "等奖:" . strval($co) . " ";
}
dump($res);
return $res;
}
/**
* 把中奖数目初始化
*/
public function initNum()
{
LotteryBall::where("id", ">", 0)->update(["red_num" => 0, "blue_num" => 0]);
}
}
运行命令
<?php
namespace App\Console\Commands;
use App\Services\LotteryBallService;
use Illuminate\Console\Command;
/**
* php artisan lottery:send
*
* Class SendLottery
* @package App\Console\Commands
*/
class SendLottery extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'lottery:send';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$stime=microtime(true);
$obj = new LotteryBallService();
// return $obj->initNum();
$obj->runPrice();
$obj->setLevel();
$obj->show_getPrice();
$etime=microtime(true);//获取程序执行结束的时间
$total=$etime-$stime; //计算差值
dump("ok, ".$total." 秒");
return Command::SUCCESS;
}
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
这样确实很快
我以前是下面这样慢的一匹
强啊铁汁,膜拜了
可以算算,回本了吗 :smirk: