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))
    
    
  • 相关阅读:
    Extjs系列篇(3)—-model数据模型
    js中parseInt()会导致的一些问题
    Extjs系列篇(2)—-初步了解
    一步一步学python(七)
    一步一步学python(六)
    一步一步学python(五) -条件 循环和其他语句
    一步一步学python(四)
    一步一步学python(三)
    MFC socket网络通讯核心代码
    MFC 遍历FTP服务器目录中文乱码问题
  • 原文地址:https://www.cnblogs.com/nowg/p/10490707.html
Copyright © 2011-2022 走看看