PHP가 고정된 빨간색 봉투와 임의의 빨간색 봉투 알고리즘을 구현하는 방법에 대한 자세한 설명(그림)

黄舟
풀어 주다: 2023-03-14 09:46:01
원래의
4080명이 탐색했습니다.

1 요구 사항

CleverCode는 최근 고정 빨간색 봉투 + 임의 빨간색 봉투 알고리즘을 작성해 달라는 요청을 받았습니다.

1 고정 빨간 봉투는 각 빨간 봉투의 양이 동일하다는 것을 의미합니다. 고정 빨간 봉투를 수량만큼 보낼 수 있습니다.

2 랜덤 빨간봉투에 대한 수요는 입니다. 예를 들어 빨간 봉투 총액이 5위안이면 빨간 봉투 10개를 보내야 합니다. 무작위 범위는 0.01~0.99이며, 5위안을 지불해야 하며 금액은 특정 추세를 갖는 정규 분포를 가져야 합니다. (0.99는 임의로 지정하거나 avg * 2 - 0.01일 수 있습니다. 예: avg = 5 / 10 = 0.5; (avg * 2 - 0.01 = 0.99))

2 수요 분석

2.1 고정 빨간 봉투

고정된 빨간 봉투라면 알고리즘은 직선입니다. t는 빨간 봉투의 고정된 양입니다. 그림과 같습니다.
f(x) = t; (1 <= x <= num)


2.2 무작위 빨간 봉투

무작위 함수 rand를 사용하면. rand(0.01,0.99); 그런 다음 무작위로 10번, 최악의 시나리오가 금액이 0.99인 경우 총 금액은 9.9위안입니다. 5 위안 이상이 될 것입니다. 금액도 정규 분포되지 않습니다. 마지막으로, 포물선과 삼각 함수를 사용할 수 있는 임의의 빨간색 봉투 생성기로 수학 함수를 사용하는 방법을 생각했습니다. 마지막으로 이등변 삼각 선형 함수가 선택되었습니다.

1 알고리즘 원리

보내려는 빨간 봉투의 총량을 totalMoney라고 하면, 빨간 봉투의 개수는 num, 금액 범위는 [min,max]이고 일차방정식은 그림과 같습니다.


세 점의 좌표:

(x1,y1) =  (1,min)
  (x2,y2)  = (num/2,max)
  (x3,y3) = (num,min)
로그인 후 복사

결정된 선형 방정식:

$y = 1.0 * ($x - $x1) / ($x2 - $x1) * ($y2 - $y1) + $y1 ; (x1 <= x <= x2)
$y = 1.0 * ($x - $x2) / ($x3 - $x2) * ($y3 - $y2) + $y2;  (x2 <= x <= x3)
로그인 후 복사

수정된 데이터:
y(전체) = y1 + y2 + y3 +...
y(전체)는 > totalMoney일 수 있습니다. , 생성된 양이 너무 많아서 데이터를 수정해야 함을 나타냅니다. 그런 다음 매번 (y1, y2, y3...ynum)에서 0.01씩 줄입니다. y(total) = totalMoney까지.
y (함께)는 < totalMoney일 수 있으며, 이는 생성된 금액이 적고 데이터를 수정해야 함을 나타내며 (y1, y2, y3...ynum)에서 매번 0.01을 추가합니다. y(total) = totalMoney까지.

2 알고리즘 원리의 예

발행할 빨간 봉투의 총량이 11470개이고, 빨간 봉투의 개수는 7400개이며, 금액 범위는 [0.01, 3.09]이면 일차방정식은 다음과 같습니다. 그림에서.


3 요구 사항 디자인

3.1 클래스 다이어그램 디자인


3.2 소스 코드 디자인

<?php
/**
 * 随机红包+固定红包算法[策略模式]
 * copyright (c) 2016 http://blog.csdn.net/CleverCode
 */

//配置传输数据DTO
class OptionDTO
{/*{{{*/

    //红包总金额
    public $totalMoney;

    //红包数量
    public $num;

    //范围开始
    public $rangeStart;

    //范围结算
    public $rangeEnd;

    //生成红包策略
    public $builderStrategy;

    //随机红包剩余规则
    public $randFormatType; //Can_Left:不修数据,可以有剩余;No_Left:不能有剩余

    public static function create($totalMoney,$num,$rangeStart,$rangEnd,
        $builderStrategy,$randFormatType = &#39;No_Left&#39;)
    {/*{{{*/
        $self = new self();
        $self->num = $num;
        $self->rangeStart = $rangeStart;
        $self->rangeEnd = $rangEnd;
        $self->totalMoney = $totalMoney;
        $self->builderStrategy = $builderStrategy;
        $self->randFormatType = $randFormatType;
        return $self; 
    }/*}}}*/

}/*}}}*/

//红包生成器接口
interface IBuilderStrategy
{/*{{{*/
    //创建红包
    public function create();    
    //设置配置
    public function setOption(OptionDTO $option); 
    //是否可以生成红包
    public function isCanBuilder();
    //生成红包函数
    public function fx($x);
}/*}}}*/

//固定等额红包策略
class EqualPackageStrategy implements IBuilderStrategy
{/*{{{*/
    //单个红包金额
    public $oneMoney;

    //数量
    public $num;

    public function construct($option = null) 
    {
        if($option instanceof OptionDTO)
        {
            $this->setOption($option);
        }
    }

    public function setOption(OptionDTO $option)
    {
        $this->oneMoney = $option->rangeStart;
        $this->num = $option->num;
    }

    public function create() 
    {/*{{{*/

        $data = array();
        if(false == $this->isCanBuilder())
        {
            return $data;    
        }

        $data = array();
        if(false == is_int($this->num) || $this->num <= 0) 
        {
            return $data;    
        }
        for($i = 1;$i <= $this->num;$i++)
        {
            $data[$i] = $this->fx($i);
        }
        return $data;
    }/*}}}*/
    
    /**
     * 等额红包的方程是一条直线 
     * 
     * @param mixed $x 
     * @access public
     * @return void
     */
    public function fx($x) 
    {/*{{{*/
        return $this->oneMoney; 
    }/*}}}*/

    /**
     * 是否能固定红包 
     * 
     * @access public
     * @return void
     */
    public function isCanBuilder()
    {/*{{{*/
        if(false == is_int($this->num) || $this->num <= 0) 
        {
            return false;    
        }

        if(false ==  is_numeric($this->oneMoney) || $this->oneMoney <= 0)
        {
            return false;
        }

        //单个红包小于1分
        if($this->oneMoney < 0.01)
        {
            return false;
        }
        
        return true;

    }/*}}}*/


}/*}}}*/

//随机红包策略(三角形)
class RandTrianglePackageStrategy implements IBuilderStrategy
{/*{{{*/
    //总额
    public $totalMoney;

    //红包数量
    public $num;

    //随机红包最小值
    public $minMoney;

    //随机红包最大值
    public $maxMoney;

    //修数据方式:NO_LEFT: 红包总额 = 预算总额;CAN_LEFT: 红包总额 <= 预算总额
    public $formatType; 

    //预算剩余金额
    public $leftMoney;


    public function construct($option = null) 
    {/*{{{*/
        if($option instanceof OptionDTO)
        {
            $this->setOption($option);
        }
    }/*}}}*/

    public function setOption(OptionDTO $option)
    {/*{{{*/
        $this->totalMoney = $option->totalMoney;
        $this->num = $option->num;
        $this->formatType = $option->randFormatType;
        $this->minMoney = $option->rangeStart;
        $this->maxMoney = $option->rangeEnd;
        $this->leftMoney = $this->totalMoney;
    }/*}}}*/

    /**
     * 创建随机红包 
     * 
     * @access public
     * @return void
     */
    public function create() 
    {/*{{{*/
        
        $data = array();
        if(false == $this->isCanBuilder())
        {
            return $data;    
        }
        
        $leftMoney = $this->leftMoney;
        for($i = 1;$i <= $this->num;$i++)
        {
            $data[$i] = $this->fx($i);
            $leftMoney = $leftMoney - $data[$i]; 
        }

        //修数据
        list($okLeftMoney,$okData) = $this->format($leftMoney,$data);

        //随机排序
        shuffle($okData);
        $this->leftMoney = $okLeftMoney;

        return $okData;
    }/*}}}*/

    /**
     * 是否能够发随机红包 
     * 
     * @access public
     * @return void
     */
    public function isCanBuilder()
    {/*{{{*/
        if(false == is_int($this->num) || $this->num <= 0) 
        {
            return false;    
        }

        if(false ==  is_numeric($this->totalMoney) || $this->totalMoney <= 0)
        {
            return false;
        }

        //均值
        $avgMoney = $this->totalMoney / 1.0 / $this->num;
        
        //均值小于最小值
        if($avgMoney < $this->minMoney )
        {
            return false;
        }
        
        return true;

    }/*}}}*/

    /**
     * 获取剩余金额 
     * 
     * @access public
     * @return void
     */
    public function getLeftMoney()
    {/*{{{*/
        return $this->leftMoney;
    }/*}}}*/

    /**
     * 随机红包生成函数。三角函数。[(1,0.01),($num/2,$avgMoney),($num,0.01)] 
     * 
     * @param mixed $x,1 <= $x <= $this->num; 
     * @access public
     * @return void
     */
    public function fx($x)
    {/*{{{*/
        
        if(false == $this->isCanBuilder())
        {
            return 0;
        }

        if($x < 1 || $x > $this->num)
        {
            return 0;
        }
        
        $x1 = 1;
        $y1 = $this->minMoney;
        
        //我的峰值
        $y2 = $this->maxMoney;

        //中间点
        $x2 = ceil($this->num /  1.0 / 2);

        //最后点
        $x3 = $this->num;
        $y3 = $this->minMoney;  

        //当x1,x2,x3都是1的时候(竖线)
        if($x1 == $x2 && $x2 == $x3)
        {
            return $y2;
        }

        // &#39;/_\&#39;三角形状的线性方程
        //&#39;/&#39;部分
        if($x1 != $x2 && $x >= $x1 && $x <= $x2)
        {

            $y = 1.0 * ($x - $x1) / ($x2 - $x1) * ($y2 - $y1) + $y1;  
            return number_format($y, 2, &#39;.&#39;, &#39;&#39;);
        }

        //&#39;\&#39;形状
        if($x2 != $x3 && $x >= $x2 && $x <= $x3)
        {

            $y = 1.0 * ($x - $x2) / ($x3 - $x2) * ($y3 - $y2) + $y2;  
            return number_format($y, 2, &#39;.&#39;, &#39;&#39;);
        }
        
        return 0;


    }/*}}}*/

    /**
     * 格式化修红包数据 
     * 
     * @param mixed $leftMoney 
     * @param array $data 
     * @access public
     * @return void
     */
    private function format($leftMoney,array $data)
    {/*{{{*/

        //不能发随机红包
        if(false == $this->isCanBuilder())
        {
            return array($leftMoney,$data);  
        }
        
        //红包剩余是0
        if(0 == $leftMoney)
        {
            return array($leftMoney,$data);  
        }

        //数组为空
        if(count($data) < 1)
        {
            return array($leftMoney,$data);  
        }

        //如果是可以有剩余,并且$leftMoney > 0
        if(&#39;Can_Left&#39; == $this->formatType
          && $leftMoney > 0)
        {
            return array($leftMoney,$data);  
        }


        //我的峰值
        $myMax = $this->maxMoney;

        // 如果还有余钱,则尝试加到小红包里,如果加不进去,则尝试下一个。
        while($leftMoney > 0)
        {
            $found = 0;
            foreach($data as $key => $val) 
            {
                //减少循环优化
                if($leftMoney <= 0)
                {
                    break;
                }

                //预判
                $afterLeftMoney =  (double)$leftMoney - 0.01;
                $afterVal = (double)$val + 0.01;
                if( $afterLeftMoney >= 0  && $afterVal <= $myMax)
                {
                    $found = 1;
                    $data[$key] = number_format($afterVal,2,&#39;.&#39;,&#39;&#39;);
                    $leftMoney = $afterLeftMoney;
                    //精度
                    $leftMoney = number_format($leftMoney,2,&#39;.&#39;,&#39;&#39;);
                }
            }

            //如果没有可以加的红包,需要结束,否则死循环
            if($found == 0)
            {
                break;
            }
        }
        //如果$leftMoney < 0 ,说明生成的红包超过预算了,需要减少部分红包金额
        while($leftMoney < 0)
        {
            $found = 0;
            foreach($data as $key => $val) 
            {
                if($leftMoney >= 0)
                {
                    break; 
                }
                //预判
                
                $afterLeftMoney =  (double)$leftMoney + 0.01;
                $afterVal = (double)$val - 0.01;
                if( $afterLeftMoney <= 0 && $afterVal >= $this->minMoney)
                {
                    $found = 1;
                    $data[$key] = number_format($afterVal,2,&#39;.&#39;,&#39;&#39;);
                    $leftMoney = $afterLeftMoney;
                    $leftMoney = number_format($leftMoney,2,&#39;.&#39;,&#39;&#39;);
                }
            }
            
            //如果一个减少的红包都没有的话,需要结束,否则死循环
            if($found == 0)
            {
                break;
            }
        }
        return array($leftMoney,$data);  
    }/*}}}*/

}/*}}}*/

//维护策略的环境类
class RedPackageBuilder
{/*{{{*/

    // 实例  
    protected static $_instance = null;  

    /** 
     * Singleton instance(获取自己的实例) 
     * 
     * @return MemcacheOperate 
     */  
    public static function getInstance()
    {  /*{{{*/
        if (null === self::$_instance) 
        {  
            self::$_instance = new self();  
        }  
        return self::$_instance;  
    }  /*}}}*/

    /** 
     * 获取策略【使用反射】
     * 
     * @param string $type 类型 
     * @return void 
     */  
    public function getBuilderStrategy($type)
    {  /*{{{*/
        $class = $type.&#39;PackageStrategy&#39;;

        if(class_exists($class))
        {
            return new $class();  
        }
        else
        {
            throw new Exception("{$class} 类不存在!");
        }
    }  /*}}}*/

    public function getRedPackageByDTO(OptionDTO $optionDTO) 
    {/*{{{*/
        //获取策略
        $builderStrategy = $this->getBuilderStrategy($optionDTO->builderStrategy);

        //设置参数
        $builderStrategy->setOption($optionDTO);

        return $builderStrategy->create();
    }/*}}}*/
    
}/*}}}*/

class Client
{/*{{{*/
    public static function main($argv)
    {
        //固定红包
        $dto = OptionDTO::create(1000,10,100,100,&#39;Equal&#39;);
        $data = RedPackageBuilder::getInstance()->getRedPackageByDTO($dto);
        //print_r($data);

        //随机红包[修数据]
        $dto = OptionDTO::create(5,10,0.01,0.99,&#39;RandTriangle&#39;);
        $data = RedPackageBuilder::getInstance()->getRedPackageByDTO($dto);
        print_r($data);

        //随机红包[不修数据]
        $dto = OptionDTO::create(5,10,0.01,0.99,&#39;RandTriangle&#39;,&#39;Can_Left&#39;);
        $data = RedPackageBuilder::getInstance()->getRedPackageByDTO($dto);
        //print_r($data);
        
    }
}/*}}}*/

Client::main($argv);
로그인 후 복사

3.3 결과 표시

1 고정 빨간 봉투

//固定红包
$dto = OptionDTO::create(1000,10,100,100,&#39;Equal&#39;);
$data = RedPackageBuilder::getInstance()->getRedPackageByDTO($dto);
print_r($data);
로그인 후 복사


2 무작위 빨간색 봉투( 데이터 수정)

여기에서는 PHP의 무작위 정렬 기능인 shuffle($okData)이 사용되므로 표시되는 결과는 선형이 아니며 이 결과는 더 무작위입니다.

//随机红包[修数据]
 $dto = OptionDTO::create(5,10,0.01,0.99,&#39;RandTriangle&#39;);
 $data = RedPackageBuilder::getInstance()->getRedPackageByDTO($dto);
 print_r($data);
로그인 후 복사


3개의 무작위 빨간색 봉투(데이터 수정 없음)

데이터 수정 없음, 양은 1이고 num은 최소값 0.01입니다.

//随机红包[不修数据]
 $dto = OptionDTO::create(5,10,0.01,0.99,&#39;RandTriangle&#39;,&#39;Can_Left&#39;);
 $data = RedPackageBuilder::getInstance()->getRedPackageByDTO($dto);
 print_r($data);
로그인 후 복사



위 내용은 PHP가 고정된 빨간색 봉투와 임의의 빨간색 봉투 알고리즘을 구현하는 방법에 대한 자세한 설명(그림)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!