1. 程式人生 > >遊戲編程精粹學習 - 使用Bloom過濾來提高計算性能(BloomFilter)

遊戲編程精粹學習 - 使用Bloom過濾來提高計算性能(BloomFilter)

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)