# Make a random drum loop

Do randomly generated drum loops sound good?

A drum loop is a $$\5\times 32\$$ matrix $$\A\$$ of $$\1\$$s and $$\0\$$s such that

1. $$\A_{1,1}=A_{1,17}=A_{2,9}=A_{2,25}=1\$$,
2. for each $$\i\$$, the $$\i\$$th row has exactly $$\f(i)\$$ different $$\1\$$s, where $$\ f(1)=3, f(2)=2, f(3)=6, f(4)=8, f(5)=5\$$,
3. for each $$\j\equiv 1\bmod 4\$$, the $$\j\$$th column has exactly one $$\1\$$, and
4. for each $$\j\not\equiv 1\bmod 4\$$, the $$\j\$$th column has at most one $$\1\$$.

Here, the rows provide instructions for each of five drums, with each column representing a beat. In particular, the first row corresponds to a kick, while second row corresponds to a snare.

Write the shortest code that draws a matrix uniformly from the set of drum loops.

Feel free to use your code to draw a random drum loop, implement the drum loop on this website, and then post a link to your creation in your post. (Note that we only use the first 5 rows of this online beat maker.)

Edit 1: To be clear, we are using 1-based indexing: the row indices are $$\\{1,\ldots,5\}\$$ and the column indices are $$\\{1,\ldots,32\}\$$.

Edit 2: Judging by the current submissions, the answer to my original question appears to be "yes": randomly generated drum loops do sound good. Here are links to the current examples:

• May we output the transposed matrix instead? – Arnauld Jan 7 at 1:11
• Just to clarify, I am guessing that you want the code to generate a different drum pattern each time it is run with a different seed value rather than to produce a single 'random' pattern that is the same each time. – tom Jan 7 at 1:11
• @Arnauld - Yes, you may output the transpose instead. – Dustin G. Mixon Jan 7 at 1:29
• @tom - Correct. Each run of the code should produce an independent realization of the random matrix. – Dustin G. Mixon Jan 7 at 1:30
• I assume $i,j$ are 1-based in your challenge description? It isn't mentioned anywhere whether the 5x32 matrix you describe uses 1- or 0-based indexing, so you might want to add that. – Kevin Cruijssen Jan 7 at 8:56

# JavaScript (ES6),  194 193  189 bytes

Returns $$\M^T\$$.

_=>(g=n=>n?g(/1/.test(c=m[R()*32|0])?n:n-=c[h()]=1):m)(16,a=[1,,6,8,5],R=Math.random,h=_=>a[y=R()*5|0]?a[y]--&&y:h(),m=[...Array(32)].map((_,x)=>(c=[0,0,0,0,0],x&3?0:c[x&7?h():x/8&1]=1,c)))


Try it online!

## How?

Note: Columns and rows in the following explanation refer to the matrix described in the challenge. They are stored the other way around in this implementation.

### Initialization

We define an array $$\a\$$ holding the number of hits that need to be added to each row, ignoring the 4 fixed positions.

a = [ 1, , 6, 8, 5 ]


We define the helper function $$\h\$$ that picks a random row among those that are not completely filled, and updates $$\a\$$ accordingly.

h = _ => a[y = R() * 5 | 0] ? a[y]-- && y : h()


We define a matrix $$\m\$$ of $$\32\times5\$$ filled with $$\0\$$'s, except the columns such that $$\x\equiv 0\pmod 4\$$ (using 0-indexing) which are initialized according to the rules described in the challenge.

m = [...Array(32)]          // set up 32 columns
.map((_, x) => (          // for each column at position x:
c = [0, 0, 0, 0, 0],    //   start with all values set to 0
x & 3 ?                 //   if x mode 4 is not equal to 0:
0                     //     do nothing
:                       //   else:
c[                    //     update c:
x & 7 ?             //       if x mod 8 is not equal to 0:
h()               //         use a random row
:                   //       else:
x / 8 & 1           //         fixed position: use either row 0 or 1,
//         depending on the parity of floor(x / 8)
] = 1,                //     set the hit
c                       //   yield the final column
))                        // end of map()


### Main loop

We randomly add the $$\16\$$ remaining hits on empty columns (which implies $$\x\not\equiv 0\pmod 4\$$) until all rows are filled.

( g = n =>                  // n = total number of hits to add
n ?                       // if n is not equal to 0:
g(                      //   do a recursive call:
/1/.test(             //     test whether
c = m[R() * 32 | 0] //     a randomly selected column c
) ?                   //     contains any '1'; if it does:
n                   //       just pass n unchanged to try again
:                     //     else:
n -= c[h()] = 1     //       set the hit on this column, on a random row
//       and decrement n
)                       //   end of recursive call
:                         // else:
m                       //   we're done: return m[]
)(16)                       // initial call to g with n = 16

• thanks for the question about M transpose, that was really useful. – tom Jan 7 at 3:05
• Thanks for the explanation - I always learn so much from your submissions :) – G0BLiN Jan 8 at 12:25

# C (gcc), 413 394 389 379 378 360 342 327 307 299 298 293 bytes

#define F;for(b[8]=b[v=24]=3,n=0;n
#define B,b[n]==
a[]=L"14444448888888822222",b[32],r[24],n,k,j,t=20,v;main(i){srand(&i)F<v;*b=b[16]=1)r[n++]=n/3-~n F<4;a[k]=a[t])b[i=4+n++*8]=a[k=rand()%t--]-48 F<16;r[j]=r[v])b[r[j=rand()%v--]]=a[n++]-48 F<32;n++)printf("%d%d%d%d%d\n"B 1 B 3 B 4 B 8 B 2);}


Try it online!

#define F;for(b[8]=b[v=24]=3,n=0;n
#define B,b[n]==
a[]=L"14444448888888822222",b[32],r[24],n,k,j,t=20,v;main(i){srand(time(0))F<v;*b=b[16]=1)r[n++]=n/3-~n F<4;a[k]=a[t])b[i=4+n++*8]=a[k=rand()%t--]-48 F<16;r[j]=r[v])b[r[j=rand()%v--]]=a[n++]-48 F<32;n++)printf("%d%d%d%d%d\n"B 1 B 3 B 4 B 8 B 2);}


try it online -earlier algorithm 307 bytes

Many thanks to @ceilingcat for more byte savings (360 to 342 - then 342 to 328 and 327 to 307 - then modified algorithm from 328 to 299 and 298 to 293).

Compiles ok without #include<stdio.h> using the -w compiler flag, thanks to @Arnaud for pointing this out.

As written it generates a fresh sequence each time. If we are allowed manual intervention of a new number in srand to generate a different drum sequence with say srand(6); then we can save 16 bytes.

Thanks to @Arnaud who made asked the question about generating the transpose matrix, which is what this program does. Thanks also for suggesting the change from int to char for a[20] that saves 15 bytes. Also thanks for @Post Rock Garf Hunter who figured out a way to remove white space near the Bs and then saved more bytes fixing up the #defines so that F is included in R. #define' tokens often seem to need whitespace so they are cleanly identified.

This code works on tio.run, but elsewhere may need to include time.h.

# How the program works

logic:

• setup
• 4 sounds are fixed (at 1,9,17,25)
• remaining sounds to be distributed randomly are in a[20]
• b[32] holds output - one cell for each beat (1 to 32, but in C of course 0 to 31)
• 4 of the b cells must have something (at 5,13,21,29)
• remaining sounds randomly scattered to empty cells
• r[24] holds list of empty cells (2,3,4, 6,7,8 etc. in C 0,1,2, 4,5,6 etc.)
• execute
• put in 4 fixed sounds
• choose random sound from a to put in each of 4 cells with sounds
• choose random cell from r to put in random sound from remaining a sounds (simplification to algorithm is possible here by putting remaining sounds one by one from 1 to 16 (or 0 to 15) in a random cell, but this gave more bytes..., but now thanks to @ceilingcat this is now smaller see tio.run link for details)
• print result

this is a long version of the code without the #define and other space savers to show the logic, but with small differences e.g. in final version a is char * not int *

#include<stdio.h>
#include<stdlib.h>
int main()
{
int a[20]={1,4,4,4,4,4,4,8,8,8,8,8,8,8,8,2,2,2,2,2},
b[32]={},r[24],n,k,j,t=20,v=24;
time_t q;srand(time(&q));
//random initialization

for(n=0;n<24;n++n=0;n<v;n++)r[n]=n+n/3+1;
// set up r with list of cells that don't definitely have sound 012,456 etc.

b[0]=b[16]=1;b[8]=b[24]=16;
//put in fixed sounds

for (n=0;n<4;n++)
{k=rand()%t--;b[4+n*8]=a[k];a[k]=a[t];}
//choose a random sound from a to put in each of 4 cells that must  have sound
// each sound selected is then replaced by the sound at the end of the list
// and the length of the list is reduced by one

for (n=0;n<16;n++)
{k=rand()%t--;j=rand()%v--;b[r[j]]=a[k];a[k]=a[t];r[j]=r[v];}
// random cell from remaining r cells and random sound are selected
// again after selection from list final item replaces selected and length
// reduced by one

for (n=0;n<32;n++)
{printf("%d%d%d%d%d\n",(b[n]==1),(b[n]==16),(b[n]==4),(b[n]==8),(b[n]==2));}
// print out 0 and 1 from logical tests to print transpose matrix
return 0;
}

• Trivial golfing suggestion: remove all the unnecessary whitespace. – pppery Jan 7 at 2:18
• Suggested optimization for the initialization of a[]. – Arnauld Jan 7 at 3:15
• @PostRockGarfHunter many thanks. I guess that after ) and " the compiler does not get confused by the lack of white space before the B. – tom Jan 7 at 3:57
• Fiddling around with your defines a bit I could I removed another 10 bytes. – Post Rock Garf Hunter Jan 7 at 4:00
• Here's a link to your creation: splice.com/sounds/beatmaker/b3054cbac7f1 – Dustin G. Mixon Jan 7 at 11:07

# 05AB1E, 65 bytes

31Ý©8ˆ24ˆ40¯ǝ0ˆ16ˆ®¯KΩˆ48¯¦¦ǝ•Wk¤]•3ôεćи}˜.ržw8и«®¯K.r.¡4Ö}é˜ǝb€¦


Outputs as a transposed list of strings.

Try it online. (The footer pretty-prints this as the intended rows/columns. Feel free to remove the footer to see the actual result.)

Explanation:

31Ý                # Push a list in the range [0,31]
©               # Store it in variable ® (without popping)
8ˆ                 # Add 8 to the global array
24ˆ              # Add 24 to the global array as well
40¯ǝ          # Replace the values at the indices of the global array with 40
0ˆ                 # Add 0 to the global array
16ˆ              # Add 16 to the global array as well
®             # Push the [0,31] list from variable ® again
¯K           # Remove all values of the global array (the [0,8,16,24])
Ω          # Pop and push a random index from this list
ˆ         # Add it to the global array as well
¯¦¦    # Push the global array with the first two (8,24) removed
48   ǝ   # Replace the values at the remaining three indices with 48
•Wk¤]•             # Push compressed integer 533636834
3ô           # Split it into parts of size 3: [533,636,834]
ε          # Map over each integer
и        #  Repeat the remainder the head amount of times
#   i.e. 533 becomes [33,33,33,33,33]
}˜         # After the map: flatten the list of list of integers
.r       # And randomly shuffle it
žw8и   # Then push a list of 8 32s
«  # And merge it to the end of the list
®                  # Push the [0,31] list from variable ® again
¯K                # Remove all indices stored in the global array
# (the [0,8,16,24] and one random index)
.r              # Randomly shuffle these remaining indices
.¡            # Group the remaining indices by:
4Ö          #  Where this (0-based) index is divisible by 4
#  (which would be i % 4 == 1 for the corresponding 1-based index)
}é            # After the group by, sort the two inner lists by length,
# so the indices divisible by 4 are before the other indices
˜           # And flatten it to a single list
ǝ                  # Then insert the shuffled integers at those shuffled indices
b                 # Convert each integer to a binary string
# (32=100000;33=100001;34=100010;36=100100;40=101000;48=110000)
€¦               # And then remove the leading "1" from each
# (after which the resulting list is output implicitly)


See this 05AB1E tip of mine (section How to compress large integers?) to understand why •Wk¤]• is 533636834.

# Jelly, 43 bytes

32s8Zs4ZḢ;FẊḣ⁴ƲƊḢŒœ;FẊ$ƲFṙ-“¥©€Þı‘œṖ;¹Ṭz0ZṖ  Try it online! A niladic link that returns a 32x5 matrix of 1s and 0s representing the drum loop. The footer simply prints it concatenated and separated by newlines. ## Explanation 32 | 32 s8 | Split into groups of 8 Z | Transpose s4 | Split into groups of 4 Z | Transpose Ɗ | Following as a monad: Ḣ | - Head ; Ʋ | - Concatenate to following as a monad: F | - Flatten Ẋ | - Shuffle list ḣ⁴ | - First 16 Ʋ | Following as a monad: Ḣ | - Head Œœ | - Odd/even indexed items ;$                     | - Concatenate to:
F                       |   - Remainder of list flattened
Ẋ                      |   - Shuffled
F                   | Flatten
ṙ-                 | Rotate right 1 item
“¥©€Þı‘œṖ        | Split list before positions 4, 6, 12, 20, 25
;¹      | Concatenate to 32 (because 32 was the beginning of this niladic link)
Ṭ     | Convert from lists of numeric indices to logical lists with 1s at those positions
z0   | Transpose with 0 as filler
Z  | Transpose
Ṗ | Remove last (the sixth list introduced with the 32 above)


# Python 3, 180 bytes

from random import*
r=range
s=sample
a=s(s([x for x in r(32)if x%4],16)+[x*8+4 for x in r(4)],20)
print([[i in t for i in r(32)]for t in[[0,16,a[0]],[8,24],a[1:7],a[7:15],a[15:]]])


Try it online!

Prints a logical matrix representing the drum loop.

# R, 138 bytes

s=sample
y=s(c(8*0:3+5,s((1:32)[-(4*0:7+1)],16)))
z=matrix(0,5,32)
z[1,c(1,17,y[1])]=z[2,c(9,25)]=z[3,y[2:7]]=z[4,y[8:15]]=z[5,y[16:20]]=1


Try it online!

A full program that implicitly prints the drum loop as a matrix of 1s and 0s.

# Alternative using a third party package:

## R + DescTools, 130 bytes

s=sample
y=s(c(8*0:3+5,s((1:32)[-(4*0:7+1)],16)))
sapply(list(c(1,17,y[1]),c(9,25),y[2:7],y[8:15],y[16:20]),DescTools::Unwhich,32)


RDRR it!

A full program that implicitly prints a logical matrix (in the transposed form).

# Python 3, 400 $$\\cdots\$$ 361 338 bytes

from random import*
r=randrange
p=65537
def h(b=p):
while b==p:b=p|1<<r(32)
l=[b,16777472]
for i in[6,8,5]:
while bin(b).count('1')!=i:b=sum(1<<r(32)for j in[1]*i)
l+=[b]
return l
g=1
while g:
l,g=h(),0
for i in range(32):
b=sum(j&1<<i!=0for j in l)
if i%4:g|=b>1
else:g|=b!=1
for i in l:print(f"{bin(i)[2:]:>032}"[::-1])
`

Try it online!

Full program outputs a $$\5\times32\$$ matrix of $$\1\$$s and $$\0\$$s. Bit slow at times ($$\\sim15\$$ seconds), but you can't rush the creative process! :P