依據獎品權重值實現的隨機抽獎,PHP實現

一、初始數據:
權重越大,抽取的概率越高
[獎品1, 權重 5], [ 獎品2, 權重6], [ 獎品3, 權重 7], [ 獎品4, 權重2]

二、處理步驟:
1)N = 5 + 6 + 7 + 2 = 20
2)而後取1-N的隨機數M
3)界定各 獎品的權重範圍值 獎品 1 : 1-5 ; 獎品2 : 6-11; 獎品3: 12-18; 獎品4: 19-20

4) 若是M在某個獎品的權重範圍值內,標識這個獎品被抽取到 php

三、具體代碼實現 this

<?php
/**
 * 獎品
 */
class Prize {
	# ID
	public $id = null;
	# 權重
	public $weight = null;
	# 獎品名
	public $name = null;

	# 權重範圍區間起始值
	protected $start = 0;
	# 權重範圍區間結束值
	protected $end = 0;

	public function __construct($id, $weight, $name) {
		if (!$id) {
			throw new Exception('獎品ID爲空.');
		}
		$this->id = $id;
		$this->weight = $weight ? $weight : 0;
		$this->name = $name ? $name : '隨機獎品' . $id;
	}

	# id
	public function getId() {
		return $this->id;
	}

	# 權重
	public function getWeight() {
		return $this->weight;
	}

	# 設置權重範圍區間
	public function setRange($start, $end) {
		$this->start = $start;
		$this->end = $end;
	}

	# 判斷隨機數是否在權重範圍區間
	public function inRange($num) {
		return ($num >= $this->start) && ($num <= $this->end);
	}
}

/**
 * 獎品池
 */
class PrizePoll implements IteratorAggregate, Countable {
	# 獎品集
	protected $items = array();

	# 加入獎品
	public function addItem(Prize $item) {
		$this->items[$item->getId()] = $item;
		return $this;
	}

	# 刪除獎品
	public function removeItem($itemId) {
		if (isset($this->items[$itemId])) {
			unset($this->items[$itemId]);
		}
		return $this;
	}

	# 更新獎品
	public function updateItem(Prize $item) {
		if (isset($this->items[$item->getId()])) {
			$this->items[$itemId->getId()] = $item;
		}
		return $this;
	}

	# 獲取全部獎品
	public function getItems() {
		return $this->items;
	}

	# 全部全部可用獎品(若是權重爲0,說明這個獎品永遠不可能抽到)
	public function getVisibleItems() {
		$items = array();
		foreach ($this->items as $item) {
			if ($item->getWeight()) {
				$items[$item->getId()] = $item;
			}
		}
		return $items;
	}

	# Countable::count
	public function count() {
		return count($this->items);
	}

	# IteratorAggregate::getIterator()
	public function getIterator() {
		return new ArrayIterator($this->items);
	}
}

/**
 * 簡單的抽獎類
 */
class SimpleTurn {
	# 獎池
	protected $poll = null;
	
	public function __construct(PrizePoll $poll) {
		if ($poll) {
			$this->setPoll($poll);
		}
	}

	# 抽獎
	public function run(PrizePoll $poll) {
		$poll = $poll ? $poll : $this->poll;
		if ( ! $poll) {
			throw new Exception('獎池未初始化');
		}

		if ($poll->count() <= 0) {
			throw new Exception('獎池爲空');
		}

		$items = $poll->getVisibleItems();
		if (count($items) <= 0) {
			throw new Exception('獎池爲空');
		}

		$sum = 0;
		foreach ($items as $item) {
			$start = $sum + 1;
			$sum += $item->getWeight();
			$end = $sum;

			# 設置獎品的權重範圍區間
			$item->setRange($start, $end);
		}

		# 隨機數
		$rand = $this->getRandNum(1, $sum);

		# 區間段判斷
		foreach ($items as $item) {
			if ($item->inRange($rand)) {
				return $item;
			}
		}
		return null;
	}

	# 獲取隨機數
	public function getRandNum($min, $max) {
		return mt_rand($min ? $min : 1, $max);
	}

	# 設置獎池
	public function setPoll(PrizePoll $poll) {
		$this->poll = $poll;
	}
}

# 示例
try {
	$prizePoll = new PrizePoll();
	$prizePoll->addItem(new Prize(1, 5))
		->addItem(new Prize(2, 6))
		->addItem(new Prize(3, 7))
		->addItem(new Prize(4, 2));

	$turn = new SimpleTurn($prizePoll);
	$prize = $turn->run();
	var_dump($prize);
} catch (Exception $e) {
	print_r($e);
}
相關文章
相關標籤/搜索