为什么我在C ++中超出范围而在Perl中没有?

时间:2014-12-22 12:44:06

标签: c++ perl

我想计算 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 ++实现中遗漏了什么(可能是这样)?

还可以了解更有效地计算此任务分布的指针。当我在思考这个问题时,这个算法是第一个出现在我脑海中的算法。

2 个答案:

答案 0 :(得分:2)

有两个问题:

  1. 你在整数运算的结果上调用ceil,它已经是一个底层,所以你实际上得到的是ceil(floor(x / y))的结果。将行更改为:

    // Ceiling of total divided by nworkers
    int tasksperworker = (total + nworkers - 1)/ nworkers;
    
  2. 断言中的不平等应该是&lt; :

    assert(k + 1 < workers.size());
    
  3. 然后它会起作用。

    有趣(但不相关)的事实:你可以通过使用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()进行访问会产生异常。