依次计算下一组的算法
我正在寻找一种算法来计算序列中的下一组操作。这是序列的简单定义。
- 任务1A将每500小时执行一次
- 任务2A将每1000小时执行一次
- 任务3A将每1500小时执行一次
因此,在t = 500时,执行1A。在t = 1000时,同时执行1A和2A,在t = 1500时,同时执行1A和3A,但不要执行2A,因为1500并非1000的倍数。
如果我有实际时间,那会很容易,但是我没有。我所拥有的是任务的历史记录(例如,上次完成[1A + 2A])。
仅仅知道上一次(例如[1A + 2A])并不能决定:
- [1A + 2A]可能在t = 1000时:接下来是[1A + 3A]在t = 1500
- [1A + 2A]可能在t = 5000:下一个是[1A]在t = 5500
是否有针对此的算法?它看起来像是一个熟悉的问题(某种筛子?),但我似乎找不到解决方案。
另外,它必须"扩展",因为我实际上有3个以上的任务。
解决方案
如果我们有足够的历史记录可以完成每个任务的最后两次,则可以重构原始任务序列定义。当它们重合时是偶然的。
该序列必须重复。对于给定的示例,序列将为1A,1A + 2A,1A + 3A,1A + 2A,1A,1A + 2A + 3A。在这种情况下,我们可以看到最后一个1A + 2A + 3A多远,并将该距离用作数组的索引。在一般情况下,对于一个长度为N的循环,我们总是可以通过针对循环的所有旋转测试最后N个事件来完成,但是我怀疑通常会有某种捷径可用,例如有多少个事件可以返回上一次"执行所有操作"事件发生的时间,或者最近一次"执行所有操作"事件发生的时间。
似乎是一个最大的共同点问题。
编辑:
啊,你必须走另一条路。在这种情况下,就像有人提到的那样,我们可以使用三个的最小公倍数来计算有效的@TimeLastJob
--Note: uses some SQL Server 2005 SQL extentions, -- but can still serve as a psuedocode specification of the algorithm DECLARE @constEvaluationPeriodLength int DECLARE @constCycleTimeJob1A int DECLARE @constCycleTimeJob2A int DECLARE @constCycleTimeJob3A int SET @constEvaluationPeriodLength = 500 SET @constCycleTimeJob1A = 500 SET @constCycleTimeJob2A = 1000 SET @constCycleTimeJob3A = 1500 DECLARE @Indicator1ARunAtLastCyclePoint int DECLARE @Indicator2ARunAtLastCyclePoint int DECLARE @Indicator3ARunAtLastCyclePoint int SET @Indicator1ARunAtLastCyclePoint = 1 SET @Indicator2ARunAtLastCyclePoint = 0 SET @Indicator3ARunAtLastCyclePoint = 1 DECLARE @tblPrimeFactors TABLE( TaskId int CycleTimePrimeFactor int ) --Capture the prime factors for each TaskId IF (@Indicator1ARunAtLastCyclePoint = 1) BEGIN INSERT @tblPrimeFactors SELECT TaskId = 1 ,PrimeFactor FROM dbo.tvfGetPrimeFactors(@constCycleTimeJob1A) --Table-valued function left for the reader END IF (@Indicator2ARunAtLastCyclePoint = 1) BEGIN INSERT @tblPrimeFactors SELECT TaskId = 2 ,PrimeFactor FROM dbo.tvfGetPrimeFactors(@constCycleTimeJob2A) --Table-valued function left for the reader END IF (@Indicator3ARunAtLastCyclePoint = 1) BEGIN INSERT @tblPrimeFactors SELECT TaskId = 3 ,PrimeFactor FROM dbo.tvfGetPrimeFactors(@constCycleTimeJob3A) --Table-valued function left for the reader END --Calculate the LCM, which can serve as an effective time --Utilizes SQL Server dynamic table capability --(Inner select statements w/in parenthesis and given the alias names t0 & t1 below) DECLARE @LCM int SELECT --Fun w/ logs/powers to effect a product aggregate function @LCM = Power(sum(log10(power(PrimeFactor,Frequency))),10) FROM ( SELECT PrimeFactor ,Frequency = max(Frequency) FROM ( SELECT PrimeFactor ,Frequency = count(*) FROM @tblPrimeFactors GROUP BY TaskId ,PrimeFactor ) t0 ) t1 DECLARE @TimeLastJob int DECLARE @TimeNextJob int SET @TimeLastJob = @LCM SET @TimeNextJob = @TimeLastJob + @constEvaluationPeriodLength SELECT Indicator1A = 1 - SIGN(@TimeNextJob % @constCycleTimeJob1A) ,Indicator2A = 1 - SIGN(@TimeNextJob % @constCycleTimeJob2A) ,Indicator3A = 1 - SIGN(@TimeNextJob % @constCycleTimeJob3A)
原版的:
模数运算符%应该可以解决问题
如果我没看错的话,我们确实有时间做最后一个任务
- t = 1000或者
- t = 5000
任务选择评估的频率是每500小时。
尝试更改@TimeLastJob,以查看下面的脚本是否为我们提供了所需的内容
DECLARE @constEvaluationPeriodLength int DECLARE @constCycleTimeJob1A int DECLARE @constCycleTimeJob2A int DECLARE @constCycleTimeJob3A int SET @constEvaluationPeriodLength = 500 SET @constCycleTimeJob1A = 500 SET @constCycleTimeJob2A = 1000 SET @constCycleTimeJob3A = 1500 DECLARE @TimeLastJob int DECLARE @TimeNextJob int --SET @TimeLastJob = 1000 SET @TimeLastJob =5000 SET @TimeNextJob = @TimeLastJob + @constEvaluationPeriodLength SELECT Indicator1A = 1 - SIGN(@TimeNextJob % @constCycleTimeJob1A) ,Indicator2A = 1 - SIGN(@TimeNextJob % @constCycleTimeJob2A) ,Indicator3A = 1 - SIGN(@TimeNextJob % @constCycleTimeJob3A)
准备工作:
- 计算任务时间的LCM;这是一个完整周期的时期。
- 计算整个周期的事件时间表。
在启动每个任务/一组任务时,在时间轴上移动一个索引。
比尔蜥蜴是正确的。以下是根据历史记录确定任务间隔的方法(在Python中):
history = [list of tuples like (timestamp, (A, B, ...)), ordered by timestamp] lastTaskTime = {} taskIntervals = {} for timestamp, tasks in history: for task in tasks: if task not in lastTaskTime: lastTaskTime[task] = timestamp else: lastTimestamp = lastTaskTime[task] interval = abs(timestamp - lastTimestamp) if task not in taskIntervals or interval < taskIntervals[task]: taskIntervals[task] = interval # Found a shorter interval # Always remember the last timestamp lastTaskTime[task] = timestamp # taskIntervals contains the shortest time intervals of each tasks executed at least twice in the past # lastTaskTime contains the last time each task was executed
要获取任务集,将在下一个步骤执行:
nextTime = None nextTasks = [] for task in lastTaskTime: lastTime = lastTaskTime[task] interval = taskIntervals[task] if not nextTime or lastTime + interval < nextTime: nextTime = lastTime + interval nextTasks = [task] elif lastTime + interval == nextTime: nextTasks.append(task) # nextTime contains the time when the next set of tasks will be executed # nextTasks contains the set of tasks to be executed
这是FizzBuzz的变相。
而不是通常将3映射为" Fizz"和将5映射为" Buzz",我们将500映射为Task 1A,将1000映射为Task 2A,将3映射为Task 3A,依此类推。
解决方案的详尽列表(或者接近未解决的问题:))可以在这里找到:我们对FizzBuzz问题的解决方案是什么?