Python – baraja solo algunos elementos de una lista

Estoy tratando de mezclar solo los elementos de una lista desde la 3ª hasta la última posición para que la 1ª siempre permanezca en su lugar, por ejemplo.

list = ['a?','b','c','d','e'] 

dentro

 list = ['a?','b','d','e','c'] 

y por alguna razón esto no funciona:

 list = ['a?','b','c','d','e'] import random random.shuffle(list[2:]) print list 

Alguien sabe que estoy haciendo mal ??

Lo único que me funciona es hasta ahora esto (EDITADO):

 lists = [['a?','b','c','d','e'],['1?','2','3','4','5','6','7']] import random for list in lists: copy = list[2:] random.shuffle(copy) list[2:] = copy print lists 

Creo que esto es exactamente lo que necesitaba.

Lo que haces es esto:

 copy = list[2:] random.shuffle(copy) 

Lo que no hace mucho a la lista original. Prueba esto:

 copy = list[2:] random.shuffle(copy) list[2:] = copy # overwrite the original 

Si desea barajar sin copiar, puede intentar escribir su propia clase de sector mutable, como la siguiente (es un bosquejo de implementación aproximado, sin controles de límites, etc.):

 class MutableSlice(object): def __init__(self, baselist, begin, end=None): self._base = baselist self._begin = begin self._end = len(baselist) if end is None else end def __len__(self): return self._end - self._begin def __getitem__(self, i): return self._base[self._begin + i] def __setitem__(self, i, val): self._base[i + self._begin] = val 

Luego envuelva la lista original en él y alimente a la mezcla estándar:

 >>> mylist = [1,2,3,4,5,6] >>> slice = MutableSlice(mylist, 2) >>> import random >>> random.shuffle(slice) >>> mylist [1, 2, 4, 3, 5, 6] 

Puede crear su propia función de reproducción aleatoria que le permitirá mezclar una porción dentro de una secuencia mutable. Maneja el muestreo de la copia de la porción y la reasignación de nuevo a la porción. Debe pasar los argumentos slice() en lugar de la notación más familiar [2:] .

 from random import sample def myShuffle(x, *s): x[slice(*s)] = sample(x[slice(*s)], len(x[slice(*s)])) 

uso:

 >>> lst = ['a?','b','c','d','e'] #don't use list as a name >>> myShuffle(lst, 2) #shuffles lst[:2] >>> lst ['b', 'a?', 'c', 'd', 'e'] >>> myShuffle(lst, 2, None) #shuffles lst[2:] >>> lst ['b', 'a?', 'd', 'e', 'c'] 

l[2:] construye una nueva lista, y random.shuffle intenta cambiar la lista “en el lugar”, lo que no tiene ningún efecto en l .

Podrías usar random.sample para esto:

 l[2:] = random.sample(l[2:], len(l)-2) 

Para mezclar una parte de la lista en su lugar, sin copias, podemos usar una mezcla de Knuth :

 import random def shuffle_slice(a, start, stop): i = start while (i < stop-1): idx = random.randrange(i, stop) a[i], a[idx] = a[idx], a[i] i += 1 

Hace lo mismo que random.shuffle, excepto en una porción:

 >>> a = [0, 1, 2, 3, 4, 5] >>> shuffle_slice(a, 0, 3) >>> a [2, 0, 1, 3, 4, 5] 

Usando el hecho de que una lista tiene una rápida eliminación e inserción y extinging una solución anterior ( https://stackoverflow.com/a/25229111/3449962 ):

Elemento de lista

  • Enumerar elementos fijos y copiarlos y su índice.
  • eliminar elementos fijos de la lista
  • barajar el subconjunto restante
  • poner los elementos fijos de nuevo en

Esto utilizará operaciones en el lugar con sobrecarga de memoria que depende del número de elementos fijos en la lista. Lineal en el tiempo. Una posible implementación más general de shuffle_subset:

 #!/usr/bin/env python """Shuffle elements in a list, except for a sub-set of the elments. The sub-set are those elements that should retain their position in the list. Some example usage: >>> from collections import namedtuple >>> class CAnswer(namedtuple("CAnswer","x fixed")): ... def __bool__(self): ... return self.fixed is True ... __nonzero__ = __bool__ # For Python 2. Called by bool in Py2. ... def __repr__(self): ... return "".format(self.x) ... >>> val = [3, 2, 0, 1, 5, 9, 4] >>> fix = [2, 5] >>> lst = [CAnswer(v, i in fix) for i, v in enumerate(val)] >>> print("Start ", 0, ": ", lst) Start 0 : [, , , , , , ] Using a predicate to filter. >>> for i in range(4): # doctest: +NORMALIZE_WHITESPACE ... shuffle_subset(lst, lambda x : x.fixed) ... print([lst[i] for i in fix], end=" ") ... [, ] [, ] [, ] [, ] >>> for i in range(4): # doctest: +NORMALIZE_WHITESPACE ... shuffle_subset(lst) # predicate = bool() ... print([lst[i] for i in fix], end=" ") ... [, ] [, ] [, ] [, ] Exclude certain postions from the shuffle. For example, exclude the first two elements: >>> fix = [0, 1] >>> lst = [CAnswer(v, i in fix) for i, v in enumerate(val)] >>> print("Start ", 0, ": ", lst) Start 0 : [, , , , , , ] >>> for i in range(4): # doctest: +NORMALIZE_WHITESPACE ... shuffle_subset(lst, fix) ... print([lst[i] for i in fix], end=" ") ... [, ] [, ] [, ] [, ] Using a selector with the same number of elements as lst: >>> fix = [0, 1] >>> lst = [CAnswer(v, i in fix) for i, v in enumerate(val)] >>> sel = [(i in fix) for i, _ in enumerate(val)] >>> print("Start ", 0, ": ", lst) Start 0 : [, , , , , , ] >>> for i in range(4): # doctest: +NORMALIZE_WHITESPACE ... shuffle_subset(lst, sel) ... print([lst[i] for i in fix], end=" ") ... [, ] [, ] [, ] [, ] A generator as selector works fine too: >>> fix = [0, 1] >>> lst = [CAnswer(v, i in fix) for i, v in enumerate(val)] >>> print("Start ", 0, ": ", lst) Start 0 : [, , , , , , ] >>> for i in range(4): # doctest: +NORMALIZE_WHITESPACE ... sel = ((i in fix) for i, _ in enumerate(val)) ... shuffle_subset(lst, sel) ... print([lst[i] for i in fix], end=" ") ... [, ] [, ] [, ] [, ] """ from __future__ import print_function import random def shuffle_subset(lst, predicate=None): """All elements in lst, except a sub-set, are shuffled. The predicate defines the sub-set of elements in lst that should not be shuffled: + The predicate is a callable that returns True for fixed elements, predicate(element) --> True or False. + If the predicate is None extract those elements where bool(element) == True. + The predicate is an iterable that is True for fixed elements or len(predicate) == len(lst). + The predicate is a list of indices of fixed elements in lst with len(predicate) < len(lst). """ def extract_fixed_elements(pred, lst): try: if callable(pred) or pred is None: pred = bool if pred is None else pred fixed_subset = [(i, e) for i, e in enumerate(lst) if pred(e)] elif (hasattr(pred, '__next__') or len(pred) == len(lst)): fixed_subset = [(i, lst[i]) for i, p in enumerate(pred) if p] elif len(pred) < len(lst): fixed_subset = [(i, lst[i]) for i in pred] else: raise TypeError("Predicate {} not supported.".format(pred)) except TypeError as err: raise TypeError("Predicate {} not supported. {}".format(pred, err)) return fixed_subset # fixed_subset = extract_fixed_elements(predicate, lst) fixed_subset.reverse() # Delete fixed elements from high index to low. for i, _ in fixed_subset: del lst[i] random.shuffle(lst) fixed_subset.reverse() # Insert fixed elements from low index to high. for i, e in fixed_subset: lst.insert(i, e) if __name__ == "__main__": import doctest doctest.testmod() 

Copié la función de reproducción aleatoria de random.shuffle y la adapté, para que mezclara una lista solo en un rango definido:

 import random a = range(0,20) b = range(0,20) def shuffle_slice(x, startIdx, endIdx): for i in reversed(xrange(startIdx+1, endIdx)): # pick an element in x[:i+1] with which to exchange x[i] j = random.randint(startIdx, i) x[i], x[j] = x[j], x[i] #Shuffle from 5 until the end of a shuffle_slice(a, 5, len(a)) print a #Shuffle b from 5 ... 15 shuffle_slice(b, 5, 15) print b 

El código anterior solo baraja los elementos dentro del rango especificado. El orden aleatorio se realiza in situ, es decir, no se crea una copia de la lista.

Intente esto … es mucho más simple y no hace ninguna copia de la lista.
Puede mantener cualquiera de los elementos fijos simplemente jugando con los índices de la lista.

trabajando:

  1. cree una nueva lista de solo los elementos que desea barajar.

  2. barajar la nueva lista.

  3. elimina aquellos elementos que quisieras barajar de tu lista original.

  4. inserte la lista recién creada en la lista antigua en el índice apropiado

     importar al azar
     list = ['a?', 'b', 'c', 'd', 'e']

     v = []
     p = [v.append (list [c]) para c en el rango (2, len (list))] # paso 1
     random.shuffle (v) #step 2
     para c en rango (2, len (lista)):
         list.remove (list [c]) #step 3
         list.insert (c, v [c-2]) #step 4 # c-2 ya que la parte a barajar comienza desde este índice de lista

     imprimir (lista)