依次计算下一组的算法

时间:2020-03-06 15:01:51  来源:igfitidea点击:

我正在寻找一种算法来计算序列中的下一组操作。这是序列的简单定义。

  • 任务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

这是FizzBu​​zz的变相。

而不是通常将3映射为" Fizz"和将5映射为" Buzz",我们将500映射为Task 1A,将1000映射为Task 2A,将3映射为Task 3A,依此类推。

解决方案的详尽列表(或者接近未解决的问题:))可以在这里找到:我们对FizzBu​​zz问题的解决方案是什么?