2012年12月9日日曜日

Java 重み付けランダム抽選

車輪の再発明かもしれませんが、
重み付けをしたランダム抽選ロジック作ったので共有


package org.chikishe.Util;

import java.util.ArrayList;
import java.util.Collection;

public class RandomUtil {
    @SuppressWarnings("unchecked")
    public static <T> T weightedRandom(Collection
<T> resource,
            Weight
<T> delegate) throws Exception {
        ArrayList
<WeightCompare> weightedResource = new ArrayList<WeightCompare> ();
        int totalWeight = 0;

        for (T temp : resource) {
            int weight = delegate.getWeight(temp);
            if (weight <= 0)
                continue;

            totalWeight += weight;
            weightedResource.add(new WeightCompare(temp, totalWeight));
        }

        if (totalWeight
<= 0)
            throw new Exception("No Resource");

        int random = randomNext(1, totalWeight + 1);

        int lower = -1;
        int upper = weightedResource.size();
        while (upper - lower
> 1) {
            int index = (lower + upper) / 2;
            if (weightedResource.get(index).weight
>= random) {
                upper = index;
            } else {
                lower = index;
            }
        }

        return (T) weightedResource.get(upper).obj;
    }

    public static int randomNext(int from, int to) {
        return (int) (Math.random() * (to - from) + from);
    }

    public interface Weight
<T> {
        int getWeight(T temp);
    }

    private static class WeightCompare {
        private Object obj;
        private int weight;

        public WeightCompare(Object obj, int weight) {
            this.obj = obj;
            this.weight = weight;
        }
    }
}


呼び出しは、次のようになります。

public String getTest4() {
        ArrayList<TestMember> members = new ArrayList<TestMember>();

        members.add(new TestMember(1, 50));
        members.add(new TestMember(2, 80));
        members.add(new TestMember(3, 130));

        try {

            TestMember member = RandomUtil.weightedRandom(members,
                    new Weight<TestMember>() {
                        public int getWeight(TestMember m) {
                            return m.weight;
                        }
                    });

            return "" + member.id;

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            throw new WebApplicationException(500);
        }
    }

    private class TestMember {
        public int id;
        public int weight;

        public TestMember(int id, int weight) {
            this.id = id;
            this.weight = weight;
        }
    }

RandomUtil.weightedRandomはジェネリック型を受け取るようにしているので
第1引数のリストに指定した型を返します。
第2引数には重み付けを与えるメソッドの定義を渡しています。
サンプルの場合は、リストのTestMemberクラスのweightを重みとして取得しています。


ジェネリックの使い方がようやくわかってきた。


追記
2014/01/* Java 8でのラムダ式を使った使用方法はこちら

0 件のコメント: