【问题标题】:Two versions of a function are not producing the same result函数的两个版本不会产生相同的结果
【发布时间】:2019-10-26 13:03:51
【问题描述】:

假设我们有一个由01 填充的stack。我想将mutation_rate_ 的许多元素从0 更改为1,反之亦然。应该“变异”的元素是随机选择的。我为此编写了两个函数,我将在下面发布一个测试用例,但其中一个似乎无法正常工作。

#include <stack>
#include <vector>
#include <random>
#include <set>
#include <algorithm>
#include <iostream>

using age_t = unsigned int;

std::stack<age_t>  mutate( std::stack<age_t>  genome_, age_t mutation_rate_ = 3){
    std::stack<age_t> final_stack;

    // Generate a set, whose elements are the positions at which we perforem the mutation.
    std::set<age_t> positions_to_switch;
    while(positions_to_switch.size() < mutation_rate_){
        positions_to_switch.insert(rand() % genome_.size());
    }

    // Create a counter to go through the stack amd iterator of the set.
    age_t cnt = 0;
    std::set<age_t>::iterator it= positions_to_switch.begin();

    while(genome_.empty()!=true){                               // Go through the whole genome_
        if(*it==cnt){                                           // check if current position in stack matches with
                                                                // element that needs mutation
            if(genome_.top()==1) final_stack.push(0);           // mutate
            else final_stack.push(1);
            ++it;                                               // go to next element that needs mutation
        }
        else{                                                   // if no mutation is needed
            final_stack.push(genome_.top());
        }
        genome_.pop();                                          // go to next element in stack
        ++cnt;                                                  // increase counter of stack
    }

    // final stack is in reverse order
    std::stack<age_t> res;
    while(!final_stack.empty()){
        res.push(final_stack.top());
        final_stack.pop();
    }
    return res;
};

std::stack<age_t> mutate_2( std::stack<age_t>  genome_, age_t mutation_rate_ = 3){
    std::stack<age_t> final_stack;

    std::vector<age_t> enumeration;
    age_t pos = 0;
    while(enumeration.size() < genome_.size()){
        enumeration.push_back(pos);
        ++pos;
    }
    pos = 0;

    auto rng = std::default_random_engine {};
    std::shuffle(enumeration.begin(), enumeration.end(), rng);

    std::vector<age_t> positions_to_switch;
    while(positions_to_switch.size() < mutation_rate_){
        positions_to_switch.push_back(enumeration[pos]);
        ++pos;
    }

    // Create a counter to go through the stack amd iterator of the set.
    age_t cnt = 0;
    std::vector<age_t>::iterator it= positions_to_switch.begin();

    while(genome_.empty()!=true){                           // Go through the whole genome_
        if(*it==cnt){                                       // check if current position in stack matches with
                                                            // element that needs mutation
            if(genome_.top()==1) final_stack.push(0);       // mutate
            else final_stack.push(1);
            ++it;                                           // go to next element that needs mutation
        }
        else{                                               // if no mutation is needed
            final_stack.push(genome_.top());
        }
        genome_.pop();                                      // go to next element in stack
        ++cnt;                                              // increase counter of stack
    }

    // final stack is in reverse order
    std::stack<age_t> res;
    while(!final_stack.empty()){
        res.push(final_stack.top());
        final_stack.pop();
    }
    return res;
};

int main(){

    std::stack<age_t> test_stack;
    test_stack.push(1);
    test_stack.push(0);
    test_stack.push(0);
    test_stack.push(0);
    test_stack.push(1);
    test_stack.push(1);

    std::stack<age_t> test_stack_copy = test_stack;
    std::stack<age_t> test_stack_copy_2 = test_stack;

    std::stack<age_t> res = mutate(test_stack);
    std::stack<age_t> res_2 = mutate_2(test_stack);


    std::cout << "Original Stack:\t";
    while(test_stack_copy_2.empty()!=true){
        std::cout << test_stack_copy_2.top();
        test_stack_copy_2.pop();
    }
    std::cout << "\n";

    std::cout << "Muatet:\t\t";
    while(res.empty()!=true){
        std::cout << res.top();
        res.pop();
    }
    std::cout << "\n";

    std::cout << "Muatet_2:\t";
    while(res_2.empty()!=true){
        std::cout << res_2.top();
        res_2.pop();
    }
    std::cout << "\n";

    return 0;
}

考虑到测试用例110001mutate 返回100111mutate_2 返回111101,其中我设置了mutation_rate_=3,所以初始堆栈的三个元素应该已更改。我们可以直接看到mutate确实改变了三个条目,而mutate_2只改变了其中两个……我很确定这与setvector对象的处理方式有关,但我就是找不到错误...

【问题讨论】:

  • 我没有检查整个代码,可能还有其他错误,但*it==cnt应该是it!=positions_to_switch.end() &amp;&amp; *it==cnt
  • 你检查了集合/向量positions_to_switch的内容吗?
  • @MikeCAT 是的,我打印了它们,尺寸和内容也很合理。大小为muattion_rate_,内容为usnigned int 介于零和genome.size()之间的数字
  • 您是否尝试过进行代码演练?随机性可能很难通过,但您已经打印出positions_to_switch 的内容,所以这应该不是问题。 (如果您需要测试用例,请尝试positions_to_switch = { 2, 3, 1 }。)

标签: c++ algorithm sorting vector set


【解决方案1】:

在第一种情况下,您使用的 std::set 包含按升序排列的元素(位置)。

在第二种情况下,带有位置的向量没有排序。

包括对向量的排序

std::sort( positions_to_switch.begin(), positions_to_switch.end() );

【讨论】:

    【解决方案2】:

    std::set 的内容会自动排序,但std::vector 的内容不会。

    当shuffle的结果不是升序时(例如{2, 1, 3}), 小元素(示例中的1)被忽略,因为在检查更大的元素(示例中的2)时会跳过该值。

    添加排序

        std::sort(positions_to_switch.begin(), positions_to_switch.end());
    

    在元素添加部分之后

        std::vector<age_t> positions_to_switch;
        while(positions_to_switch.size() < mutation_rate_){
            positions_to_switch.push_back(enumeration[pos]);
            ++pos;
        }
    

    会解决你的问题。

    另一个问题是迭代器it在拖曳*it==cnt中读取时可能位于最后一个元素之后。
    检查应该像it!=positions_to_switch.end() &amp;&amp; *it==cnt一样插入。
    (在这段代码中,it==positions_to_switch.end() 时不会读取it,这要归功于短路评估。)

    【讨论】:

    • 你能解释一下为什么第二种情况可能是一个问题吗?我的意思是我显然知道这可能是个问题,但 it 不应该指向 positions_to_switch.end() 后面的元素,因为我只是在匹配时增加它..
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-10
    • 1970-01-01
    相关资源
    最近更新 更多