在每个用户的窗口内选择随机时间的算法
本文关键字:选择 随机 时间 算法 窗口 用户 | 更新日期: 2023-09-27 18:05:26
我需要每天随机选择N个事件,但它们不能太接近(M),所以N个事件必须在一个特定的窗口(W)内相隔至少M。在这种情况下,我考虑的窗口是12小时。
- N =事件个数
- T =事件发生的时间(UTC)
- M =最小间距(小时)。
- W =事件窗口(从现在到现在+ 12小时)。
- U =用户(可能对这个问题不重要)
我可能会弄清楚这个问题,但我认为这将是一个有趣的StackOverflow问题,并对人们如何解决它感兴趣。
Thanks in advance:)
更新:将答案移动到答案
试试这个:
它随机分割可用时间(window - count * minimum),然后对时间进行排序并添加最小数量以生成最终的事件数组T[]
。
static Random rnd=new Random();
static void Main(string[] args)
{
double W=12;
double M=1.0;
int N=7;
double S=W-(N-1)*M;
double[] T=new double[N];
for(int i=0; i<N; i++)
{
T[i]=rnd.NextDouble()*S;
}
Array.Sort(T);
for(int i=0; i<N; i++)
{
T[i]+=M*i;
}
Console.WriteLine("{0,8} {1,8}", "#", "Time");
for(int i=0; i<N; i++)
{
Console.WriteLine("{0,8} {1,8:F3}", i+1, T[i]);
}
// With N=3, Window 12h, Min. Span = 5h
// # Time
// 1 0.468
// 2 5.496
// 3 10.529
// With N=7, Window 12h, Min. Span = 1h
// # Time
// 1 0.724
// 2 2.771
// 3 4.020
// 4 5.790
// 5 7.331
// 6 9.214
// 7 10.673
}
作为检查,当最小时间完全覆盖时间窗口时,事件是等间隔的。因此,对于12小时窗口上的3个事件,最小时间为6小时,该算法将按照预期在0.0,6.0和12.0产生事件。
您可以使用我在这里的问题的想法:生成非连续组合,本质上要求您只解决M=0的情况。
如果你想跳过描述,该算法在文章的末尾给出,它没有不可预测的while循环等,并且保证是O(N log N)时间(如果没有排序步骤,将是O(N))。
长描述
为了将一般的M情况简化为M=0的情况,我们将每个可能的组合(具有"至少M"约束)映射为不具有"至少M"间隔约束的组合。
如果你的事件是在T1, T2, .., TN
,那么T1 <= T2 -M, T2 <= T3 - M ...
你将它们映射到Q1, Q2, .. QN
,这样
Q1 = T1
Q2 = T2 - M
Q3 = T3 - 2M
...
QN = TN - (N-1)M.
这些Q满足Q1 <= Q2 <= ... <= QN
的性质,并且映射是1到1。(从T
可以构造Q
,从Q
可以构造T
)。
所以你所需要做的就是生成Q
(本质上是M=0
的情况),并将它们映射回T
。
注意生成Q
的窗口变成了[Now, Now+12 - (N-1)M]
要解决M=0
问题,只需在您的窗口中生成N
随机数并对它们进行排序。
<
最终算法/strong>
那么整个算法就是
Step 1) Set Window = [Start, End - (N-1)M]
Step 2) Generate N random numbers in the Window.
Step 3) Sort the numbers generated in Step 2. Call them Q1, Q2, .. , QN
Step 4) Create Ti with the formula Ti = Qi + (i-1)M, for i = 1 to N.
Step 5) Output T1,T2,..,TN
如果我们假设事件是瞬间发生的(因此,可以在time = end of window发生),您可以这样做:
//Using these, as defined in the question
double M;
int N;
DateTime start; //Taken from W
DateTime end; //Taken from W
//Set these up.
Random rand = new Random();
List<DateTime> times;
//This assumes that M is
TimeSpan waitTime = new TimeSpan.FromHours(M);
int totalSeconds = ((TimeSpan)end-start).TotalSeconds;
while( times.Count < N )
{
int seconds = rand.Next(totalSeconds);
DateTime next = start.AddSeconds(seconds);
bool valid = true;
if( times.Count > 0 )
{
foreach( DateTime dt in times )
{
valid = (dt > next && ((TimeSpan)dt - next) > waitTime) ? true : false;
if( !valid )
{
break;
}
}
}
if( valid )
{
times.Add(next);
}
}
现在,在一个12小时的窗口中,每个事件在下一个事件之前至少有一个小时,你最好有一个小N -我上面的伪代码没有检查是否有可能将N个事件放入X时间中,每个事件之间有M个小时。
timeItems = new List();
int range;
double randomDouble;
for i = 1 to N
{
range = W/M;
//assumes generate produces a random number between 0 and 1 (exclusive)
randomDouble = RandomGenerator.generate() * (range);
T = Math.floor(randomDouble)*M;
timeItems.add(T);
}
return timeItems
首先考虑生成一个事件的任务,这样就有(n-1)多个事件要生成(每个事件需要至少间隔M个事件),并且总共剩下w时间。
时间t可以在0到w-(n-1)m之间。t的平均值应该是w/(n-1)现在,使用任何你喜欢的分布(我推荐使用泊松)来生成一个平均值为w/(n-1)的随机数。如果数字大于w-n-1)m,则重新生成。这将给你t。
递归调用(offset=offset+t, w=w-t, n=n-1, m=m)生成更多的数字
def generate(offset, w, n, m):
mean = w/(n-1);
t=ininity;
while (t> (w-(n-1)m)):
t= poisson( w/(n-1) )
return [t+offset] + generate(offset+t, w-t, n-1, m)
我没有对角落条件和其他情况进行编码,我把它留给你。
这是我的解决方案。如果你的时间间隔和nummofevents冲突,你会得到一些奇怪的行为。摆弄一下,看看会发生什么。
using System;
using System.Collections.Generic;
namespace RandomScheduler
{
class Program
{
public static Random R = new Random();
static void Main()
{
var now = DateTime.Now;
var random = new Random((int)now.Ticks);
const int windowOfHours = 12;
const int minimumTimeApartInHours = 2;
const int numOfEvents = 5;
// let's start the window 8 AM
var start = new DateTime(now.Year, now.Month, now.Day, 8, 0, 0, 0);
// let's end 12 hours later
var end = start.AddHours(windowOfHours);
var prev = null as DateTime?;
const int hoursInEachSection = windowOfHours / numOfEvents;
var events = new List<DateTime>();
for (var i = 0; i < numOfEvents; i++)
{
// if there is a previous value
// let's at least start 2 hours later
if (prev.HasValue)
start = prev.Value.AddHours(minimumTimeApartInHours);
DateTime? @event;
do
{
// pick a random double, so we get minutes involved
var hoursToAdd = random.NextDouble()*hoursInEachSection;
// let's add the random hours to the start
// and we get our next event
@event = start.AddHours(hoursToAdd);
// let's make sure we don't go past the window
} while (@event > end);
prev = @event;
events.Add(@event.Value);
}
Console.WriteLine(string.Join("'n", events));
Console.ReadLine();
}
}
}