Подтвердить что ты не робот

Алгоритм планирования с ограничениями

Благодаря пользователю3125280, D.W. и Евгений Клюев вопрос обновляется.

У меня есть список веб-страниц, и я должен их часто скачивать, каждая веб-страница имеет другую частоту загрузки. Основываясь на этой частоте, мы группируем веб-страницы в 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.

Каждый час моя программа должна загружаться на сайтах max X. Я ожидаю, что алгоритм выдаст что-то вроде:

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

A1 = второй элемент 1-й группы
C0 = 1-й элемент 3-й группы

Мой алгоритм должен быть максимально эффективным. Это означает:

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 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.

Возьмем C0, C1 C2 и C3 один раз каждые 4 часа. Мы также принимаем B0, B1 и B2 один раз каждые 2 часа.


Вопрос: Пожалуйста, объясните мне, как разработать алгоритм, способный загружать элементы, при использовании абсолютного минимального количества загрузок? Грубая сила НЕ a решение, и алгоритм должен быть эффективным процессором, потому что количество элементов может быть огромным.

Вы можете прочитать ответ, размещенный здесь: https://cs.stackexchange.com/a/19422/12497, а также ответ, отправленный ниже пользователем3125280.

4b9b3361

Ответ 1

Проблема заключается в типичной проблеме планирования. Эти проблемы хорошо изучены в информатике, поэтому существует огромное количество литературы для консультаций.

Этот код похож на Дефицит round robin, но с несколькими упрощениями. Во-первых, мы сами кормим очереди, добавляя к переменной data_to_process. Во-вторых, очереди просто перебирают список значений.

Одно отличие состоит в том, что это решение получит оптимальное значение, которое вы хотите, за исключением математической ошибки.

Грубый эскиз: не скомпилирован (С++ 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 = С++ 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...), ceil (update_rate)), а затем повторить шаблон.

ТАКЖЕ: это действительно будет неразрешимо иногда из-за требования на часовых границах. Когда я использую ваш неразрешимый пример и изменяю time_passed, чтобы вернуть 0.1, расписание разрешается с обновлениями каждые 1,1 часа (только не на границах часов!).

Ответ 2

Кажется, ваши ограничения повсюду. Чтобы быстро подытожить мой другой ответ:

  • Он соответствует частотам обновления только в среднем
  • Наименьшее количество загрузок с часовыми интервалами, необходимыми для выполнения вышеуказанных

Он основывался на этих (иногда невыполнимых) ограничениях

  • Обновление с дискретными интервалами в 1 час.
  • Обновлять наименьшее количество элементов каждый раз
  • Обновление каждого элемента с фиксированными интервалами

и сломал 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.