Tengo un conjunto de 4 cadenas y quiero generar una lista de 16 elementos, pero al hacer cumplir la regla (u obtener el mismo resultado que hacer cumplir esa regla) nunca se repite el mismo elemento en dos posiciones contiguas en la lista resultante.

Siendo casi un novato total en Python, fui a verificar los diferentes métodos en la biblioteca aleatoria y encontré muchas formas diferentes y útiles de hacer algo similar (random.shuffle casi haría el truco), pero ninguno de los que abordó esto es mi necesidad particular .

¿Qué formato de datos y qué métodos debo usar?

2
Fabrizio Bianchi 11 jul. 2011 a las 11:49

5 respuestas

La mejor respuesta

Algoritmo de seudocódigo:

  1. Para i en n (n es la cantidad de elementos que desea)
  2. Genera el siguiente elemento
  3. Si es igual al elemento anterior, repita 2

Use random.choice para elegir un elemento de una lista de elementos al azar.

Aquí hay una prueba de concepto de código Python:

import random
sources = ['a', 'b', 'c', 'd']      # you said 4 strings
result = [random.choice(sources)]

while len(result) < 16:             # you said you need 16 elements    
    elem = random.choice(sources)
    if elem != result[-1]:
        result.append(elem)

Este código está optimizado para la claridad, no para la concisión, la inteligencia o la velocidad.

6
Eli Bendersky 11 jul. 2011 a las 08:02

Esta es la versión revisada de Eli que no tiene elementos de fuerza bruta, y con suerte no le falta claridad:

import random

# 4 strings
sources = ['a', 'b', 'c', 'd']

# convert them to set
input = set(sources)

# choose first element
output = [random.choice(input)]

# append random choices excluding previous element till required length
while len(output) < 16:
    output.append(random.choice(input - set(output[-1:])))
1
Zart 11 jul. 2011 a las 09:10

El código de Zart se modificó para (a) trabajar y (b) calcular previamente las restas establecidas:

import random

def setsub():
    # 4 strings
    sources = ['a', 'b', 'c', 'd']

    # convert them to set
    input = set(sources)
    subs = {}
    for word in sources:
        subs[word] = list(input - set([word]))

    # choose first element
    output = [random.choice(sources)]

    # append random choices excluding previous element till required length
    while len(output) < 16:
        output.append(random.choice(subs[output[-1]]))
    return output
2
Duncan 11 jul. 2011 a las 09:53

Un abuso bastante severo de itertools:

import itertools
import random

print list(itertools.islice((x[0] for x in
  itertools.groupby(random.randint(1, 10) for y in itertools.count())), 16))

Utiliza islice() para obtener los primeros 16 elementos de un generador infinito basado en count(), utilizando groupby() para colapsar elementos adyacentes iguales.

1
Ignacio Vazquez-Abrams 11 jul. 2011 a las 07:57

Para una solución más general, puede recurrir a Generadores de Python.

Dado un iterativo arbitrario de entradas (por ejemplo, sus cuatro cadenas de entrada), el siguiente generador generará un infinito de opciones iterables de esa lista, sin que haya dos elementos uno al lado del otro:

import random
def noncontiguous(inputs):
  last = random.choice(inputs)
  yield last
  while True:
    next = random.choice(inputs)
    if next != last:
      last = next
      yield next

Luego puede usar las comprensiones de listas o un bucle básico para obtener el subconjunto de 16 elementos de esta secuencia infinita:

>>> gen = noncontiguous(['a', 'b', 'c', 'd'])
>>> [gen.next() for i in range(16)]
['c', 'b', 'c', 'b', 'a', 'c', 'b', 'c', 'd', 'a', 'd', 'c', 'a', 'd', 'b', 'c']

Más interesante aún, puede seguir usando el mismo objeto generador para crear más elementos no contiguos.

>>> for i in range(8):
...   gen.next()
...
'b'
'c'
'd'
'c'
'b'
'd'
'a'
'c'
3
Dusty Phillips 11 jul. 2011 a las 09:14