Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python — Randomly fill 2D array with set number of 1's

Suppose I have a 2D array (8x8) of 0's. I would like to fill this array with a predetermined number of 1's, but in a random manner. For example, suppose I want to place exactly 16 1's in the grid at random, resulting in something like this:

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

The resulting placement of the 1's does not matter in the slightest, as long as it is random (or as random as Python will allow).

My code technically works, but I imagine it's horrendously inefficient. All I'm doing is setting the probability of each number becoming a 1 to n/s, where n is the number of desired 1's and s is the size of the grid (i.e. number of elements), and then I check to see if the correct number of 1's was added. Here's the code (Python 2.7):

length = 8
numOnes = 16
while True:
    board = [[(random.random() < float(numOnes)/(length**2))*1 for x in xrange(length)] for x in xrange(length)]
    if sum([subarr.count(1) for subarr in board]) == 16:
        break
print board

While this works, it seems like a roundabout method. Is there a better (i.e. more efficient) way of doing this? I foresee running this code many times (hundreds of thousands if not millions), so speed is a concern.

like image 486
dpwilson Avatar asked Oct 14 '25 21:10

dpwilson


2 Answers

Either shuffle a list of 16 1s and 48 0s:

board = [1]*16 + 48*[0]
random.shuffle(board)
board = [board[i:i+8] for i in xrange(0, 64, 8)]

or fill the board with 0s and pick a random sample of 16 positions to put 1s in:

board = [[0]*8 for i in xrange(8)]
for pos in random.sample(xrange(64), 16):
    board[pos//8][pos%8] = 1
like image 180
user2357112 supports Monica Avatar answered Oct 17 '25 09:10

user2357112 supports Monica


I made the ones, made the zeros, concatenated them, shuffle them, and reshaped.

import numpy as np
def make_board(shape, ones):
    o = np.ones(ones, dtype=np.int)
    z = np.zeros(np.product(shape) - ones, dtype=np.int)
    board = np.concatenate([o, z])
    np.random.shuffle(board)
    return board.reshape(shape)

make_board((8,8), 16)

Edit.

For what it's worth, user2357112's approach with numpy is fast...

def make_board(shape, ones):
    size = np.product(shape)
    board = np.zeros(size, dtype=np.int)
    i = np.random.choice(np.arange(size), ones)
    board[i] = 1
    return board.reshape(shape)
like image 28
Matt Hall Avatar answered Oct 17 '25 09:10

Matt Hall