zoukankan      html  css  js  c++  java
  • 公式计算,分量累加计算优化

    问题

    计算公式 f = (base + genconst) * percent * genpercent + const
    公式中的运算变量均为长度为 26 的一维向量。 在游戏卡牌中,假如有 20 个养成系统, 会对公式中的某个或多个部分提供一个加成,在 20 个系统加成完成后, 计算出最终的结果。

    现有计算方式

    在一个函数中, 创建与公式对应的变量, 依次计算 20 个养成系统的加成并将养成累加到对应的变量, 最终得到结果。
    这样的计算方式,非常简单直观。 但存在两个的问题:

    1. 当只有某个养成系统变化时,为了得到结果, 你需要完整的把 20 个都跑一遍
    2. 当其中某个养成计算结果比较费时时,你需要对特定的养成做缓存来提高计算的效率

    优化计算方式

    将公式拆解成一颗树的形式,如下所示:
    属性优化示意图

    每个养成系统将自己的值放到对应的节点, 当某个养成系统有改动时,去设置对应节点的值,然后改动以增量的形式沿着路径往上走,直到根节点,这样根节点的值就是最新的,同时也不会引起到其他节点的计算。

    这样就很好的处理了现有计算方式的两个问题, 计算时间减少 97%, 虽然引入了额外的内存消耗, 但在可接受范围,在python 中不要忘记用 slots 优化内存。

    demo 代码:

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    try:
    	from game.object import AttrDefs
    # for test
    except ImportError:
    	AttrMaxID = 26 + 1
    else:
    	AttrMaxID = AttrDefs.attrTotal + 1
    
    import numpy as np
    
    
    def dict2attrs(d):
    	value = np.zeros(AttrMaxID)
    	for i, attr in enumerate(AttrDefs.attrsEnum):
    		if attr:
    			value[i] = d.get(attr, 0)
    	return np.array(value)
    
    def attrs2dict(attr):
    	d = {}
    	for i, v in enumerate(attr):
    		if v:
    			d[AttrDefs.attrsEnum[i]] = v
    	return d
    
    class AttrContext(object):
    	'''simple context'''
    	def __init__(self, game, scene=0, **kwargs):
    		self.game = game
    		self.scene = scene
    		self.__dict__.update(kwargs)
    
    	# TODO: use statement with to achieve a temp context
    
    
    class Node(object):
    	__slots__ = ("name", "ret", "_adds", "_parent", "left", "right", "tag")
    
    	def __init__(self, name, tag=None, parent=None, left=None, right=None):
    		self.name = name
    		self.ret = np.zeros(AttrMaxID)
    		self._adds = {}
    		self._parent = parent
    		self.left = left
    		self.right = right
    		self.tag = tag
    
    	def __str__(self):
    		return '<Node object at 0x%x>
    %s: %s
    ' % (id(self), self.name, tuple(self.ret))
    
    	def addLeft(self, node):
    		self.left = node
    		node._parent = self
    		node.tag = 'l'
    
    	def addRight(self, node):
    		self.right = node
    		node._parent = self
    		node.tag = 'r'
    
    	def set(self, k, v):
    		d = v - self._adds.get(k, 0)
    		if any(d):
    			self._adds[k] = v
    			self.ret += d
    			self.onChange(d)
    
    	def onChange(self, d):
    		if self._parent:
    			self._parent.change(d, self.tag)
    
    	def change(self, d, tag):
    		if tag == 'l':
    			v = self.right.ret
    		else:
    			v = self.left.ret
    
    		if self.name == '*':
    			d1 = v*d
    		elif self.name == '+':
    			d1 = d
    
    		if any(d1):
    			self.ret += d1
    			self.onChange(d1)
    
    class Calculator(object):
    
    	def __init__(self, ctx):
    		self.ctx = ctx
    		self._nodes = {}
    		self._ft = self.buildFTree()
    		self.init()
    
    	# set default value
    	def init(self):
    		self.percent.set('default', np.ones(AttrMaxID))
    		self.genpercent.set('default', np.ones(AttrMaxID))
    
    	def __getattr__(self, name):
    		try:
    			return self._nodes[name]
    		except KeyError:
    			raise AttributeError("%s instance has no attribute '%s'" % (self.__class__.__name__, name))
    
    	def buildFTree(self, f):
    		root = None
    		p = Node('+')
    		root = p
    
    		base = Node('base')
    		genconst = Node('genconst')
    		percent = Node('percent')
    		genpercent = Node('genpercent')
    		const = Node('const')
    
    		self._nodes['base'] = base
    		self._nodes['genconst'] = genconst
    		self._nodes['percent'] = percent
    		self._nodes['genpercent'] = genpercent
    		self._nodes['const'] = const
    
    		p.addRight(const)
    		tn = Node('*')
    		p.addLeft(tn)
    		p = tn
    
    		p.addRight(genpercent)
    		tn = Node('*')
    		p.addLeft(tn)
    		p = tn
    
    		p.addRight(percent)
    		tn = Node('+')
    		p.addLeft(tn)
    		p = tn
    
    		p.addLeft(base)
    		p.addRight(genconst)
    
    		return root
    
    	@property
    	def result(self):
    		return attrs2dict(self._ft.ret)
    
    	# show formula
    	def showf(self):
    		f = []
    
    		def show(p, f):
    			if p.name in '+*':
    				f.append('(')
    
    			if p.left:
    				show(p.left, f)
    			f.append(p.name)
    			if p.right:
    				show(p.right, f)
    
    			if p.name in '+*':
    				f.append(')')
    
    		show(self._ft, f)
    		print(''.join(f))
    
    
  • 相关阅读:
    git(1)-git关联GitHub-windows-转载
    jenkins(4)-jenkins配置邮件通知
    jenkins(3)-linux下安装jenkins(yum install方式)
    【PAT甲级】1090 Highest Price in Supply Chain (25 分)(DFS)
    【PAT甲级】1087 All Roads Lead to Rome (30 分)(MAP【int,string】,邻接表,DFS,模拟,SPFA)
    【PAT甲级】1018 Public Bike Management (30 分)(DFS,SPFA)
    Educational Codeforces Round 61 (Rated for Div. 2) G(线段树,单调栈)
    Atcoder Grand Contest 032C(欧拉回路,DFS判环)
    Educational Codeforces Round 62 (Rated for Div. 2)E(染色DP,构造,思维,组合数学)
    Atcoder Grand Contest 031C(构造,思维,异或,DFS)
  • 原文地址:https://www.cnblogs.com/nowg/p/10490707.html
Copyright © 2011-2022 走看看