c++matrixrandom

How to put ones in random places in matrix (or 2D array) of zeroes in C++


I wanted to create a 9x9 matrix with 10 ones in it, which is filled randomly in C/C++.

For example, it should look like this:

array[[ 0, 1, 0, 0, 0, 1, 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, 0, ]
      [ 0, 1, 0, 0, 0, 1, 0, 0, 0, ]
      [ 0, 0, 0, 0, 0, 0, 0, 0, 0, ]
      [ 0, 0, 0, 0, 0, 0, 0, 0, 1, ]
      [ 0, 0, 1, 0, 0, 0, 0, 0, 0, ]
      [ 1, 0, 0, 0, 0, 1, 0, 0, 0, ]]

Solution

  • There are different ways. You could fill the 10 first positions in an array with 1s and then call std::shuffle to put those in random positons. You could also create a list of available positions and randomly pick 10 out of that list.

    Commented example of the former:

    #include <algorithm>  // shuffle
    #include <array>      // array
    #include <iostream>   //
    #include <random>     // mt19937
    
    int main() {
        constexpr unsigned side_size = 9;
        // a seeded pseudo number generator
        std::mt19937 prng(std::random_device{}());
    
        std::array<std::array<int, side_size>, side_size> arr_to_fill;
        unsigned ones = 10;  // the number of 1's we want
    
        // create an array with all available positions number from 0-
        std::array<unsigned, side_size * side_size> positions{};
    
        // put 1's in the first `ones` positions:
        std::fill_n(positions.begin(), ones, 1);
    
        // shuffle the array so the 1's are put in random positions
        std::shuffle(positions.begin(), positions.end(), prng);   
    
        // fill the array:
        for (size_t idx = 0; idx < positions.size(); ++idx) {
            arr_to_fill[idx / side_size][idx % side_size] = positions[idx];
        }
    
        // print result:
        for (auto& inner : arr_to_fill) {
            for (auto v : inner) std::cout << ' ' << v;
            std::cout << '\n';
        }
    }
    

    Demo

    (Note that it's tempting to just cast the 2d array into a plain 1d array and fill that with 1's and shuffle it directly but then you are in undefined behavior territory)


    Commented example of the latter:

    #include <array>    // array
    #include <iostream> // 
    #include <numeric>  // iota
    #include <random>   // mt19937, uniform_int_distribution
    
    int main() {
        constexpr unsigned side_size = 9;
        // a seeded pseudo number generator
        std::mt19937 prng(std::random_device{}());
    
        // all zeroes:
        std::array<std::array<int, side_size>, side_size> arr_to_fill{};
        unsigned ones = 10;  // the number of 1's we want
    
        // create an array with all available positions number from 0-
        std::array<unsigned, side_size * side_size> positions;
        std::iota(positions.begin(), positions.end(), 0);  // [0, side_size^2)
    
        for (unsigned i = 0; i < ones; ++i) {
            unsigned last = positions.size() - 1 - i;  // last unpicked pos
            // distribution of random numbers from 0 to last (inclusive)
            std::uniform_int_distribution dist(0u, last);
    
            unsigned idx = dist(prng);         // get random index ...
            unsigned pos = positions[idx];     // ... to pick a position
            positions[idx] = positions[last];  // copy last unpicked position
    
            // put a 1 in the chosen positions place:
            arr_to_fill[pos / side_size][pos % side_size] = 1;
        }
        // print result:
        for (auto& inner : arr_to_fill) {
            for (auto v : inner) std::cout << ' ' << v;
            std::cout << '\n';
        }
    }
    

    Demo


    If you can use external libraries, you could also simplify this by using boost::multi::array.

    Example provided by alfC:

    #include <multi/array.hpp> // boost::multi::array
    
    #include <algorithm>  // shuffle
    #include <iostream>   //
    #include <random>     // mt19937
    
    int main() {
        std::mt19937 prng(std::random_device{}());
    
        // create a 9x9 array, initialized with 0's:
        boost::multi::array<int, 2> A({9, 9}, 0);
    
        // fill the first 10 elements:
        std::fill_n(A.elements().begin(), 10, 1);
    
        // shuffle the array so the 1's end up in random positions:
        std::shuffle(A.elements().begin(), A.elements().end(), prng);
    
        // print result:
        for (auto const& row : A) {
            for (auto const& v : row) {
                std::cout << ' ' << v;
            }
            std::cout << '\n';
        }
    }
    

    Demo