大乐透模块随机 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 协议》,转载必须注明作者和本文链接
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 3

file

file

这样确实很快

for($i=0; $i<12; $i++){
    $users = \App\Models\User::factory()->times(10000)->make();
    \App\Models\User::insert($users->toArray());
 }

我以前是下面这样慢的一匹

for($i=0; $i<12; $i++){
    \App\Models\User::factory(10000)->create();
}
5个月前 评论
猪猪

强啊铁汁,膜拜了

5个月前 评论

可以算算,回本了吗 :smirk:

5个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!