依次计算下一组的算法
我正在寻找一种算法来计算序列中的下一组操作。这是序列的简单定义。
- 任务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问题的解决方案是什么?

