Controlling repetition rate in randomness


#1

Hello Synths,

I am trying to generate random numbers with specific repetition constraints and many questions arose. For instance, generate random numbers that do not repeat for each 6 numbers generated.

It is easy to do this when the repetition “step” is one, i.e no repetition for sequencial elements, using Dxrand or Pxrand, but I could not figure out a built-in way to do change the repetition step.

A tangential question: one the language side, is there an instance method like .rand, .rrand, for avoiding non-consecutive repetition? Something like a .xrand?

What would be the possible ways for controlling the repetition “step” on Patterns and on instance methods? Is there any Quark that already does that?
Should I use seeding or conditionals ?
Is the solution strictly linked to list comprehension ?

[ ]'s


#2

Hi,

I also stumbled across this and wrote Idev suite, contained in miSCellaneous_lib. It’s basic idea is to search grid environments of moving sources with avoidance of repetitions as an additional feature. As a special case the source can also be static, then it looks like your use case, see the example below.

/////////////// text from help

DIdev / PIdef / PLIdev search for numbers with integer distance from a source signal / pattern up to a given deviation. Repetitions within a lookback span are avoided, DIdev / PIdef / PLIdev randomly choose from possible solutions. Intended for search within integer grids (pitches, indices etc.), however applications with non-integer sources are possible, see examples.
The main musical idea of these classes is, that it’s often unwanted to have certain characteristics repeated within a perceptional time window. This might apply to melodic lines as well as to rhythmic patterns or sequences of timbre and can be continued in the domain of microsound. The principle can easily be understood with pitches, therefore most examples are of this kind.
In the following example integers are searched within the neighbourhood of a rounded sine source. The hi and lo deviation is constant (+/-5) and a lookBack value of 3 garantuees that there are no repetitions of integer values within a group of 4 (e.g. find a closest repetition within the last 5 points). In general lookback and deviations can be dynamic, the source doesn’t need to be rounded and the comparison threshold for the repetition check can also be passed as a dynamic argument.

https://github.com/dkmayer/miSCellaneous_lib/blob/master/HelpSource/Tutorials/attachments/Idev_suite/Idev_scheme_3.png

/////////////// args

PIdev.new(pattern, maxLookBack: 3, loDev: -3, hiDev: 3, lookBack, thr: 0.001, length: inf)

pattern
The source value pattern to start search from.

maxLookBack
Integer, the maximum lookback span. Fixed, defaults to 3.

loDev
Determines the current low deviation for the search. Defaults to -3.

hiDev
Determines the current high deviation for the search. Defaults to 3.

lookBack
Determines the current lookback span for avoiding repetitions. Can be modulated but must not exceed maxLookBack. If no value is passed, then maxLookBack is taken.

thr
Threshold for equality comparison. Can be modulated, defaults to 1e-3.

length
Number of repeats. Defaults to inf.

///////////////

// gives random integers between 0 and 20, no reps within 6 items

PIdev(10, 20, -10, 10, 5).iter.nextN(30)

-> [ 18, 6, 7, 4, 8, 15, 13, 3, 11, 18, 6, 2, 0, 15, 3, 13, 18, 19, 11, 0, 20, 4, 15, 7, 17, 6, 19, 0, 9, 20 ]

PLIdev is the PLx (proxy) variant of PIdev, DIdev the demand rate ugen version.

Daniel


#3

It should be noted that Idev suite was designed with rather small and changing environments (ranges) in mind. Therefore the search process isn’t optimized for large search ranges (from some hundred integers onwards or so). This might or might not be an issue in your use case.

However miSCellaneous_lib contains alternative possibilities for these cases: PSrecur, a stream pattern which stores its history in a buffer can be used here, the recursive Function must perform a search for a random element with a check for non-repetition. For this search there are several options: it can be defined explicitely, with the help of ‘enum’ - miSCellaneous_lib’s search tool - or, as you guessed, with list comprehensions. Admittedly ‘enum’ isn’t super easy to understand, it has a lot of options, but this is similar with List Comprehensions, in the following it’s not so difficult as we only need both procedures to give one search result with an easy constraint (new item from number pool must not be contained in buffer/array of last elements of stream).

Here the two latter variants for the example that I posted with PIdev before:

// PSrecur + enum

(
a = (0..20); 
p = PSrecur(
	// note the special search options: order = false causes random order
	// maxNum 1 gives one solution, result is a nested array, hence [0][0]
	{ |buf| 1.enum(a, { |x| buf.includes(x).not }, true, 0, false, 1)[0][0] },
	bufSize: 5,
	start: a.choose
);

p.iter.nextN(30);
)


// PSrecur + List Comprehension

(
a = (0..20);
p = PSrecur(
	// scramble to avoid trivial ordered solutions
	{ |buf| next {: x, x <- a.scramble, buf.includes(x).not } },
	bufSize: 5,
	start: a.choose
);

p.iter.nextN(30);
)

#4

BTW there’s a mistake in PSrecur’s help file:

recurFunc
Recursive Function, the PSrecur will be passed as first arg, count as second arg.

But in fact the recurFunc is passed the array of last values as first arg as it is rather clear from the example.