【问题标题】:Scheduling Algorithm with limitations有限制的调度算法
【发布时间】:2013-12-30 04:31:25
【问题描述】:

感谢 user3125280,D.W.和 Evgeny Kluev 问题已更新。

我有一个网页列表,我必须经常下载它们,每个网页都有不同的下载频率。根据这个频率,我们将网页分为 5 组:

Items in group 1 are downloaded once per 1 hour
items in group 2 once per 2 hours
items in group 3 once per 4 hours
items in group 4 once per 12 hours
items in group 5 once per 24 hours

这意味着,我们必须在 1 小时内下载所有第 1 组网页,在 2 小时内下载所有第 2 组网页等等。

我正在尝试制作一个算法。作为输入,我有:

a) DATA_ARR = 一个包含 5 个数字的数组。每个数字代表该组中的项目数。

b) TIME_ARR = 一个包含 5 个数字(1、2、4、12、24)的数组,表示下载项目的频率。

b) X = 每小时下载的网页总数。这是使用 items_in_group/download_frequently 计算的并向上四舍五入。 If we have 15 items in group 5, and 3 items in group 4, this will be 15/24 + 3/12 = 0.875 and rounded is 1.

每小时我的程序必须在最多X 个站点下载。我希望算法输出如下内容:

Hour 1: A1 B0 C4 D5
Hour 2: A2 B1 C2 D2
...

A1 = 第一组的第二项
C0 = 第三组的第一项

我的算法必须尽可能高效。这意味着:

a) 模式必须可扩展到至少 200 多个小时
b) 无需创建可重复的模式
c) 尽可能需要空间以使用绝对最小带宽
d) 下载项目的频率永远不要超过更新频率,没有例外


示例:

group 1: 0 items | once per 1 hour
group 2: 3 items | once per 2 hours
group 3: 4 items | once per 4 hours
group 4: 0 items | once per 12 hours
group 5: 0 items | once per 24 hours

我们计算每小时可以带走的物品数量:3/2+4/4 = 2.5. We round this upwards and it's 3.

使用铅笔和纸,我们可以找到以下解决方案:

Hour 1: B0 C0 B1
Hour 2: B2 C1 c2
Hour 3: B0 C3 B1
Hour 4: B2
Hour 5: B0 C0 B1
Hour 6: B2 C1 c2
Hour 7: B0 C3 B1
Hour 8: B2
Hour 9: B0 C0 B1
Hour 10: B2 C1 c2
Hour 11: B0 C3 B1
Hour 12: B2
Hour 13: B0 C0 B1
Hour 14: B2 C1 c2
and continue the above.

我们每 4 小时接受一次 C0C1 C2C3。我们还每 2 小时服用一次 B0B1B2


问题:请给我解释一下,如何设计一个能够下载项目的算法,同时使用绝对最小下载次数?蛮力不是解决方案和算法必须是高效的 CPU 明智的,因为元素的数量可能很大。

您可以阅读此处发布的答案:https://cs.stackexchange.com/a/19422/12497 以及 user3125280 在下面发布的答案。

【问题讨论】:

  • 更好,但我们称这是一个 c++ 问题,并删除所有其他语言的标签。
  • 您的具体问题是什么?
  • @paqogomez 我看不到 C++;我删除了除“c”之外的所有内容,哎呀,错过了标准输出
  • 您不应该重新发布已删除的问题,您应该努力修复previous post,但实际上只有 10k+ 用户可以查看它。
  • 我添加了更多标签,以便更多人查看我所理解的内容,并且可以使用我标记的所有语言编写代码。 cout 也是 C++。不管怎样,我是新人,所以我会在这里听前辈的。 @Digital_Reality:在给定的“刷新时间”内,我们必须至少从该组中取出每个项目一次。您可以在 codepad.org 上运行我的代码并查看。这将帮助您了解更多。

标签: algorithm


【解决方案1】:

你的问题是一个典型的调度问题。这类问题在计算机科学中得到了很好的研究,因此有大量文献可供参考。

代码有点像Deficit round robin,但有一些简化。首先,我们通过添加data_to_process 变量来自己提供队列。其次,队列只是遍历一个值列表。

一个不同之处在于,此解决方案将获得您想要的最佳值,除非出现数学错误。

粗略:尚未编译 (c++11) 基于 unix 的规范代码

#include <iostream>
#include <vector>
#include <numeric>
#include <unistd.h>
//#include <cmath> //for ceil

#define TIME_SCALE ((double)60.0) //1 for realtime speed

//Assuming you are not refreshing ints in the real case
template<typename T>
struct queue
{
    const std::vector<T> data; //this will be filled with numbers
    int position;

    double refresh_rate; //must be refreshed ever ~ hours
    double data_rate; //this many refreshes per hour
    double credit; //amount of refreshes owed

    queue(std::initializer_list<T> v, int r ) :
        data(v), position(0), refresh_rate(r), credit(0) {
        data_rate = data.size() / (double) refresh_rate;
    }

    int getNext() {
        return data[position++ % data.size()];
    }
};

double time_passed(){
static double total;
//if(total < 20){ //stop early
    usleep(60000000 / TIME_SCALE); //sleep for a minute
    total += 1.0 / 60.0; //add a minute
    std::cout << "Time: " << total << std::endl;
    return 1.0; //change to 1.0 / 60.0 for real time speed
//} else return 0;
}

int main()
{
    //keep a list of the queues
    std::vector<queue<int> > queues{
    {{1, 2, 3}, 2},
    {{1, 2, 3, 4}, 3}};

    double total_data_rate = 0;
    for(auto q : queues) total_data_rate += q.data_rate;

    double data_to_process = 0; //how many refreshes we have to do
    int queue_number = 0; //which queue we are processing

    auto current_queue = &queues[0];

    while(1) {
        data_to_process += time_passed() * total_data_rate;
        //data_to_process = ceil(data_to_process) //optional

        while(data_to_process >= 1){
            //data_to_process >= 0 will make the the scheduler more
            //eager in the first time period (ie. everything will updated correctly
            //in the first period and and following periods
            if(current_queue->credit >= 1){
            //don't change here though, since credit determines the weighting only,
            //not how many refreshes are made
                //refresh(current_queue.getNext();
                std::cout << "From queue " << queue_number << " refreshed " <<
                current_queue->getNext() << std::endl;
                current_queue->credit -= 1;
                data_to_process -= 1;
            } else {
                queue_number = (queue_number + 1) % queues.size();
                current_queue = &queues[queue_number];
                current_queue->credit += current_queue->data_rate;
            }
        }
    }
   return 0;
}

该示例现在应该在 gcc 上使用 --std=c++11 编译,并为您提供所需的内容。

这里是测试用例输出:(对于非时间缩放的早期代码)

Time: 0
From queue 1 refreshed 1
From queue 0 refreshed 1
From queue 1 refreshed 2
Time: 1
From queue 0 refreshed 2
From queue 0 refreshed 3
From queue 1 refreshed 3
Time: 2
From queue 0 refreshed 1
From queue 1 refreshed 4
From queue 1 refreshed 1
Time: 3
From queue 0 refreshed 2
From queue 0 refreshed 3
From queue 1 refreshed 2
Time: 4
From queue 0 refreshed 1
From queue 1 refreshed 3
From queue 0 refreshed 2
Time: 5
From queue 0 refreshed 3
From queue 1 refreshed 4
From queue 1 refreshed 1

作为扩展,通过允许此调度程序仅完成第一个 lcm(update_rate * lcm(...refresh rates...), ceil(update_rate)) 步骤,然后重复该模式来回答重复模式问题.

另外:这实际上有时是无法解决的,因为需要时间限制。当我使用您无法解决的示例并将 time_passed 修改为返回 0.1 时,计划通过每 1.1 小时更新一次来解决(而不是在小时边界!)。

【讨论】:

  • 嘿,感谢您指出这一点。它接近我的问题。您能找到解释其工作原理的任何来源吗?
  • 在这种情况下,很容易计算出每单位时间必须从每个列表中提取多少,然后在每个时间单位开始时进行提取(小时/秒/无论如何) .我建议使用调度类比,因为事情似乎有点简化,调度可以帮助处理更复杂的情况
  • 你说的对,但确实不足以解决这个难题。
  • 我的算法与加权公平队列有一些共同点,但也有一些区别。总而言之,阅读网络上关于该主题的极少数网站并不能帮助我解决这个问题。
  • @Luka 我之前写了一些代码,我会在几分钟后发布它
【解决方案2】:

看来你的限制无处不在。快速总结一下我的其他答案:

  • 它仅满足平均刷新率
  • 满足上述要求的每小时下载次数最少

它基于这些(有时无法实现的)约束

  1. 每隔 1 小时更新一次
  2. 每次更新最少的项目
  3. 以固定的时间间隔更新每个项目

并打破了 3.

由于每小时间隔和最少每次限制都不是真正必要的,我将在这里给出一个更简单、更好的答案,它打破了 2。

#include <iostream>
#include <vector>
#include <numeric>
#include <unistd.h>

#define TIME_SCALE ((double)60.0)

//Assuming you are not refreshing ints in the real case
template<typename T>
struct queue
{
    const std::vector<T> data; //this is the data to refresh
    int position; //this is the data we are up to
    double refresh_rate; //must be refreshed every this many hours
    double data_rate; //this many refreshes per hour
    double credit; //is owed this many refreshes
    const char* name;//a name for each queue

    queue(std::initializer_list<T> v, int r, const char* n ) :
        data(v), position(0), refresh_rate(r), credit(0), name(n) {
        data_rate = data.size() / (double) refresh_rate;
    }

    void refresh() {
        std::cout << "From queue " << name << " refreshed " << data[position++ % data.size()] << "\n";
    }
};

double time_passed(){
static double total;
    usleep(60000000 / TIME_SCALE); //sleep for a minute
    total += 1.0; //add a minute
    std::cout << "Time: " << total << std::endl;
    return 1.0; //change to 1.0 / 60.0 for real time speed
}

int main()
{
    //keep a list of the queues
    std::vector<queue<int> > queues{
        {{1}, 1, "A"},
        {{1}, 2, "B"}};

    while(1) {
        auto t = time_passed();
        for(queue<int>& q : queues) {
            q.credit += q.data_rate * t;
            while(q.credit >= 1){
                q.refresh();
                q.credit -= 1.0;
            }
        }
    }
   return 0;
}

但是,它有可能在同一小时安排多次刷新。还有第三种选择,它打破了小时间隔规则,一次只更新一个。

我认为这是最简单的,需要最少的更新次数(如上一个答案),但不违反规则 3。

【讨论】:

    猜你喜欢
    • 2020-02-26
    • 1970-01-01
    • 1970-01-01
    • 2017-09-06
    • 2013-04-27
    • 1970-01-01
    • 2020-10-02
    • 1970-01-01
    • 2013-05-14
    相关资源
    最近更新 更多