对于随机数是否随机问题的讨论

# 随机数测试

本次随机测试主要测试以下两个指标:

1、随机结果是否满足期望,即:100 次抽取如果 10% 的概率中奖,那么抽 100 次能中间 10 次吗

2、两次相同结果之间的出现间隔是否满足或者接近一个概率周期,即:如果 10% 概率中奖,那么每 10 次就会中奖一次

# Random.1

直接用整数随机接口

@classmethod
	def _random_int(cls, start, end):
		"""整数随机, range[start, end]"""
		return random.randint(start, end)

# Random.2

随机浮点数,再转整数

@classmethod
	def _random_float(cls, start, end):
		"""浮点随机, range[start, end]"""
		float_random = random.random()
		float_random = float_random * (end + 1 - start) + start
		return int(float_random)

# Random.3

先随机整数集合,再逐个抽取

@classmethod
	def _random_choice(cls, start, end):
		"""洗牌算法的有限集抽取, range[start, end]"""
		if not cls.CHOICE_POOL:
			cls.CHOICE_POOL = list(range(start, end + 1))
			random.shuffle(cls.CHOICE_POOL)
		return cls.CHOICE_POOL.pop(0)

# Random.4

先正太分布随机抽取期望,再逐个抽取,本算法参考 https://huangwei.pro/2015-07/game-random/

@classmethod
	def _random_gass(cls, start, end):
		"""抽取期望的正太分布随机, range[start, end]"""
		total = end + 1 - start
		expect_cnt_lst = [(random.normalvariate(total, total / 3), _id)
		                  for _id in range(start, end + 1)]
		heapq.heapify(expect_cnt_lst)
		_, _id = heapq.heappop(expect_cnt_lst)
		return _id

# 结论

针对随机进行了四种测试

  • 随机整数 random.randint
  • 随机浮点数转整数 random.random
  • 洗牌算法限定池随机整数 random.shuffle + pool
  • 正太分布抽取期望随机 random.normalvariate or random.gauss

# 结果

  • case 1:同概率集合单词抽取 1 个,执行 N 次。

    • ①、② 算法在 N 足够大(> total * 100)时期望符合要求
    • ③、④ 算法在 N 较大(> total * 10)时期望符合要求
    • ③ 在 N 足够大(> total * 100)满足频率正太分布
  • case 2:同概率集合单词抽取 N 个,执行 1 次。

    • ①、② 算法在 N 足够大(> total * 100)时期望符合要求
    • ③、④ 算法在 N 较大(> total * 10)时期望符合要求
    • ③、④ 在 N 足够大(> total * 100)时频率接近正太分布
  • case 3:加权集合单词抽取 1 个,执行 N 次。

    • ①、②、③ 在 N 足够大(> total * 100)时期望符合要求
    • ④ 在有权值较大项的情况下会放大较大项的出现概率,不满足期望
    • ③ 在 N 足够大(> total * 100)满足频率正太分布
  • case 4:加权集合单词抽取 N 个,执行 1 次。

    • ①、② 算法在 N 足够大(> total * 100)时期望符合要求
    • ③、④ 算法在 N 较大(> total * 10)时期望符合要求
    • ③、④ 在 N 足够大(> total * 10)时频率接近正太分布

# 完整代码

import unittest
import random
import time
import math
from collections import Counter
import heapq
import matplotlib.pyplot as plt
"""
本次随机测试主要测试以下两个指标:
1、随机结果是否满足期望,即:100次抽取如果10%的概率中奖,那么是否确实如此
2、两次相同结果之间的出现间隔是否满足或者接近一个概率周期,即:如果10%概率中奖,那么基本上没间隔10次就会中奖一次
直接说结果吧,针对随机进行了四种测试
①、随机整数 				random.randint
②、随机浮点数转整数			random.random
③、洗牌算法限定池随机整数	random.shuffle + pool
④、正太分布抽取期望随机		random.normalvariate or random.gauss
case 1:同概率集合单词抽取 1 个,执行 N 次。
	①、② 算法在 N 足够大(> total * 100)时期望符合要求
	③、④ 算法在 N 较大(> total * 10)时期望符合要求
	③ 在 N 足够大(> total * 100)满足频率正太分布
case 2:同概率集合单词抽取 N 个,执行 1 次。
	①、② 算法在 N 足够大(> total * 100)时期望符合要求
	③、④ 算法在 N 较大(> total * 10)时期望符合要求
	③、④ 在 N 足够大(> total * 100)时频率接近正太分布
case 3:加权集合单词抽取 1 个,执行 N 次。
	①、②、③ 在 N 足够大(> total * 100)时期望符合要求
	④ 在有权值较大项的情况下会放大较大项的出现概率,不满足期望
	③ 在 N 足够大(> total * 100)满足频率正太分布
case 4:加权集合单词抽取 N 个,执行 1 次。
	①、② 算法在 N 足够大(> total * 100)时期望符合要求
	③、④ 算法在 N 较大(> total * 10)时期望符合要求
	③、④ 在 N 足够大(> total * 10)时频率接近正太分布
"""
def gen_random_seed():
	seed = list(str(int(time.time() * 7777777773)))
	random.shuffle(seed)
	seed = int("".join(seed))
	print(f"random seed {seed}")
	return seed
class RandomTestCase(unittest.TestCase):
	RANDOM_SEED = gen_random_seed()
	CHOICE_POOL = None
	# ================= random once =================
	@classmethod
	def _random_choice(cls, start, end):
		global xxx
		"""洗牌算法的有限集抽取, range[start, end]"""
		if not cls.CHOICE_POOL:
			cls.CHOICE_POOL = list(range(start, end + 1))
			random.shuffle(cls.CHOICE_POOL)
		return cls.CHOICE_POOL.pop(0)
	@classmethod
	def _random_float(cls, start, end):
		"""浮点随机, range[start, end]"""
		float_random = random.random()
		float_random = float_random * (end + 1 - start) + start
		return int(float_random)
	@classmethod
	def _random_int(cls, start, end):
		"""整数随机, range[start, end]"""
		return random.randint(start, end)
	@classmethod
	def _random_gass(cls, start, end):
		"""抽取期望的正太分布随机, range[start, end]"""
		total = end + 1 - start
		expect_cnt_lst = [(random.normalvariate(total, total / 3), _id)
		                  for _id in range(start, end + 1)]
		heapq.heapify(expect_cnt_lst)
		_, _id = heapq.heappop(expect_cnt_lst)
		return _id
	# ================= random multi =================
	@classmethod
	def _random_choice_multi(cls, random_cnt, start, end):
		"""洗牌算法的有限集抽取, range[start, end]"""
		result_lst = []
		for _ in range(random_cnt):
			if not cls.CHOICE_POOL:
				cls.CHOICE_POOL = list(range(start, end + 1))
				random.shuffle(cls.CHOICE_POOL)
			random_number = cls.CHOICE_POOL.pop(0)
			result_lst.append(random_number)
		return result_lst
	@classmethod
	def _random_float_multi(cls, random_cnt, start, end):
		"""浮点随机, range[start, end]"""
		result_lst = []
		for _ in range(random_cnt):
			float_random = random.random()
			float_random = float_random * (end + 1 - start) + start
			result_lst.append(int(float_random))
		return result_lst
	@classmethod
	def _random_int_multi(cls, random_cnt, start, end):
		"""整数随机, range[start, end]"""
		return [random.randint(start, end) for _ in range(random_cnt)]
	@classmethod
	def _random_gass_multi(cls, random_cnt, start, end):
		"""抽取期望的正太分布随机, range[start, end]"""
		total = end + 1 - start
		result_lst = []
		expect_cnt_lst = [(random.normalvariate(total, total / 3), _id)
		                  for _id in range(start, end + 1)]
		heapq.heapify(expect_cnt_lst)
		for _ in range(random_cnt):
			min_expect_cnt, _id = heapq.heappop(expect_cnt_lst)
			result_lst.append(_id)
			heapq.heappush(expect_cnt_lst,
			               (random.normalvariate(total, total / 3) + min_expect_cnt, _id))
		return result_lst
	# ================= random weight =================
	@classmethod
	def _random_choice_weight_1(cls, weight_map):
		"""洗牌算法的有限集抽取带权重, range[start, end]"""
		random_number = 0
		if not cls.CHOICE_POOL:
			result_lst = []
			for _id, weight in weight_map.items():
				result_lst += [_id] * weight
			cls.CHOICE_POOL = result_lst
			random.shuffle(cls.CHOICE_POOL)
		random_number = cls.CHOICE_POOL.pop(0)
		return random_number
	@classmethod
	def _random_choice_weight_2(cls, weight_map):
		"""洗牌算法的有限集抽取带权重, range[start, end]"""
		return random.choices(list(weight_map.keys()), weights=list(weight_map.values()))[0]
	@classmethod
	def _random_float_weight(cls, weight_map):
		"""浮点随机带权重, range[start, end]"""
		total = sum(weight_map.values())
		float_random = random.random() * total
		result = 0
		for _id, weight in weight_map.items():
			result = _id
			if weight < float_random:
				float_random -= weight
			else:
				break
		return result
	@classmethod
	def _random_int_weight(cls, weight_map):
		"""整数随机带权重, range[start, end]"""
		total = sum(weight_map.values())
		int_random = random.randint(0, total - 1)
		result = 0
		for _id, weight in weight_map.items():
			result = _id
			if weight <= int_random:
				int_random -= weight
			else:
				break
		return result
	@classmethod
	def _random_gass_weight(cls, weight_map):
		"""抽取期望的正太分布随机, range[start, end]"""
		total = sum(weight_map.values())
		expect_cnt_lst = [(random.normalvariate(total / weight, total / 3 / weight), _id)
		                  for _id, weight in weight_map.items()]
		heapq.heapify(expect_cnt_lst)
		_, _id = heapq.heappop(expect_cnt_lst)
		return _id
	# ================= random multi weight =================
	@classmethod
	def _random_choice_multi_weight_1(cls, weight_map, cnt):
		"""洗牌算法的有限集抽取带权重, range[start, end]"""
		result = []
		for _ in range(cnt):
			if not cls.CHOICE_POOL:
				result_lst = []
				for _id, weight in weight_map.items():
					result_lst += [_id] * weight
				random.shuffle(result_lst)
				cls.CHOICE_POOL = result_lst
				print(f"cls.CHOICE_POOL {cls.CHOICE_POOL}")
			random_number = cls.CHOICE_POOL.pop(0)
			result.append(random_number)
		return result
	@classmethod
	def _random_choice_multi_weight_2(cls, weight_map, cnt):
		"""抽取带权重, range[start, end]"""
		return random.choices(list(weight_map.keys()), weights=list(weight_map.values()), k=cnt)
	@classmethod
	def _random_float_multi_weight(cls, weight_map, cnt):
		"""浮点随机带权重, range[start, end]"""
		total = sum(weight_map.values())
		result_lst = []
		for _ in range(cnt):
			float_random = random.random() * total
			result = 0
			for _id, weight in weight_map.items():
				result = _id
				if weight < float_random:
					float_random -= weight
				else:
					break
			result_lst.append(result)
		return result_lst
	@classmethod
	def _random_int_multi_weight(cls, weight_map, cnt):
		"""整数随机带权重, range[start, end]"""
		total = sum(weight_map.values())
		result_lst = []
		for _ in range(cnt):
			int_random = random.randint(0, total - 1)
			result = 0
			for _id, weight in weight_map.items():
				result = _id
				if weight <= int_random:
					int_random -= weight
				else:
					break
			result_lst.append(result)
		return result_lst
	@classmethod
	def _random_gass_multi_weight(cls, weight_map, cnt):
		"""抽取期望的正太分布随机, range[start, end]"""
		total = sum(weight_map.values())
		expect_cnt_lst = [(random.normalvariate(total / weight, total / 3 / weight), _id)
		                  for _id, weight in weight_map.items()]
		heapq.heapify(expect_cnt_lst)
		result_lst = []
		for _ in range(cnt):
			min_expect_cnt, _id = heapq.heappop(expect_cnt_lst)
			result_lst.append(_id)
			heapq.heappush(
			    expect_cnt_lst,
			    (random.normalvariate(total / weight_map[_id], total / weight_map[_id] / 3) +
			     min_expect_cnt, _id))
		return result_lst
	@classmethod
	def _draw_expect(cls, result_map, color):
		"""统计所有抽取的结果"""
		cls._draw_map(result_map, color)
	@classmethod
	def _draw_simple_interval(cls, result_lst, random_simple, color):
		"""统计两次抽取到相同数据的间隔"""
		interval = 0
		interval_lst = []
		for number in result_lst:
			interval += 1
			if number == random_simple:
				interval_lst.append(interval)
				interval = 0
		cls._draw_list(interval_lst, color)
	@classmethod
	def _draw_simple_interval_distribution(cls, result_lst, random_simple, color):
		"""间隔分布统计"""
		distribute_count = Counter()
		interval = 0
		for number in result_lst:
			interval += 1
			if number == random_simple:
				distribute_count[interval] += 1
				interval = 0
		cls._draw_map_bar(distribute_count, color)
	@classmethod
	def _draw_list(cls, result_lst, color):
		plt.xlim(0, len(result_lst))
		plt.ylim(math.floor(min(result_lst)) - 1, math.ceil(max(result_lst)) + 1)
		x = list(range(len(result_lst)))
		y = result_lst
		plt.scatter(x, y, color=color)
	@classmethod
	def _draw_map(cls, result_map, color):
		plt.xlim(min(result_map.keys()) - 1, max(result_map.keys()) + 1)
		plt.ylim(math.floor(min(result_map.values())) - 1, math.ceil(max(result_map.values())) + 1)
		x = list(result_map.keys())
		y = list(result_map.values())
		plt.scatter(x, y, color=color)
	@classmethod
	def _draw_map_bar(cls, result_map, color):
		plt.xlim(min(result_map.keys()) - 1, max(result_map.keys()) + 1)
		plt.ylim(math.floor(min(result_map.values())) - 1, math.ceil(max(result_map.values())) + 1)
		x = list(result_map.keys())
		y = list(result_map.values())
		plt.bar(x, y, color=color)
	def test_random_once(self):
		"""单次均等权重抽取 1 个,执行 N 次"""
		random.seed(self.RANDOM_SEED)
		random_start, random_simple, random_end, random_cnt = 0, 6, 9, 100000
		random_handler = {
		    self._random_float: "yellow",
		    self._random_int: "blue",
		    self._random_choice: "red",
		    self._random_gass: "green",
		}
		for handler, color in random_handler.items():
			name = handler.__name__
			result_lst = []
			for _ in range(random_cnt):
				result_lst.append(handler(random_start, random_end))
			expect_val = sum(val * cnt for val, cnt in Counter(result_lst).items())
			cnt_total = sum(cnt for cnt in Counter(result_lst).values())
			print(f"handler: {name} {Counter(result_lst)} {expect_val} {cnt_total}")
			self._draw_expect(Counter(result_lst), color)
			plt.savefig(f'./logic/at/random_expect/{name}_expect.jpg')
			plt.clf()
			self._draw_simple_interval(result_lst, random_simple, color)
			plt.savefig(f'./logic/at/random_simple_interval/{name}_simple_interval.jpg')
			plt.clf()
			self._draw_simple_interval_distribution(result_lst, random_simple, color)
			plt.savefig(f'./logic/at/simple_interval_distribution/{name}_interval_distribution.jpg')
			plt.clf()
	def test_random_once_weight(self):
		"""单次加权权重抽取 1 个,执行 N 次"""
		random.seed(self.RANDOM_SEED)
		weight_map, random_simple, random_cnt = {1: 1, 2: 8, 3: 1}, 1, 1000
		random_handler = {
		    self._random_float_weight: "yellow",
		    self._random_int_weight: "blue",
		    self._random_choice_weight_1: "red",
		    self._random_gass_weight: "green",
		}
		for handler, color in random_handler.items():
			name = handler.__name__
			result_lst = []
			for _ in range(random_cnt):
				result_lst.append(handler(weight_map))
			expect_val = sum(val * cnt for val, cnt in Counter(result_lst).items())
			cnt_total = sum(cnt for cnt in Counter(result_lst).values())
			print(f"handler: {name} {expect_val} {cnt_total} {Counter(result_lst)}")
			self._draw_expect(Counter(result_lst), color)
			plt.savefig(f'./logic/at/random_expect/{name}_expect.jpg')
			plt.clf()
			self._draw_simple_interval(result_lst, random_simple, color)
			plt.savefig(f'./logic/at/random_simple_interval/{name}_simple_interval.jpg')
			plt.clf()
			self._draw_simple_interval_distribution(result_lst, random_simple, color)
			plt.savefig(f'./logic/at/simple_interval_distribution/{name}_interval_distribution.jpg')
			plt.clf()
	def test_random_multi(self):
		"""单次均等权重抽取 N 个,执行 1 次"""
		random.seed(self.RANDOM_SEED)
		random_start, random_simple, random_end, random_cnt = 0, 3, 9, 100
		random_handler = {
		    self._random_float_multi: "yellow",
		    self._random_int_multi: "blue",
		    self._random_choice_multi: "red",
		    self._random_gass_multi: "green",
		}
		for handler, color in random_handler.items():
			name = handler.__name__
			result_lst = handler(random_cnt, random_start, random_end)
			expect_val = sum(val * cnt for val, cnt in Counter(result_lst).items())
			cnt_total = sum(cnt for cnt in Counter(result_lst).values())
			print(f"handler: {name} {expect_val} {cnt_total} {Counter(result_lst)}")
			self._draw_expect(Counter(result_lst), color)
			plt.savefig(f'./logic/at/random_expect/{name}_random_expect.jpg')
			plt.clf()
			self._draw_simple_interval(result_lst, random_simple, color)
			plt.savefig(f'./logic/at/random_simple_interval/{name}_simple_interval.jpg')
			plt.clf()
			self._draw_simple_interval_distribution(result_lst, random_simple, color)
			plt.savefig(f'./logic/at/simple_interval_distribution/{name}_interval_dis.jpg')
			plt.clf()
	def test_random_multi_weight(self):
		"""单次加权权重抽取 N 个,执行 1 次"""
		random.seed(self.RANDOM_SEED)
		weight_map, random_simple, random_cnt = {1: 1, 2: 8, 3: 1, 4: 1, 5: 89}, 2, 10000
		random_handler = {
		    self._random_float_multi_weight: "yellow",
		    self._random_int_multi_weight: "blue",
		    self._random_choice_multi_weight_1: "red",
		    self._random_gass_multi_weight: "green",
		}
		for handler, color in random_handler.items():
			name = handler.__name__
			result_lst = handler(weight_map, random_cnt)
			expect_val = sum(val * cnt for val, cnt in Counter(result_lst).items())
			cnt_total = sum(cnt for cnt in Counter(result_lst).values())
			print(f"handler: {name} {expect_val} {cnt_total} {Counter(result_lst)}")
			self._draw_expect(Counter(result_lst), color)
			plt.savefig(f'./logic/at/random_expect/{name}_expect.jpg')
			plt.clf()
			self._draw_simple_interval(result_lst, random_simple, color)
			plt.savefig(f'./logic/at/random_simple_interval/{name}_simple_interval.jpg')
			plt.clf()
			self._draw_simple_interval_distribution(result_lst, random_simple, color)
			plt.savefig(f'./logic/at/simple_interval_distribution/{name}_interval_distribution.jpg')
			plt.clf()
更新于 阅读次数

请我[恰饭]~( ̄▽ ̄)~*

鑫酱(●'◡'●) 微信支付

微信支付

鑫酱(●'◡'●) 支付宝

支付宝