【问题标题】:Getting Random Durations within a range in C#在 C# 的范围内获取随机持续时间
【发布时间】:2008-11-03 00:30:40
【问题描述】:

对于我正在编写的随机事件生成器,我需要一个简单的算法来生成随机范围。

所以,例如:

我可以说我想要 10 个随机间隔,在 1/1 和 1/7 之间,没有重叠,在状态 1 事件加起来为 1 天,状态 2 事件加起来的状态 (1,2,3)到 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 上发布这个。

【问题讨论】:

  • 无论间隔如何,状态 1 的事件是否总是加起来为 1(状态 2 也是如此)?你需要多随机?什么类型的分布?
  • 正确,在示例中,状态 1 的事件总加起来为 1 天,状态 2 的总和为 2 天。所以它是随机的,但它有上限。我希望东西分布在几个标准偏差内。
  • 标准偏差实际上是没有意义的,样本量为 10,分布在 3 个事件中。
  • 更正,我在平均持续时间 - 平均持续时间 / 2 和平均持续时间 + 平均持续时间 / 2 之间选择了持续时间

标签: c# algorithm date-range


【解决方案1】:

首先使用 DateTime.Subtract 来确定您的最小日期和最大日期之间的分钟/秒/任意值。然后使用 Math.Random 获得随机数的分钟/秒/该范围内的任何值。然后使用该结果构建另一个 TimeSpan 实例并将其添加到您的 min DateTime。

【讨论】:

    【解决方案2】:

    这是一个编译和工作的实现,虽然它仍然有些粗糙。它要求输入状态数组正确考虑整个感兴趣的时间范围(结束 - 开始),但是添加一些代码会使最终状态填满第一个 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();
    }
    

    【讨论】:

    • 我认为我的算法将事件分布得更均匀一些......而且算法中保证了前 n 个事件的顺序,这有点不可取。这个算法比我的要快一点,因为它不会多次迭代返回值。
    • 我可能遗漏了一些东西,但我不认为前 N 个事件的顺序在这里是固定的。在我的测试中肯定不是这样。
    • 你说得对,我想我错过了算法,但我认为关于事件分布的评论是有效的。我应该测试一下。
    【解决方案3】:

    这是我当前的实现,它似乎工作正常,并且占了所有时间。如果我不必以 .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);
    }
    

    【讨论】:

      猜你喜欢
      • 2023-02-06
      • 1970-01-01
      • 2020-07-14
      • 1970-01-01
      • 1970-01-01
      • 2020-10-23
      • 2017-07-13
      • 2012-12-21
      • 2012-04-07
      相关资源
      最近更新 更多