我想计算 n 工作人员的任务均匀分布。任务由成对比较组成;每个 m 项目都与自己和其他项目进行比较。我想避免冗余的比较。
例如,4个项目和3个工作人员将是(4 + 3 + 2 + 1 = 10)个任务,这些任务将分配给3个工作人员。每个工作人员最多可获得ceil(10/3)
个任务。它们分布如下。我生成一个 m * m 矩阵并取下它的一半(对角线分开),如下所示:
1 2 3 4
1 x - - -
2 x x - -
3 x x x -
4 x x x x
我走遍矩阵,将工作任务分配给工人,最后下一个工人完成任务。
我在Perl中实现了这个功能,效果很好:
my $total = 0;
for (my $i = 0; $i < $nitems; $i++) {
for (my $j = 0; $j <= $i; $j++) { $total++ }
}
my $tasksperworker = ceil($total / $nworkers);
my $worker = [ ];
push @$worker, { 'imin' => 1, 'imax' => 1, 'jmin' => 1, 'jmax' => 1 } for (1 .. $nworkers);
my $k = 0;
for (my $i = 0; $i < $nitems; $i++) {
for (my $j = 0; $j <= $i; $j++) {
# start a new worker if this one would be overloaded
if ($tasksforthisworker + 1 > $tasksperworker) {
$k++;
$$worker[$k]{'imin'} = $i;
$$worker[$k]{'imax'} = $i;
$$worker[$k]{'jmin'} = $j;
$$worker[$k]{'jmax'} = $j;
$tasksforthisworker = 1;
}
else {
$$worker[$k]{'imin'} = $i if $$worker[$k]{'imin'} > $i;
$$worker[$k]{'imax'} = $i if $$worker[$k]{'imax'} < $i;
$$worker[$k]{'jmin'} = $j if $$worker[$k]{'jmin'} > $j;
$$worker[$k]{'jmax'} = $j if $$worker[$k]{'jmax'} < $j;
$tasksforthisworker++;
}
}
}
我需要为 m 的更大值计算此值。 Perl版本一直在计算 n = 8和 m = 1397704的输入值的周末。我意识到这导致了大量的任务,但他们需要尽管如此。所以Perl版本仍在处理中,我想用C ++实现这个东西以提高效率。我想我在这里复制了算法:
// populate the workers array
vector <map <string, int> > workers (nworkers);
for (int i = 0; i < nworkers; i++) {
map <string, int> worker;
worker["imin"] = 1;
worker["imax"] = 1;
worker["jmin"] = 1;
worker["jmax"] = 1;
workers[i] = worker;
}
// calculate total number of tasks and tasks per worker
int total = 0;
for (int i = 0; i < nitems; i++) {
for (int j = 0; j <= i; j++) total++;
}
int tasksperworker = ceil( total / nworkers );
// distribute tasks across workers
int tasksforthisworker = 0;
int i = 0;
int j = 0;
int k = 0;
for (i = 0; i < nitems; i++) {
for (j = 0; j <= i; j++) {
// start a new worker if this one would be overloaded
if (tasksforthisworker + 1 > tasksperworker) {
// this would exceed the number of workers!
assert(k + 1 > workers.size());
k++;
workers.at(k)["imin"] = i;
workers.at(k)["imax"] = i;
workers.at(k)["jmin"] = j;
workers.at(k)["jmax"] = j;
tasksforthisworker = 1;
}
else {
if (workers.at(k)["imin"] > i) workers.at(k)["imin"] = i;
if (workers.at(k)["imax"] < i) workers.at(k)["imax"] = i;
if (workers.at(k)["jmin"] > j) workers.at(k)["jmin"] = j;
if (workers.at(k)["jmax"] < j) workers.at(k)["jmax"] = j;
tasksforthisworker++;
}
}
}
这给了我一个错误,因为k
在某些时候超过了workers.size()
:
terminate called after throwing an instance of 'std::out_of_range'
what(): vector::_M_range_check
assert()
标记发生错误的位置。
我的问题是:为什么在C ++版本中会发生这种情况,而在Perl版本中却没有?我在C ++实现中遗漏了什么(可能是这样)?
还可以了解更有效地计算此任务分布的指针。当我在思考这个问题时,这个算法是第一个出现在我脑海中的算法。
答案 0 :(得分:2)
有两个问题:
你在整数运算的结果上调用ceil,它已经是一个底层,所以你实际上得到的是ceil(floor(x / y))的结果。将行更改为:
// Ceiling of total divided by nworkers
int tasksperworker = (total + nworkers - 1)/ nworkers;
断言中的不平等应该是&lt; :
assert(k + 1 < workers.size());
然后它会起作用。
有趣(但不相关)的事实:你可以通过使用std :: generate_n进行矢量初始化来使它更像perl:
vector <map <string, int> > workers (nworkers);
std::generate_n(back_inserter(workers), nworkers, []{
return std::map<string, int>{
{ "imin", 1 },
{ "imax", 1 },
{ "jmin", 1 },
{ "jmax", 1 },
};});
答案 1 :(得分:0)
在访问k
的任何元素之前,您正在递增workers
。这意味着永远不会访问索引为0的workers
元素,并且您可能尝试在某个时刻访问索引为workers.size()
的元素。可以访问的最后一个元素具有索引workers.size() - 1
。虽然Perl似乎按需扩展数组,但是在C ++中访问数组或向量的元素会导致错误,而使用.at()
进行访问会产生异常。