How to generate binary array whose elements with values 1 are randomly drawn

I am trying to generate a binary matrix (whose elements are 0 or 1) of dimension 20x20. To do this, I want to supply as input the number of matrix elements that will take value 1. After, I want to draw randomly distinct position of those elements (values 1). I thought of doing this:

n = 20; (*matrix dimension*)
d = 300; (*number of matrix elements that will assume value 1*)
rules = RandomInteger[{1, n}, {d, 2}]; (*defines the position of the matrix elements*)
rules2 = Table[rules[[i]] -> 1, {i, Length[rules]}]; (*applies the list of random positions the value 1*)
s = SparseArray[rules2] (*creates the binary random matrix*)


However, this method is not efficient because it does not create 300 different random positions (some are repeated). For example, the result appears 212 filled matrix elements (many of the positions contains summed values).

SparseArray[<212>,{20,20}]

I would like to know if anyone could help me solve this problem of generating 300 numbers 1 in random positions in a 20x20 dimension matrix.

Thanks in advance

5 Answers

This might work:

n = 20;
d = 300;
s = Join[ConstantArray[1, d], ConstantArray[0, n^2 - d]];
a = Partition[RandomSample[s], n]

Total[a, 2]


300

r = RandomSample[Range[400], 300];
q = Array[0 &, 400];
q[[r]] = 1;
Q = ArrayReshape[q, {20, 20}] // MatrixForm


• Quite nice. Here's a shorter variation: ArrayReshape[SparseArray[Transpose[{RandomSample[Range[400], 300]}] -> 1, {400}], {20, 20}] – J. M.'s torpor Mar 17 '19 at 14:34
• @J. M. Thank you! And I like your version too! Have not been as familiar with SparseArray[] as some of the other commands, because I never really use it. But now I see its benefit here. Ironically, most (75%) of the entries are ones! – mjw Mar 17 '19 at 15:11
• Indeed, it's not actually "sparse" in that sense. So, just turn things around a bit: ArrayReshape[SparseArray[Transpose[{RandomSample[Range[400], 100]}] -> 0, {400}, 1], {20, 20}] – J. M.'s torpor Mar 17 '19 at 15:27
• Yes! Very much agreed. Even more efficient! – mjw Mar 17 '19 at 15:30

In a situation where generating all admissible matrix indices can get prohibitive (e.g. the result of Tuples[] having too many elements), here is an approach that generates just the needed nonzero indices:

(* random k-subset *)
rs[n_, k_] := Take[PermutationList[RandomPermutation[n]], k]

BlockRandom[SeedRandom[1023, Method -> "ExtendedCA"]; (* for reproducibility *)
With[{n = 20, p = 300},
Block[{k = 1, idl, id},
idl = {rs[n, 2]};
While[k < p, id = rs[n, 2];
If[! MemberQ[idl, id], k++; AppendTo[idl, id]]];
mat = SparseArray[idl -> 1, {n, n}]]]];


Check:

Total[mat, 2]
300


Here is a variation of JM's comment to mjw's answer that will be much faster when large matrices (e.g., 10^5 by 10^5) are to be created:

randomBinary[dim_, count_] := ArrayReshape[
SparseArray[Thread[RandomSample[1;;dim^2, count]->1], dim^2],
{dim, dim}
]


The key idea is that one can use Span (e.g., 1;;max) as the first argument of RandomSample. For example:

mat = randomBinary[10^5, 300]; //RepeatedTiming
Total[mat, Infinity]


{0.000327, Null}

300

Of the other answers, only JM's answer will be able to produce a result, and does so about 4 orders of magnitude more slowly.

sa = SparseArray[RandomSample[Tuples[Range@20, {2}], 300] -> 1, {20, 20}]


Total[sa, 2]


300

Alternatively, without Tuples:

a2 = Unitize @ Threshold[RandomReal[1, {20, 20}], {"LargestValues", 300}]

a3 = Partition[SparseArray[Partition[RandomSample[Range[20^2], 300], 1] -> 1, {20^2}], 20]

a4  = SparseArray[(1 + QuotientRemainder[RandomSample[Range[20^2 - 1], 300], 20]) -> 1,
{20, 20}]

Total[#, 2] & /@ {a2, a3, a4}


{300, 300, 300}