【问题标题】:How to efficiently mitigate "cycle bias"如何有效缓解“周期偏差”
【发布时间】:2020-06-14 16:14:15
【问题描述】:

我正在遍历矩阵元素的邻居,以便找到最小的元素,如下所示,同时还保存其坐标:

int minimum_r = curr_r; 
int minimum_c = curr_c;
uint8_t tmp_value = 255;

for (int i = curr_r - 1; i <= curr_r + 1; i++) {
    for (int j = curr_c - 1; j <= curr_c + 1; j++) {
        uint8_t value = src[i * cols + j];
        if (value < tmp_value) {
            tmp_value = value;
            minimum_r = i;
            minimum_c = j;
        }
    }
}

我注意到的问题是,如果有多个具有相同value 的最小邻居,则将选择第一个要检查的邻居。我记得在某处读到过这个“周期偏差”问题,但我不记得正确的术语了。

sn-p 来自一种计算梯度下降的算法,我想也许我可以通过找到“亚像素”梯度来缓解这个问题。我在这里错过了一个更简单的解决方案吗?

欢迎提出任何建议。

编辑:

说清楚,我的主要问题是: 这个编程问题通常是如何提到的,如果有的话,以便我可以在网上查找它(我记得以前读过它,并且以特定的方式命名)。

【问题讨论】:

  • 请添加一个矩阵示例,显示 a) 你得到的结果和 b) 你期望的结果。看起来它会选择第一个,但当你写“最后一个”时,你的意思可能不同
  • "最后一个被选中的" - 你确定吗?对我来说,这似乎是第一个。
  • 看起来好像不是所有的邻居都被检查了——你是不是想使用i &lt;= curr_r + 1
  • @user445082 请将问题编辑为 1) 更正第一个/最后一个问题和 2) 解释您的问题是什么 - 目前还不清楚。
  • @user445082 所以问题是算法 总是 选择第一个 - 是吗?那么你想要什么呢?最后一个?左边那个?一个随机的?你需要澄清你在寻找什么!

标签: c++ c algorithm


【解决方案1】:

如何有效缓解“周期偏差”? ...我注意到的问题是,如果有多个具有相同值的最小邻居,则将选择第一个要检查的邻居。

正如我在评论中提到的,避免偏见的一种方法是随机选择其中一个邻居。

为了使其相当快速和准确,我会创建一个邻居列表 - 并且为了不偏袒任何邻居而不是其他人,我会创建一个表,其中包含可以搜索邻居的所有排列顺序。然后,您只需要随机选择一个排列即可保证选择无偏差。

想法:

#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <iostream>
#include <random>
#include <type_traits>
#include <utility>
#include <vector>

inline std::mt19937& generator() {
    static thread_local std::mt19937 gen(std::random_device{}());
    return gen;
}

template<typename T, std::enable_if_t<std::is_integral_v<T>>* = nullptr>
T my_rand(T min, T max) {
    std::uniform_int_distribution<T> dist(min, max);
    return dist(generator());
}

auto make_permutations() {
    std::vector<std::array<std::pair<int8_t, int8_t>, 8>> result;
    result.reserve(40320); // * 2 == 80640 bytes

    // it's important that this entered as the first permutation (sorted)
    std::array<std::pair<int8_t, int8_t>, 8> ne{
        {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}}};

    do { // loop over all the permutations and store them
        result.push_back(ne);
    } while(std::next_permutation(ne.begin(), ne.end()));

    return result;
}

using rc = std::pair<size_t, size_t>; // row column alias

rc get_lowest_neighbour(size_t curr_r, size_t curr_c,
                        const uint8_t (&src)[rows * cols]) {
    static const auto nperm = make_permutations();

    // select a random permutation
    const auto& neighbours = nperm[my_rand(0ul, nperm.size() - 1)];

    // initialize low-score with the first neighbour in the permutation
    auto it = neighbours.begin();

    size_t r = curr_r + it->first;
    size_t c = curr_c + it->second;
    uint8_t tmp_value = src[r * cols + c];
    rc result = {r, c};

    // search the rest of the neighbours for a lower score
    for(++it; it != neighbours.end(); ++it) {
        r = curr_r + it->first;
        c = curr_c + it->second;
        uint8_t value = src[r * cols + c];
        if(value < tmp_value) {
            tmp_value = value;
            result = {r, c};
        }
    }

    return result;
}

//...
auto [row, col] = get_lowest_neighbour(y, x, src);

【讨论】:

    【解决方案2】:

    如果考虑的元素数量足够大并且选择不需要稳定,则随机化选择的元素是一种局部解决方案。

    最简单的方法是将要查看的第一个元素随机化(即不要一直从同一个角落开始)。

    如果不能选择随机化,请使用“从哪个角落开始”计数器,每次检查都会递增。

    要实现其中任何一个,请将偏移量移动到列表中:

    int offs[][2] = { {-1,-1 }, { -1, 0 }, { -1, 1 }, .... };
    

    你现在可以循环了:

    for(int neighbourIdx = 0; neighbourIdx < 8; ++neighbourIdx)
    // look at neighbour [curr_r+offs[neighbourIdx][0], curr_c+offs[neighbourIdx][1]]
    

    这相当于您的解决方案。要允许任意开始,请将offs 加倍,遍历所有邻居两次,然后从任意偏移量开始

    int startOffset = random(0,7); // or (++counter) % 8
    for(int neighbourIdx = startOffset; neighbourIdx < startOffset+8; ++neighbourIdx)
    // ... as above
    

    【讨论】:

      猜你喜欢
      • 2023-04-10
      • 2020-09-24
      • 1970-01-01
      • 2023-03-12
      • 2013-04-20
      • 2019-02-06
      • 2021-10-31
      • 1970-01-01
      • 2017-05-08
      相关资源
      最近更新 更多