Quello che potresti fare (in prossimità del tempo O (n), se non mi sbaglio) è, parametrizzandolo con "limite", preparare / pre-calcolare una grammatica senza contesto epsilon come (per limit = 3),
Upto3 -> LimitOnes | LimitZeros
LimitOnes -> Zeros Ones LimitOnes | Zeros Ones | Zeros
LimitZeros -> Ones Zeros LimitZeros | Ones Zeros | Ones
Zeros -> Zero | Zero Zero | Zero Zero Zero
Ones -> One | One One | One One One
Zero -> 0
One -> 1
(nota: non pretendo che sia la grammatica corretta o più semplice che vorresti usare, ma puoi sempre giocherellare / sperimentare con la tua usando l'elegante Grammofono strumento, di Michael Daines)
dove "Upto3" è il simbolo assioma / inizio della grammatica.
(Le uniche due regole che dipendono da N che sono Zeros - > ... e Ones - > ...)
Quindi, usando una grammatica pre-calcolata (che userete per scopi di generazione anziché l'analisi), potete scrivere una semplice funzione ricorsiva, che, partendo dal simbolo di inizio, seleziona casualmente una delle alternative del regole e la applica, tramite concatenazione di stringhe, su un accumulatore che passa a se stesso (e che avrai passato come stringa vuota per la chiamata di livello superiore).
Ad ogni chiamata ricorsiva, è banale decidere (a seconda di N e dell'ultima lunghezza conosciuta dell'accumulatore) quale ramo alternativo delle regole (cioè, produzioni) - LimitOnes, LimitZeros, Ones o Zeros - è significativo seguire (non superare N).
Per esempio,
N = 10, limit = 3:
inizia con Upto3: accumulator=""
da "Upto3", scegli in modo casuale "LimitZeros"
da "LimitZeros", applica "Ones", "Zeros" in "Ones Zeros LimitZeros": accumulator="100"
da "LimitZeros", applica "Ones", "Zeros" in "Ones Zeros LimitZeros": accumulator="100110"
ecc., ecc.
Ti lascio capire i dettagli di implementazione.
'Spero che questo aiuti,