うにてぃブログ

主にUnityとC#に関する記事を書いていきます

【C#】重みをつけて IList を抽選する

抽選したいクラスに interface を継承させて LotteryUtil.Lot を呼び出せば抽選してくれる

private class Food : LotteryUtil.IWeight
{
    public string Name;
    public int Weight;
 
    public int GetWeight() => Weight;
}

コード

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
 
public static class LotteryUtil
{
    public static T Lot<T>(IList<T> weightTable) where T : IWeight
    {
        if (weightTable == null || weightTable.Count < 1)
            throw new Exception("Table is empty");

        if (weightTable.Count == 1)
            return weightTable[0];

        var totalWeight = weightTable.Sum(t => t.GetWeight());
        var value = Random.Range(1, totalWeight + 1);
        var index = 0;
        for (var i = 0; i < weightTable.Count; ++i)
        {
            if (weightTable[i].GetWeight() == 0)
                continue;

            if (weightTable[i].GetWeight() >= value)
            {
                return weightTable[i];
            }

            value -= weightTable[i].GetWeight();
        }

        throw new Exception("Invalid Calc");
    }

    public interface IWeight
    {
        int GetWeight();
    }
}

動作検証

10000回ほどループさせて重みがあってるのか検証
近しい割合になってるっぽいので多分大丈夫

f:id:hacchi_man:20200521003153p:plain

using System.Collections.Generic;
 
public class SampleMonoBehaviour : MonoBehaviour
{
    private class Food : LotteryUtil.IWeight
    {
        public string Name;
        public int Weight;

        public int GetWeight() => Weight;
    }
 
    private void Awake()
    {
        var dic = new Dictionary<string, int>();
        var foods = new []
        {
            new Food
            {
                Name = "Apple",
                Weight = 50,
            },
            new Food
            {
                Name = "Orange",
                Weight = 25,
            },
            new Food
            {
                Name = "Banana",
                Weight = 15,
            },
            new Food
            {
                Name = "Strawberry",
                Weight = 10,
            }
        };
 
        for (var i = 0; i < 10000; i++)
        {
            var result = LotteryUtil.Lot(foods);
            if (!dic.ContainsKey(result.Name))
                dic.Add(result.Name, 0);

            dic[result.Name]++;
        }
 
        foreach (var pair in dic)
            Debug.Log($"{pair.Key}:{pair.Value}");
    }
}