简体   繁体   English

随机时间间隔生成器,用于在指定的时间段内设置一定数量的实例

[英]Random Interval Generator For a Set Number of Instances Over a Set Period of Time

I was presented with an intriguing problem in c# that I am not sure how to do. 我在c#中遇到了一个有趣的问题,我不确定该怎么做。

I need to have two tracks playing on top one another. 我需要两个曲目互相叠加播放。 A constant number of beeps need to be played over a set amount of time. 需要在设定的时间内播放恒定数量的蜂鸣声。 One of them will have a set interval( think metronome), but the other needs to be played at random intervals. 其中一个将有一个固定的间隔(以节拍器为准),但另一个则需要以随机的间隔播放。

I am not sure how to solve that second issue, a set number of beeps played at random intervals over a set amount of time. 我不确定如何解决第二个问题,即在一定时间内随机播放一定数量的哔哔声。

Just take that set amount of time, T. Represent it as some sufficiently-fine-grained structure, say milliseconds. 只需花费设定的时间T,即可将其表示为某种足够细粒度的结构(以毫秒为单位)。 If you need to make N beeps, you need to split the timespan N times. 如果您需要发出N次蜂鸣声,则需要将时间间隔拆分N次。 So make a loop that runs N times and in each iteration, selects a random location in the time interval for a beep. 因此,使一个循环运行N次,并且在每次迭代中,在时间间隔中为蜂鸣声选择一个随机位置。 Depending on what you are doing with the data after that, you may need to sort the beep points. 根据之后对数据的处理方式,可能需要对提示音点进行排序。

Use random number generate to generate a datetime within the the range of the total amount of time. 使用随机数生成可在总时间范围内生成日期时间。 When you are done placing the random beeps then the intervals will of course be random. 完成放置随机蜂鸣声后,间隔当然是随机的。 Something like this: 像这样:

    List<DateTime> randomBeeps = new List<DateTime>();

    Random rand = new Random();
    for( int j = 0; j < numberBeepsNeeded; j++ )
    {
         int randInt = rand.Next();
         double percent = ((double)randInt) / Int32.MaxValue;
         double randTicksOfTotal = ((double)setAmountOfTime.Ticks) * percent;
         DateTime randomBeep = new DateTime((long)randTicksOfTotal);
         randomBeeps.Add(randomBeep);
    }

You might need to use a Convert.ToLong or something of that sort. 您可能需要使用Convert.ToLong或类似的东西。 Not sure if it will give you an error on converting from double to long as it is rounding it, which is fine here. 不知道它是否会在四舍五入的过程中给您从double转换为long的错误,在这里很好。

You can implement this as a series of one-shot timers. 您可以将其实现为一系列的一次性计时器。 As each timer expires (or "ticks"), you play the beep and then randomly determine the duration to use for the next one-shot timer. 当每个计时器到期(或“滴答”)时,您会发出哔声,然后随机确定下一个计时器的持续时间。 If the duration you choose is some random number between 1 and 1000 (milliseconds), you will average one "tick" every half-second. 如果您选择的持续时间是介于1到1000(毫秒)之间的某个随机数,则您将平均每半秒一次“滴答”。

Edit: just for fun, I thought I'd mention that this is an old problem for behavioral psychologists running the kinds of experiments inspired by BF Skinner. 编辑:只是为了好玩,我想提一提,这对于行为心理学家进行由BF Skinner启发的各种实验是一个老问题。 They sometimes use a schedule of reinforcement called "Variable Interval", in which the time between reinforcements varies randomly around some predetermined average interval. 他们有时使用称为“可变间隔”的加固计划,其中加固之间的时间在某个预定的平均间隔附近随机变化。 See http://www.ncbi.nlm.nih.gov/pmc/articles/PMC1404199/pdf/jeabehav00190-0145.pdf for an egg-headed discussion of the formulae involved. 请参阅http://www.ncbi.nlm.nih.gov/pmc/articles/PMC1404199/pdf/jeabehav00190-0145.pdf ,以深入了解所涉及的公式。

Something like this ought to do the trick (this code not tested...but it compiles clean) 这样的事情应该可以解决(此代码未经测试...但是编译干净)

using System;
using System.Security.Cryptography;
using System.Threading;

class BeatBox : IDisposable
{
    private RandomNumberGenerator RNG;

    private DateTime dtStarted;
    private TimeSpan TimeElapsed { get { return DateTime.Now - dtStarted; } }

    private TimeSpan  Duration;
    private TimeSpan  BeatInterval;
    private uint      MinRandomInterval;
    private uint      MaxRandomInterval;
    private uint      RandomIntervalDomain;

    private Timer     RegularIntervalTimer;
    private Timer     RandomIntervalTimer;

    public delegate void TickHandler( object sender , bool isRandom );
    public event TickHandler TickEvent;

    private EventWaitHandle CompletionEventWaitHandle;

    public BeatBox( TimeSpan duration , TimeSpan beatInterval , uint minRandomInterval , uint maxRandomInterval )
    {
        this.RNG = RandomNumberGenerator.Create();

        this.Duration             = duration          ;
        this.BeatInterval         = beatInterval      ;
        this.MinRandomInterval    = minRandomInterval ;
        this.MaxRandomInterval    = maxRandomInterval ;
        this.RandomIntervalDomain = ( maxRandomInterval - minRandomInterval ) + 1 ;
        this.dtStarted            = DateTime.MinValue ;

        this.RegularIntervalTimer = null ;
        this.RandomIntervalTimer  = null ;

        return;
    }

    private long NextRandomInterval()
    {
        byte[] entropy = new byte[sizeof(long)] ;

        RNG.GetBytes( entropy );

        long randomValue    = BitConverter.ToInt64( entropy , 0 ) & long.MaxValue; // ensure that its positive
        long randomoffset   = ( randomValue % this.RandomIntervalDomain );
        long randomInterval = this.MinRandomInterval + randomoffset;

        return randomInterval;
    }

    public EventWaitHandle Start()
    {
        long randomInterval = NextRandomInterval();

        this.CompletionEventWaitHandle = new ManualResetEvent( false );
        this.RegularIntervalTimer = new Timer( RegularBeat , null , BeatInterval , BeatInterval );
        this.RandomIntervalTimer = new Timer( RandomBeat , null , randomInterval , Timeout.Infinite );

        return this.CompletionEventWaitHandle;
    }

    private void RegularBeat( object timer )
    {
        if ( this.TimeElapsed >= this.Duration )
        {
            MarkComplete();
        }
        else
        {
            this.TickEvent.Invoke( this , false );
        }
        return;
    }
    private void RandomBeat( object timer )
    {
        if ( this.TimeElapsed >= this.Duration )
        {
            MarkComplete();
        }
        else
        {
            this.TickEvent.Invoke( this , true );

            long nextInterval = NextRandomInterval();
            this.RandomIntervalTimer.Change( nextInterval , Timeout.Infinite );

        }
        return;
    }

    private void MarkComplete()
    {
        lock ( this.CompletionEventWaitHandle )
        {
            bool signaled = this.CompletionEventWaitHandle.WaitOne( 0 );
            if ( !signaled )
            {
                this.RegularIntervalTimer.Change( Timeout.Infinite , Timeout.Infinite );
                this.RandomIntervalTimer.Change( Timeout.Infinite , Timeout.Infinite );
                this.CompletionEventWaitHandle.Set();
            }
        }
        return;
    }

    public void Dispose()
    {
        if ( RegularIntervalTimer != null )
        {
            WaitHandle handle = new ManualResetEvent( false );
            RegularIntervalTimer.Dispose( handle );
            handle.WaitOne();
        }
        if ( RandomIntervalTimer != null )
        {
            WaitHandle handle = new ManualResetEvent( false );
            RegularIntervalTimer.Dispose( handle );
            handle.WaitOne();
        }
        return;
    }
}

class Program
{
    static void Main( string[] args )
    {
        TimeSpan duration          = new TimeSpan( 0 , 5 , 0 ); // run for 5 minutes total
        TimeSpan beatInterval      = new TimeSpan( 0 , 0 , 1 ); // regular beats every 1 second
        uint     minRandomInterval = 5; // minimum random interval is 5ms
        uint     maxRandomInterval = 30; // maximum random interval is 30ms

        using ( BeatBox beatBox = new BeatBox( duration , beatInterval , minRandomInterval , maxRandomInterval ) )
        {
            beatBox.TickEvent += TickHandler;

            EventWaitHandle completionHandle = beatBox.Start();

            completionHandle.WaitOne();

        }
        return;
    }

    static void TickHandler( object sender , bool isRandom )
    {
        Console.WriteLine( isRandom ? "Random Beep!" : "Beep!" );
        return;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM