2011-11-16 54 views
1

好吧,假設我有一個類「foo」,並具有「狀態」的適當性。我需要一個函數來隨機選擇一個具有這些限制的新狀態:一些轉換是不允許的,每個轉換都有不同的發生概率。隨機從狀態s1到狀態s2給定概率

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]。我需要這種方法儘可能通用,以便可以有三種以及三百種可能的狀態。
這不是功課,我只需要一個很好的起點,因爲我不知道從哪裏:)

開始

乾杯

+0

性能有多重要? – CodesInChaos

+0

@CodeInChaos:不多,只要小於O(n^18);) – BlackBear

+2

你可以通過設置適當的概率爲'0'來擺脫'allowedTransitions'。 – Vlad

回答

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(); 
      } 
     } 
    } 
} 
+0

謝謝。我仍然是C#的初學者,所以我需要一些時間去理解它;) – BlackBear

2

對於從狀態的過渡,你計算(均勻分佈)0和1之間的隨機數r。你有過渡概率:p1,p2,...,pn,它們的總和顯然必須是1.現在,如果是r < p1,則按照第一次過渡;否則,如果r < p1 + p2,您遵循第二個轉換,依此類推。

PS:爲了產生所需要的隨機數,你會得到一個(單)Random對象,並調用NextDouble方法:

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

好吧,讓我們說,我們有產生一個隨機值的函數1和100

之間讓我們調用這個函數

float GetRandomNumber() 
{  
    .... 
} 

現在讓我們假設我們希望生成具有probabi三個數字lities發生以下兩種情況:

情況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; 
    } 
    } 

而對於二是容易

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

我已經直接編寫的代碼,它可能無法編譯,但是,我想到的是顯示我在做什麼的地步。

希望它有幫助。