2008-11-03 67 views
0

对于一个随机事件发生器,我在写我需要一个简单的算法来生成随机范围。在C#中获得一个范围内的随机持续时间

所以,例如:

我可以说我希望10周随机的时间间隔,和1/1 1/7之间,没有重叠,在状态(1,2,3),其中状态1个事件添加最多1天,状态2事件加起来2天,状态3事件加起来剩下的事情。

或者代码:

struct Interval 
{ 
    public DateTime Date; 
    public long Duration; 
    public int State; 
} 

struct StateSummary 
{ 
    public int State; 
    public long TotalSeconds; 
} 

public Interval[] GetRandomIntervals(DateTime start, DateTime end, StateSummary[] sums, int totalEvents) 
{ 
    // insert your cool algorithm here 
} 

我的工作现在这个样子,但如果有人打我的解决方案(或优雅的预先存在的算法都知道),我对SO张贴本。

+0

无论间隔(状态2),状态1事件总是加起来1吗?你需要多少随机数?什么类型的分配? – 2008-11-03 00:35:53

+0

正确,在示例状态1事件总是加起来1天,状态2总是2天。所以它是随机的,但它有上限。我想要在一些标准偏差内分发东西。 – 2008-11-03 00:49:51

+0

标准偏差在3个事件中分布的样本量为10时实际上没有意义。 – 2008-11-03 04:20:24

回答

0

这是我目前的实施,似乎工作正常,占所有时间。如果我不必定位目标,这将会非常干净.net 1.1

public class Interval 
{ 
    public Interval(int state) 
    { 
     this.State = state; 
     this.Duration = -1; 
     this.Date = DateTime.MinValue; 
    } 
    public DateTime Date; 
    public long Duration; 
    public int State; 
} 

class StateSummary 
{ 
    public StateSummary(StateEnum state, long totalSeconds) 
    { 
     State = (int)state; 
     TotalSeconds = totalSeconds; 
    } 
    public int State; 
    public long TotalSeconds; 
} 

Interval[] GetRandomIntervals(DateTime start, DateTime end, StateSummary[] sums, int totalEvents) 
{ 
    Random r = new Random(); 
    ArrayList intervals = new ArrayList(); 

    for (int i=0; i < sums.Length; i++) 
    { 
     intervals.Add(new Interval(sums[i].State)); 
    } 

    for (int i=0; i < totalEvents - sums.Length; i++) 
    { 
     intervals.Add(new Interval(sums[r.Next(0,sums.Length)].State)); 
    } 

    Hashtable eventCounts = new Hashtable(); 
    foreach (Interval interval in intervals) 
    { 
     if (eventCounts[interval.State] == null) 
     { 
      eventCounts[interval.State] = 1; 
     } 
     else 
     { 
      eventCounts[interval.State] = ((int)eventCounts[interval.State]) + 1; 
     } 
    } 

    foreach(StateSummary sum in sums) 
    { 
     long avgDuration = sum.TotalSeconds/(int)eventCounts[sum.State]; 
     foreach (Interval interval in intervals) 
     { 
      if (interval.State == sum.State) 
      { 
       long offset = ((long)(r.NextDouble() * avgDuration)) - (avgDuration/2); 
       interval.Duration = avgDuration + offset; 
      } 
     } 
    } 

    // cap the durations. 
    Hashtable eventTotals = new Hashtable(); 
    foreach (Interval interval in intervals) 
    { 
     if (eventTotals[interval.State] == null) 
     { 
      eventTotals[interval.State] = interval.Duration; 
     } 
     else 
     { 
      eventTotals[interval.State] = ((long)eventTotals[interval.State]) + interval.Duration; 
     } 
    } 

    foreach(StateSummary sum in sums) 
    { 
     long diff = sum.TotalSeconds - (long)eventTotals[sum.State]; 
     if (diff != 0) 
     { 
      long diffPerInterval = diff/(int)eventCounts[sum.State]; 
      long mod = diff % (int)eventCounts[sum.State]; 
      bool first = true; 
      foreach (Interval interval in intervals) 
      { 
       if (interval.State == sum.State) 
       { 
        interval.Duration += diffPerInterval; 
        if (first) 
        { 
         interval.Duration += mod; 
         first = false; 
        } 

       } 
      } 
     } 
    } 

    Shuffle(intervals); 

    DateTime d = start; 
    foreach (Interval interval in intervals) 
    { 
     interval.Date = d; 
     d = d.AddSeconds(interval.Duration); 
    } 

    return (Interval[])intervals.ToArray(typeof(Interval)); 
} 

public static ICollection Shuffle(ICollection c) 
{ 
    Random rng = new Random(); 
    object[] a = new object[c.Count]; 
    c.CopyTo(a, 0); 
    byte[] b = new byte[a.Length]; 
    rng.NextBytes(b); 
    Array.Sort(b, a); 
    return new ArrayList(a); 
} 
1

首先使用DateTime.Subtract以确定有多少分/秒/无论你的最小和最大日期之间。然后使用Math.Random获得随机数分钟/秒/任何在该范围内。然后使用该结果构造另一个TimeSpan实例并将其添加到最小日期时间。

0

这是一个编译和工作的实现,尽管它仍然有些粗糙。它要求输入状态数组适当地考虑整个感兴趣的时间范围(结束 - 开始),但是添加一些代码会使得最终状态填满在第一个N中未考虑的时间将是微不足道的-1个州。我还修改了你的结构定义,在整个持续时间内使用整数而不是长整数,只是为了简化一点。

为了清楚(和懒惰),我省略了所有错误检查。它可以很好地处理您所描述的输入,但绝不是无懈可击的。

public static Interval[] GetRandomIntervals(DateTime start, DateTime end, 
    StateSummary[] states, int totalIntervals) 
{ 
    Random r = new Random(); 

    // stores the number of intervals to generate for each state 
    int[] intervalCounts = new int[states.Length]; 

    int intervalsTemp = totalIntervals; 

    // assign at least one interval for each of the states 
    for(int i = 0; i < states.Length; i++) 
     intervalCounts[i] = 1; 
    intervalsTemp -= states.Length; 

    // assign remaining intervals randomly to the various states 
    while(intervalsTemp > 0) 
    { 
     int iState = r.Next(states.Length); 
     intervalCounts[iState] += 1; 
     intervalsTemp -= 1; 
    } 

    // make a scratch copy of the state array 
    StateSummary[] statesTemp = (StateSummary[])states.Clone(); 

    List<Interval> result = new List<Interval>(); 
    DateTime next = start; 
    while(result.Count < totalIntervals) 
    { 
     // figure out which state this interval will go in (this could 
     // be made more efficient, but it works just fine) 
     int iState = r.Next(states.Length); 
     if(intervalCounts[iState] < 1) 
      continue; 
     intervalCounts[iState] -= 1; 

     // determine how long the interval should be 
     int length; 
     if(intervalCounts[iState] == 0) 
     { 
      // last one for this state, use up all remaining time 
      length = statesTemp[iState].TotalSeconds; 
     } 
     else 
     { 
      // use up at least one second of the remaining time, but 
      // leave some time for the remaining intervals 
      int maxLength = statesTemp[iState].TotalSeconds - 
       intervalCounts[iState]; 
      length = r.Next(1, maxLength + 1); 
     } 

     // keep track of how much time is left to assign for this state 
     statesTemp[iState].TotalSeconds -= length; 

     // add a new interval 
     Interval interval = new Interval(); 
     interval.State = states[iState].State; 
     interval.Date = next; 
     interval.Duration = length; 
     result.Add(interval); 

     // update the start time for the next interval 
     next += new TimeSpan(0, 0, length); 
    } 

    return result.ToArray(); 
}