在 C# 中获取一定范围内的随机持续时间

发布于 2024-07-09 06:37:40 字数 593 浏览 9 评论 0原文

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

因此,例如:

我可能会说我想要 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上。

For a random event generator I'm writing I need a simple algorithm to generate random ranges.

So, for example:

I may say I want 10 random intervals, between 1/1 and 1/7, with no overlap, in the states (1,2,3) where state 1 events add up to 1 day, state 2 events add up to 2 days and state 3 events add up to the rest.

Or in code:

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 
}

I'm working on this now, but in case someone beats me to a solution (or knows of an elegant pre-existing algorithm) I'm posting this on SO.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

一人独醉 2024-07-16 06:37:40

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

First use DateTime.Subtract to determine how many minutes/seconds/whatever between your min and max dates. Then use Math.Random to get a random number of minutes/seconds/whatever in that range. Then use the result of that to construct another TimeSpan instance and add that to your min DateTime.

找个人就嫁了吧 2024-07-16 06:37:40

这是一个可以编译并运行的实现,尽管它仍然有些粗糙。 它要求输入状态数组正确地考虑到感兴趣的整个时间范围(结束 - 开始),但是添加一些代码使最终状态填充前 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();
}

Here's an implementation that compiles and works, although it's still somewhat rough. It requires that the input state array properly account for the entire time range of interest (end - start), but it would be trivial to add a bit of code that would make the final state fill up the time not accounted for in the first N-1 states. I also modified your structure definitions to use ints instead of longs for the durations, just to simplify things a bit.

For clarity (and laziness) I omitted all error checking. It works fine for the inputs like the ones you described, but it's by no means bulletproof.

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();
}
剩余の解释 2024-07-16 06:37:40

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

Here is my current implementation that seems to work ok and accounts for all time. This would be so much cleaner if I didn't have to target .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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文