Генерировать событие X чисел раз в секунду, но не равномерно распределенных в течение каждой секунды

Я хотел бы написать класс, который будет запускать событие x заранее определенное число раз в секунду, вызывает его n.

Однако я бы хотел, чтобы x NOT срабатывал равномерно в пределах каждой секунды.

Итак, скажем, n= 100, 25 могут стрелять в первые 300 мс, а затем еще 50 в течение следующих 600 мс, а последние 25 - в оставшиеся 100 мс.

В идеале я бы хотел получить более гранулярность, чем представленную выше, и более широкий интервал между каждой секундой.

Мне интересно, смогу ли я создать массив с определенной миллисекундой и количество событий, которые нужно запустить. Затем используйте цикл и класс Stopwatch, чтобы определить, нужно ли запускать определенные события для этой миллисекундной путевой точки.

Вопрос в том, может ли массив быть рассчитан для каждой секунды достаточно быстро, каждая секунда должна иметь случайный интервал.

Очевидно, что событие должно быть асинхронным, чтобы избежать задержек с тем, что когда-либо было подключено к нему.

Кто-нибудь сталкивается с любой подобной потребностью?

UPDATE

Я думал, что, по крайней мере, приложил свои усилия сюда.

Итак, я обнаружил, что достаточно разрешения, чтобы проверить, какой миллисекундой вы сейчас находитесь, и какой второй. В основном, каждую секунду я перестраиваю свой интервал, каждый миллисекунда получает запись в массиве, и каждая запись указывает, сколько раз запускать событие, которое миллисекунда.

Моя проблема - это интервал... Мне нужен лучший способ попробовать и скомпилировать счетчик событий, в настоящее время этот jsut, похоже, накладывает их равномерно на 0, 1 или 2.

public delegate void EmptyEventDelegate(); public class RandEvent { public event EmptyEventDelegate OnEvent = delegate { }; private bool running = false; Random r = new Random(); private int eventsPS; public RandEvent(int eventsPS = 1) { this.eventsPS = eventsPS; } public void Start() { running = true; Task.Factory.StartNew(() => { Run(); }); } private void Run() { var sw = new Stopwatch(); sw.Start(); int currentSecond = 0; int[] eventCount = BuildEventSpacing(); while(running) { if (currentSecond != sw.Elapsed.Seconds) { currentSecond = sw.Elapsed.Seconds; eventCount = BuildEventSpacing(); } else { for(int i = 0; i < eventCount[sw.Elapsed.Milliseconds]; i++) OnEvent(); } } sw.Stop(); } private int[] BuildEventSpacing() { var array = new int[1000]; for (int i = 0; i < eventsPS; i++) { array[r.Next(0, 999)]++; } return array; } public void Stop() { running = false; } }
5 ответов

Создайте Timer и в событии Tick сделайте все, что нужно сделать. Кроме того, используйте следующий метод (в дополнительном обработчике события Tick), чтобы каждый раз изменять интервал. Было бы лучше, если бы тот же экземпляр Random передавался методу каждый раз, а не предоставлял новые экземпляры.

private static ****** millisecondsPerSecond = 1000.0;
/// <summary>
/// Method used to determine how long you would wait for the event to fire next
/// </summary>
/// The approximate number of times the event should occur per second.
/// How much variance should be allowed, as a percentage. i.e. a variance of 0.1 would mean that
/// the delay will be +/- 10% of the exact rate.
/// A randon number generator.
/// <returns>The number of milliseconds to wait for the next event to fire.</returns>
public ****** GetNextDelay(int averageFiresPerSecond, ****** variance, Random generator)
{ ****** randomFactor = ((generator.**********() * 2) * variance); return (millisecondsPerSecond / averageFiresPerSecond) * randomFactor;
}


Скажем, вы хотите, чтобы 100 чисел, чья сумма равна 1. Вы можете сделать следующее:

  • Сгенерировать 100 случайных чисел между 0 и 1 (т.е. Random.**********) и сохранить в списке.
  • Введите числа.
  • Разделите каждое число в списке на сумму.

Теперь у вас есть список из 100 чисел, сумма которых равна 1.0.

Теперь создайте таймер с одним выстрелом, интервал которого является первым значением в списке. Когда он срабатывает, обработчик выполняет свою работу, а затем снова устанавливает таймер однократного выстрела с интервалом, равным следующему значению в списке.

За исключением того, что он не будет работать отлично, потому что он не учитывает количество времени, необходимое для обработки тика и установки таймера снова. Таким образом, вам нужно будет отслеживать эту задержку и соответствующим образом настроить следующий тик. Но даже это не будет идеальным.

Как я уже сказал в своем комментарии, вам будет сложно получить какой-либо из таймеров .NET, чтобы дать вам более 60 тиков в секунду. В лучшем случае вы можете ожидать 15 мс между отметками таймера. Даже если вы идете с пользовательской реализацией таймера, у вас будут проблемы, потому что таймеры не происходят точно вовремя. Проблема ухудшается, конечно, по мере уменьшения интервала таймера.

В любом случае, вы не можете получить таймер Windows, чтобы дать вам разрешение более 1 мс, поэтому вам придется настроить метод, описанный выше, чтобы дать вам номера, которые больше или равны 0,001.

Ваша идея использования Stopwatch может работать с моим методом создания интервалов событий. Поймите, однако, что цикл будет потреблять почти 100% времени процессора на одном ядре. Даже тогда он может не дать вам событий точно вовремя, потому что другие задачи с более высоким приоритетом могут привести к потере вашего цикла.


Чтобы создать массив случайных событий:

  • Выберите минимальный интервал времени между событиями и заполните массив со всеми возможными событиями. Например, если вы выберете гранулярность 1 мс, то массив будет иметь 1000 значений: 0, 0.001, 0.002,..., 0.999.
  • Каждая секунда перемещает массив, чтобы рандомизировать порядок элементов.
  • Используйте первые n элементов массива как время срабатывания для события.


Я бы предложил сделать набор объектов TimeSpan, инициализированных равными, и иметь сумму в 1 секунду. Точные значения будут определены вашим n.

Оттуда вы можете компенсировать пары или группы любыми значениями, которые вам нравятся, и ваша сумма будет составлять одну секунду. Например...

var offset = new TimeSpan.FromMilliseconds(10); // 10ms offset
timeSpans[0] += offset;
timeSpans[1] -= offset;

Таким образом, вы можете перемещать их, не влияя на общую сумму. Если вам нужно сделать их так, чтобы больше приходило раньше и меньше приходило поздно, тогда вы могли бы сделать что-то вроде...

timeSpans[0] -= offset;
timeSpans[1] -= offset;
timeSpans[2] += offset;
timeSpans[2] += offset;

Это приведет к тому, что индексы 0 и 1 будут иметь более короткую задержку, а задержка индекса 2 будет вдвое длиннее, но сохранить общую сумму не затронутой.

Единственное, о чем нужно помнить, это то, что ни одна из временных интервалов не должна быть меньше 0, а ваша общая сумма должна быть 1 с, тогда вы золотые. Когда у вас будут распределены ваши объекты TimeSpan, вы можете использовать их для приостановки между событием зажигания x. Вы захотите изменить (рандомизировать?) Свое смещение несколько раз, и каждый раз, когда вы его изменяете, выберите новые временные интервалы (случайным образом?), Чтобы применить смещение. В конце процесса он должен быть довольно хаотичным.

Я могу предоставить более подробный пример кода, если это слишком абстрактно.:)

Надеюсь, это поможет!


using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Diagnostics;
namespace TestProject1
{ [TestClass] public class Test { [TestMethod] public void TestSteps() { var random = new Random(); for (int i = 0; i < 20000; i++) { int numberOfEvents = random.Next(1,1000); var generator = new RandomTimeStepGenerator(numberOfEvents); var stopwatch = new Stopwatch(); stopwatch.Start(); var steps = generator.MillisecondDeltas; Assert.AreEqual(numberOfEvents, steps.Count); var sum = generator.MillisecondDeltas.Sum(); Assert.AreEqual(1000.0,sum,0.1); Assert.IsTrue(stopwatch.ElapsedMilliseconds<10); } } } public class RandomTimeStepGenerator { private readonly int _numberOfEvents; const int timeResolution = 10000; public RandomTimeStepGenerator(int numberOfEvents) { _numberOfEvents = numberOfEvents; } public int NumberOfEvents { get { return _numberOfEvents; } } public List<******> MillisecondDeltas { get { var last=0; var result = new List<******>(); var random = new Random(); for (var i = 0; i < timeResolution && result.Count < _numberOfEvents; i++) { var remainingEvents = _numberOfEvents - result.Count; var remainingTime = timeResolution - i; if(remainingEvents==1) // make sure the last event fires on the second { result.Add((timeResolution - last) / 10.0); last = i; } else if (remainingTime <= remainingEvents) // hurry up and fire you lazy !!! your going to run out of time { result.Add(0.1); } else { ****** probability = remainingEvents / (******)remainingTime; int next = random.Next(0,timeResolution); if ((next*probability) > _numberOfEvents) { result.Add((i - last)/10.0); last = i; } } } return result; } } }
}
</******></******>

licensed under cc by-sa 3.0 with attribution.