遊戲編程精粹學習 - 使用Bloom過濾來提高計算性能(BloomFilter)
阿新 • • 發佈:2018-05-05
scale gin cache fault bili 1.2 .com img 代碼
原文在《遊戲編程精粹2》的1.2中,BloomFilter是一種可以快速檢測是否存在集合包含關系的數據結構,但有一定的誤識別率。
該結構的優勢
- 判斷包含時效率高,粗略測試了下比List快一倍(不拆分哈希)
- 由於內部是位數組BitArray,做交集並集幾乎不產生開銷
該結構的劣勢
- 有一定的誤識別率
- 使用情境有限
我在Github找了一個BloomFilter的庫(這個庫使用時會產生大量GC,但學習來用夠了):
https://github.com/joeyrobert/bloomfilter
首先構建一個長度為N的位數組,將傳入數據的哈希值按位拆分成不同段,每一個段作為一個Key放入這個長度為N的位數組。
判斷包含時再把對象的多個key與位數組進行比較即可。
當然既然都存在誤判率,而且是以效率為優先的話,也可以不按位拆分哈希,我寫了一個最簡單的版本:
public class MyBloomFilter<T> { BitArray mBitArray; public MyBloomFilter(int bitLength) { mBitArray = new BitArray(bitLength); } public void Add(T obj) { var key = Mathf.Abs(obj.GetHashCode()) % mBitArray.Length; mBitArray[key]= true; } public bool Contains(T obj) { var key = Mathf.Abs(obj.GetHashCode()) % mBitArray.Length; return mBitArray[key]; } }
註意C#已經內置了位數組這樣的數據結構BitArray。
結合之前的可預測隨機數(http://www.cnblogs.com/hont/p/8716586.html),我寫了這樣一個例子
假設這是一個采草藥的功能,每一個不同顏色的方塊代表一顆草藥
鼠標點擊來采集它們。
代碼如下
using System.Collections.Generic; using UnityEngine; public class HerbGenerator : MonoBehaviour { public int x; public int y; public int probability = 70; List<HerbObject> mCreatedHerbList = new List<HerbObject>(); MyBloomFilter<int> mCollectedHerbList = new MyBloomFilter<int>(64); void Update() { GetArea(x - 3, y - 3, x + 3, y + 3); if (Input.GetMouseButtonDown(0)) { var hit = default(RaycastHit); var isHit = Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit); if (isHit) { var herbObject = hit.transform.GetComponent<HerbObject>(); mCollectedHerbList.Add(herbObject.seed); Destroy(hit.transform.gameObject); } } } void GetArea(int beginX, int beginY, int endX, int endY) { for (int i = 0; i < mCreatedHerbList.Count; i++) { if (!mCreatedHerbList[i]) continue; Destroy(mCreatedHerbList[i].gameObject); } var cacheState = Random.state; mCreatedHerbList.Clear(); float spacingScale = 1f;//增加間距防止兩顆草藥同時消失. for (int x = beginX, k = 0; x < endX; x++) { for (int y = beginY; y < endY; y++, k++) { var seed = 1000 + x + y * (endX - beginX); if (mCollectedHerbList.Contains(seed)) continue; Random.InitState(seed); var r = (int)(Random.value * 100); if (r % 100 < probability) { var cube = GameObject.CreatePrimitive(PrimitiveType.Cube); var herbObject = cube.AddComponent<HerbObject>(); herbObject.seed = seed; cube.transform.position = new Vector3(x * spacingScale, 0, y * spacingScale); GalaxyBuild(r, cube); mCreatedHerbList.Add(herbObject); } } } Random.state = cacheState; } void GalaxyBuild(int seed, GameObject go) { var cacheState = Random.state; Random.InitState(seed); var meshRenderer = go.GetComponent<MeshRenderer>(); switch ((int)(Random.value * 100 % 3)) { case 0://草藥類型1 meshRenderer.material.color = Color.red; break; case 1://草藥類型2 meshRenderer.material.color = Color.blue; break; case 2://草藥類型3 meshRenderer.material.color = Color.green; break; } Random.state = cacheState; } }HerbGenerator
出現誤判時會發生A草藥采完後B草藥同時消失的情況,只能增加草藥刷新的間距來緩解這個問題。
遊戲編程精粹學習 - 使用Bloom過濾來提高計算性能(BloomFilter)