【问题标题】:Evenly distribute values into array将值均匀分布到数组中
【发布时间】:2020-01-15 18:22:34
【问题描述】:

我有一个大小为 8 的固定大小的布尔数组。数组中所有元素的默认值为 false。在 1-8 之间将有许多真值需要填充。

我想将真值分布在尽可能远的地方。我也希望能够随机化配置。在这种情况下,数组环绕,因此位置 7 与数组中的位置 0“相邻”。

以下是填充值的一些示例。我没有包括所有可能性,但希望它能传达我的意思。

1:[1, 0, 0, 0, 0, 0, 0, 0][0, 1, 0, 0, 0, 0, 0, 0]

2:[1, 0, 0, 0, 1, 0, 0, 0][0, 1, 0, 0, 0, 1, 0, 0]

3:[1, 0, 0, 1, 0, 0, 1, 0][0, 1, 0, 0, 1, 0, 0, 1]

4:[1, 0, 1, 0, 1, 0, 1, 0][0, 1, 0, 1, 0, 1, 0, 1]

5:[1, 1, 0, 1, 1, 0, 1, 0]

6:[1, 1, 0, 1, 1, 1, 0, 1]

7:[1, 1, 1, 1, 1, 1, 1, 0]

8:[1, 1, 1, 1, 1, 1, 1, 1]

到目前为止,我提出的最接近的解决方案还没有完全产生我正在寻找的结果......

我试图用 C++ 编写它,但到目前为止,这是我算法的一些伪代码...... 不太清楚我想要的方式

truths = randBetween(1, 8)
values = [0,0,0,0,0,0,0,0]
startPosition = randBetween(0, 7) //starting index
distance = 4

for(i = 0; i < truths; i++) {
    pos = i + startPosition + (i * distance)
    values[pos % 8] = 1
}

这是我当前代码的示例输出。标有星号的是不正确的。

[0, 0, 0, 0, 1, 0, 0, 0]
[0, 1, 0, 0, 1, 0, 0, 0]*
[0, 1, 0, 0, 1, 0, 1, 0]
[0, 1, 0, 1, 1, 0, 1, 0]*
[1, 1, 0, 1, 1, 0, 1, 0]
[1, 1, 0, 1, 1, 1, 1, 0]*
[1, 1, 1, 1, 1, 1, 1, 0]
[1, 1, 1, 1, 1, 1, 1, 1]

我正在寻找一种简单的方法来在整个数组中均匀分布真值,而不必为特殊情况编写代码。

【问题讨论】:

  • as far away from one another - 对于 2 个值,只有 [1, 0, 0, 0, 0, 0, 0, 1] 对吗?哦,你计算距离假设数组包裹自己?
  • 好像数组环绕。我应该指定...所以他们将在[1, 0, 0, 0, 1, 0, 0,0 ][0, 1, 0, 0, 0, 1, 0, 0][0, 0, 1, 0, 0, 0, 1, 0]等位置之间有3个0,以获得2个真值
  • 如果你想精确分割空格,en.wikipedia.org/wiki/Bresenham%27s_line_algorithm.

标签: c++ arrays algorithm distribution


【解决方案1】:

看看这个:

#include <cassert>
#include <vector>
#include <iostream>
#include <iomanip>

/**
 * Generate an even spaced pattern of ones
 * @param arr destination vector of ints
 * @param onescnt the requested number of ones
 */
static inline
void gen(std::vector<int>& arr, size_t onescnt) {
    const size_t len = arr.size();
    const size_t zeroscnt = len - onescnt;
    size_t ones = 1;
    size_t zeros = 1;

    for (size_t i = 0; i < len; ++i) {
        if (ones * zeroscnt < zeros * onescnt) {
            ones++;
            arr[i] = 1;
        } else {
            zeros++;
            arr[i] = 0;
        }
    }
}

static inline
size_t count(const std::vector<int>& arr, int el) {
    size_t cnt = 0;
    for (size_t i = 0; i < arr.size(); ++i) {
        cnt += arr[i] == el;
    }
    return cnt;
}

static inline
void gen_print(size_t len, size_t onescnt) {
    std::vector<int> arr(len);
    gen(arr, onescnt);
    std::cout << "gen_printf(" << std::setw(2) << len << ", " << std::setw(2) << onescnt << ") = {";
    for (size_t i = 0; i < len; ++i) {
        std::cout << arr[i] << ",";
    }
    std::cout << "}\n";
    assert(count(arr, 1) == onescnt);

}

int main() {
    for (int i = 0; i <= 8; ++i) {
        gen_print(8, i);
    }
    for (int i = 0; i <= 30; ++i) {
        gen_print(30, i);
    }
    return 0;
}

生成:

gen_printf( 8,  0) = {0,0,0,0,0,0,0,0,}
gen_printf( 8,  1) = {0,0,0,0,0,0,0,1,}
gen_printf( 8,  2) = {0,0,0,1,0,0,0,1,}
gen_printf( 8,  3) = {0,1,0,0,1,0,0,1,}
gen_printf( 8,  4) = {0,1,0,1,0,1,0,1,}
gen_printf( 8,  5) = {1,0,1,1,0,1,0,1,}
gen_printf( 8,  6) = {1,1,0,1,1,1,0,1,}
gen_printf( 8,  7) = {1,1,1,1,1,1,0,1,}
gen_printf( 8,  8) = {1,1,1,1,1,1,1,1,}
gen_printf(30,  0) = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}
gen_printf(30,  1) = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,}
gen_printf(30,  2) = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,}
gen_printf(30,  3) = {0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,}
gen_printf(30,  4) = {0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,}
gen_printf(30,  5) = {0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,}
gen_printf(30,  6) = {0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,}
gen_printf(30,  7) = {0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,1,}
gen_printf(30,  8) = {0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,}
gen_printf(30,  9) = {0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,0,0,1,}
gen_printf(30, 10) = {0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,}
gen_printf(30, 11) = {0,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,}
gen_printf(30, 12) = {0,1,0,0,1,0,1,0,0,1,0,1,0,0,1,0,1,0,0,1,0,1,0,0,1,0,1,0,0,1,}
gen_printf(30, 13) = {0,1,0,1,0,1,0,0,1,0,1,0,1,0,0,1,0,1,0,1,0,0,1,0,1,0,1,0,0,1,}
gen_printf(30, 14) = {0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,}
gen_printf(30, 15) = {0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,}
gen_printf(30, 16) = {1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,}
gen_printf(30, 17) = {1,0,1,0,1,0,1,1,0,1,0,1,0,1,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0,1,}
gen_printf(30, 18) = {1,0,1,0,1,1,0,1,0,1,1,0,1,0,1,1,0,1,0,1,1,0,1,0,1,1,0,1,0,1,}
gen_printf(30, 19) = {1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,0,1,0,1,}
gen_printf(30, 20) = {1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,}
gen_printf(30, 21) = {1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,1,1,0,1,}
gen_printf(30, 22) = {1,1,0,1,1,1,0,1,1,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,0,1,}
gen_printf(30, 23) = {1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,}
gen_printf(30, 24) = {1,1,1,0,1,1,1,1,0,1,1,1,1,0,1,1,1,1,0,1,1,1,1,0,1,1,1,1,0,1,}
gen_printf(30, 25) = {1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,}
gen_printf(30, 26) = {1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,}
gen_printf(30, 27) = {1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,}
gen_printf(30, 28) = {1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,}
gen_printf(30, 29) = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,}
gen_printf(30, 30) = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,}

@edit - 更好的均匀间隔模式。

解释:

所以让我们获取一个 8 整数数组,我们想要有 5 整数。在具有 8 个元素和 5 个 1 的序列中,理想的 (1/0) 比率应该是 (5 / 3)。我们永远不会接近这样的比例,但我们可以尝试。

这个想法是遍历数组并记住我们在数组中写入的 1 和 0 的数量。如果(写入一/写入零)的比率低于我们想要达到的目标比率(一/零),我们需要在序列中添加一个。否则,我们将零放入序列中。比率发生变化,我们下次再做决定。我们的想法是在数组的每个切片中追求理想的 1/0 比率。

【讨论】:

  • 与我的方法相同的数学基础,但实现要简单得多——非常喜欢......
  • 您的解决方案对我有用,但我似乎偏移了起始位置。将接受答案,我将不得不更多地使用它,但我想抵消分布,这样你就可以在不同的配置中拥有相同数量的真值。
  • @ShawnPacarar 如果要从任意位置开始,只需稍作修改:arr[i + startIndex % arr.size()] = ...
  • 完美,我在创建它们时犯了打印它们的错误,导致它们看起来像是按相同的顺序排列,而实际上它们已被偏移。解决方案和评论正是我想要的,非常感谢!
【解决方案2】:

一种简单的方法是对理想的小数位置进行四舍五入。

truths = randBetween(1, 8)
values = [0,0,0,0,0,0,0,0]
offset = randBetween(0, 8 * truths - 1)
for(i = 0; i < truths; i++) {
    pos = (offset + (i * 8)) / truths
    values[pos % 8] = 1
}

【讨论】:

    【解决方案3】:

    这是Bresenham's line-drawing algorithm 的应用程序。我使用它不是因为它在旧硬件上速度很快,而是它准确地放置了真实值。

    #include <iostream>
    #include <stdexcept>
    #include <string>
    #include <random>
    
    int main(int argc, char **argv) {
        try {
            // Read the argument.
            if(argc != 2) throw std::invalid_argument("one argument");
            int dy = std::stoi(argv[1]);
            if(dy < 0 || dy > 8) throw std::out_of_range("[0..8]");
            int values[8] = {0};
    
            // https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
            int dx = 8;
            int delta = 2 * dy - dx; // Balance the line. Permute it up later.
            for(int x = 0; x < dx; x++) {
                if(delta > 0) {
                    values[x] = 1;
                    delta -= 2 * dx;
                }
                delta += 2 * dy;
            }
            for(int x = 0; x < dx; x++)
                std::cout << (x ? ", " : "") << values[x];
            std::cout << std::endl;
    
            // Rotate the number by a random amount.
            // I'm sure there is an easier way to do this.
            // https://stackoverflow.com/questions/7560114/random-number-c-in-some-range
            std::random_device rd; // obtain a random number from hardware
            std::mt19937 eng(rd()); // seed the generator
            std::uniform_int_distribution<> distr(0, dx - 1);
            int rotate = distr(eng);
            bool first = true;
            int x = rotate;
            do {
                std::cout << (first ? "" : ", ") << values[x];
                first = false;
                x = (x + 1) % dx;
            } while(x != rotate);
            std::cout << std::endl;
    
        } catch(const std::exception &e) {
            std::cerr << "Something went wrong: " << e.what() << std::endl;
            return 1;
        }
        return 0;
    }
    

    一旦你有一个精确的解决方案,随机旋转它。

    0, 1, 0, 0, 1, 0, 1, 0
    1, 0, 0, 1, 0, 0, 1, 0
    

    【讨论】:

      【解决方案4】:

      您需要动态计算距离。一个元素是明确的,可以驻留在任意位置

      • 2个元素也是明确的,距离需要为4个。
      • 4 个元素需要 2 的距离
      • 8 个元素,距离为 1

      更难的是不分割数组的数字:

      • 3 需要 2.66 的距离。
      • 5 需要 1.6 的距离
      • 7 需要 0.875 的距离

      Errm... 一般来说,如果您的距离为 XY,则必须将一些元素放置在 X 的距离处,而将一些元素放置在距离为X + 1X 很简单,它将是整数除法的结果:8 / numberOfElements。其余的将决定您必须多久切换到X + 1:8 % numberOfElements。对于 3,这也将导致 2,因此您将获得 2 的 1 倍距离和 3 的 2 倍距离:

      [ 1 0 1 0 0 1 0 0 ]
          2    3     3 (distance to very first 1)
      

      对于 5,您将得到:8/5 = 1, 8%5 = 3,因此:1 的 2 倍距离,2 的 3 倍距离

      [ 1 1 1 0 1 0 1 0 ]
         1 1  2   2   2  
      

      对于 7,您将得到:8/7 = 1, 8%7 = 1,因此:1 的 7x 距离,2 的 1x 距离

      [ 1 1 1 1 1 1 1 0 ]
         1 1 1 1 1 1  2
      

      这将适用于任意数组长度L

      L/n   = minimum distance
      L%n   = number of times to apply minimum distance
      L-L%n = number of times to apply minimum distance + 1
      

      数学指标不会揭示首先应用所有较小距离然后应用所有较大距离之间的任何区别,但是,如果您尽可能频繁地在较大和较小之间交替使用算法,那么人类对美学的感觉可能会更喜欢 - 或者您递归地应用算法(对于更大的数组长度),得到类似 2x2、3x3、2x2、3x3 而不是 4x2 和 6x3 的东西。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-11-24
        • 2011-04-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-10-18
        • 1970-01-01
        • 2017-06-28
        相关资源
        最近更新 更多