2012-07-29 3 views
5

Ist es möglich, nur einen (kontinuierlichen) Teil einer gegebenen Liste (oder eines Arrays in numpy) zu mischen?Erstes Element fixieren, den Rest einer Liste/eines Arrays mischen

Wenn dies nicht generell möglich ist, wie wäre es dann mit dem Spezialfall, bei dem das erste Element fixiert ist und der Rest der Liste/des Arrays neu gemischt werden muss? Zum Beispiel habe ich eine Liste/Array:

to_be_shuffled = [None, 'a', 'b', 'c', 'd', ...] 

wo das erste Element sollte immer bleiben, während der Rest wiederholt werden neu gemischt werden.

Eine Möglichkeit besteht darin, die gesamte Liste zuerst zu mischen und dann das erste Element zu prüfen, wenn es nicht das spezielle feste Element ist (zB None), dann seine Position mit der des speziellen Elements tauschen (was dann erforderlich wäre) ein Nachschlagen).

Gibt es einen besseren Weg, dies zu tun?

Antwort

5

numpy Arrays kopieren keine Daten über Slicing:

numpy.random.shuffle(a[1:]) 
+0

Wie ich mit einem Numpy-Array zu implementieren, ist dies die beste Lösung in meinem Fall.Andere Lösungen sind ebenfalls sehr hilfreich und hoffen, dass andere sie in ihren jeweiligen Anwendungsfällen für geeignet halten. – skyork

9

Warum nicht einfach

import random 
rest = to_be_shuffled[1:] 
random.shuffle(rest) 
shuffled_lst = [to_be_shuffled[0]] + rest 
+0

@DavidRobinson, danke. Ist das Obige in-place, was bedeutet, dass keine neue Kopie eines Teils der Liste/des Arrays erstellt wurde? – skyork

+2

@kojiro 'shuffled_lst' existiert vorher nicht. Du meinst wahrscheinlich: 'to_be_shuffled [1:] = rest ' – jamylak

+2

@skyork nein, sowohl' shuffled_lst' als auch 'rest' sind Kopien (teilweise und vollständig) der Daten in' to_be_shuffled'. – kojiro

3

ich die Shuffle-Funktion aus der Standardbibliothek random Modul hat (gefunden in Lib\random.py) und modifiziert es leicht, so dass es durch start und stop angegeben, nur einen Teil der Liste schlurft. Es macht das an Ort und Stelle. Genießen!

from random import randint 

def shuffle(x, start=0, stop=None): 
    if stop is None: 
     stop = len(x) 

    for i in reversed(range(start + 1, stop)): 
     # pick an element in x[start: i+1] with which to exchange x[i] 
     j = randint(start, i) 
     x[i], x[j] = x[j], x[i] 

für Ihre Zwecke, diese Funktion mit 1 als start Parameter aufrufen sollte es tun.

+0

@ J.F.Sebastian: Das habe ich Sir. Vielen Dank! –

+1

Verwenden Sie XRange anstelle von Bereich auf Python 2.x – jfs

4

Ich dachte, es wäre interessant und lehrreich, zu versuchen, einen etwas allgemeineren Ansatz zu implementieren als das, was Sie verlangen. Hier mische ich die Indizes zu der ursprünglichen Liste (und nicht die Liste selbst), ausgenommen die gesperrten Indizes, und benutze diese Indexliste, um Elemente aus der ursprünglichen Liste auszuwählen. Dies ist keine In-Place-Lösung, sondern wird als Generator implementiert, sodass Sie Elemente bequem auswählen können.

Fühlen Sie sich frei zu bearbeiten, wenn Sie es verbessern können.

import random 

def partial_shuf(input_list, fixed_indices): 
    """Given an input_list, yield elements from that list in random order 
    except where elements indices are in fixed_indices.""" 
    fixed_indices = sorted(set(i for i in fixed_indices if i < len(input_list))) 
    i = 0 
    for fixed in fixed_indices: 
     aslice = range(i, fixed) 
     i = 1 + fixed 
     random.shuffle(aslice) 
     for j in aslice: 
      yield input_list[j] 
     yield input_list[fixed] 
    aslice = range(i, len(input_list)) 
    random.shuffle(aslice) 
    for j in aslice: 
     yield input_list[j] 

print '\n'.join(' '.join((str(i), str(n))) for i, n in enumerate(partial_shuf(range(4, 36), [0, 4, 9, 17, 25, 40]))) 

assert sorted(partial_shuf(range(4, 36), [0, 4, 9, 17, 25, 40])) == range(4, 36)