在给定概率的情况下从状态s1随机地进入状态s2

时间:2011-11-16 13:37:14

标签: c# random state

好吧,假设我有一个具有“州”特权的班级foo。我需要一个随机选择具有这些限制的新状态的函数:不允许某些转换,并且每个转换都有不同的发生概率。

enum PossibleStates { A, B, C, D }
PossibleStates currentState;

float[,] probabilities;

probabilities[s1, s2]表示从状态s1进入状态s2的概率。它也可以是0.0f,意味着s1-> s2是不可能的,probabilities[s1, s2]可以与probabilities[s2, s1]不同。我需要这种方法尽可能通用,这样就可以有三种以及三百种可能的状态 这不是功课,我只需要一个很好的起点,因为我不知道从哪里开始:)

干杯

3 个答案:

答案 0 :(得分:2)

对于从状态A的转换,您在0和1之间计算(均匀分布的)随机数r。您具有转换的概率:p1p2 ,. ..,pn,他们的总和显然必须是1.现在,如果r < p1,你遵循第一次过渡;否则,如果r < p1 + p2,则按照第二次转换进行操作,依此类推。

P.S。:为了产生所需的随机数,你得到一个(单个)Random个对象,并调用NextDouble方法:

Random rnd = new Random();
...
double r = rnd.NextDouble();

答案 1 :(得分:1)

好的,我们假设我们有一个函数可以产生1到100之间的随机值。

让我们调用这个函数

 float GetRandomNumber()
 {    
     ....
 }

现在我们假设我们想要生成三个在两种情况下可能发生的数字:

案例1)

概率是互斥的,即如果一个发生,其他概率就不会发生

案例2)

概率是独立的,因此它们以自己的概率发生

让我们看看1:

发生了什么
  var mutuallyExclusiveProbs = new List<float>({30,50,20});
  var number = GetRandomNumber();
  var cumulativeValue =0;
  //
  for (int i=0; i++; i<mutuallyExclusiveProbs.Count())
  {
    cumulativeValue += mutuallyExclusiveProbs(i)
    if (number <=cumulativeValue)
    {
       //case found 
       return i;
    }
  }

而对于2则更容易

  var mutuallyExclusiveProbs = new List<float>({30,50,20});
  var number = GetRandomNumber();
  return mutuallyExclusiveProbs.Where(x=>x<=number );

我已直接编写代码,但可能无法编译,但我认为我正在做的事情已经显示出来了。

希望它有所帮助。

答案 2 :(得分:1)

完整功能示例如下。 F5然后点击ENTER改变状态。 在States课程中,您可以定义概率和状态。

在我的代码中我假设路由是互斥的(它们的组合概率永远不会超过1)。如果不是这样的话,我已经标记了要改变的代码。

namespace RandomStatesProgram
{
    class State
    {
        public string Name;

        private bool current;
        public bool Current
        {
            get { return current; }
            set
            {
                if (current)
                {
                    if (value) StayingHere();
                    else LeavingState();
                }
                else
                {
                    if (value) EnteringState();
                }

                current = value;
            }
        }

        public void StayingHere() { Console.WriteLine("Staying in state " + this.Name); }
        public void EnteringState() { Console.WriteLine("Entering state " + this.Name); }
        public void LeavingState() { Console.WriteLine("Leaving state " + this.Name); }

        public State()
        {
            this.Name = "New";
            this.Current = false;
        }

        public State(string name)
            : this()
        {
            this.Name = name;
        }
    }

    class TransitionCourse
    {
        public State From { get; set; }
        public State To { get; set; }
        public float Probability { get; set; }

        public TransitionCourse(Dictionary<int, State> allStates, int fromState, int toState, float probability)
        {
            if (probability < 0 || probability > 1)
                throw new ArgumentOutOfRangeException("Invalid probability");

            if (!allStates.Keys.Any(K => K == fromState || K == toState))
                throw new ArgumentException("State not found");

            this.From = allStates[fromState];
            this.To = allStates[toState];
            this.Probability = probability;
        }
    }

    static class States
    {
        private static Dictionary<int, State> PossibleStates;
        public static State Current
        {
            get
            {
                if (PossibleStates.Where(S => S.Value.Current).Count() == 1)
                    return PossibleStates.Single(S => S.Value.Current).Value;
                else
                    return null;
            }
        }

        public static List<TransitionCourse> Transitions;

        static States()
        {
            PossibleStates = new Dictionary<int, State>()
            {
                {1, new State("One")}, 
                {2, new State("Two")}, 
                {3, new State("Three")},
                {4, new State("Four")} 
            };

            // example: 50% chance of switching to either state from every one of the three
            // note: it must be  0 <= 3rd param <= 1 of course (it's a probability)
            Transitions = new List<TransitionCourse>()
            {
                new TransitionCourse(PossibleStates,1,2,1f/3f),
                new TransitionCourse(PossibleStates,1,3,1f/3f),
                new TransitionCourse(PossibleStates,1,4,1f/3f),
                new TransitionCourse(PossibleStates,2,1,1f),            
                new TransitionCourse(PossibleStates,3,1,1f),
                new TransitionCourse(PossibleStates,4,1,1f)               
            };
        }

        public static void GoTo(int targetState)
        {
            if (!PossibleStates.Keys.Contains(targetState))
                throw new ArgumentException("Invalid state");

            foreach (KeyValuePair<int, State> state in PossibleStates.OrderByDescending(S=>S.Value.Current))
            {
                //first is the "true" state (the current one) then the others.
                //this way we go OUT from a state before going IN another one.
                state.Value.Current = state.Key.Equals(targetState);
            }
        }

        public static void Travel()
        {
            if (Current == null)
                throw new InvalidOperationException("Current state not set");

            TransitionCourse[] exits = Transitions.Where(T => T.From.Equals(Current)).OrderBy(T=>T.Probability).ToArray();
            if (exits.Length == 0) //nowhere to go from here
                return;
            else
                if (exits.Length == 1) //not much to choose here
                {
                    GoTo(PossibleStates.Single(S => S.Value.Equals(exits.First().To)).Key);
                }
                else //ok now we have a choice
                {
                    //we need a "random" number
                    double p = new Random().NextDouble();

                    // remapping probabilities so we can choose "randomly"
                    // this works IF the sum of all transitions probability does not exceed 1.
                    // if it does, at best this'll act weird
                    for (int i = 1; i < exits.Length; i++)
                    {
                        exits[i].Probability += exits[i - 1].Probability;
                        if (exits[i].Probability > p)
                        {
                            GoTo(PossibleStates.Single(S => S.Value.Equals(exits[i].To)).Key);
                            return;
                        }
                    }
                }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            States.GoTo(1);
            while (Console.ReadLine().ToUpper() != "Q")
            {
                States.Travel();
            }
        }
    }
}