Reputation: 31
I start with a list of integers:
A = [ 1, 2, 5, 3, 4, 6, 7, 8 ]
After a shuffle, I would like some elements (say 3, 4 and 5) to preserve their order of occurrence in A while the rest of the elements are free to be randomly shuffled. Something like:
Outcome #1: A = [ 5, 2, 3, 1, 8, 4, 6, 7 ]
-or-
Outcome #2: A = [ 7, 5, 6, 1, 3, 4, 8, 2 ]
-but not-
Outcome #3 (invalid outcome) A = [7, 4, 6, 1, 3, 5, 8, 2]
Appreciate all suggestions!
Upvotes: 2
Views: 97
Reputation: 57414
Extract the elements you want to maintain relative ordering among, shuffle as normal, then glue the lists back together by randomly picking indexes for the "kept" elements to be inserted. All operations are linear if you use sets for speeding up in
operations, which I didn't bother with. Ordering of the keep
list matters.
>>> import random
>>> L = [1, 2, 5, 3, 4, 6, 7, 8]
>>> keep = [2, 3, 4]
>>> kept = [L[i] for i in keep][::-1]
>>> unkept = [x for i, x in enumerate(L) if i not in keep]
>>> random.shuffle(unkept)
>>> idxes = random.sample(list(range(len(L))), k=len(keep))
>>> result = [kept.pop() if i in idxes else unkept.pop() for i in range(len(L))]
>>> result
[6, 5, 3, 8, 4, 1, 7, 2]
Random tests:
import random
def shuffle_with_fixed_order(L, keep):
kept = [L[i] for i in keep][::-1]
unkept = [x for i, x in enumerate(L) if i not in keep]
random.shuffle(unkept)
idxes = random.sample(list(range(len(L))), k=len(keep))
return [kept.pop() if i in idxes else unkept.pop() for i in range(len(L))]
if __name__ == "__main__":
for _ in range(5000):
L = list(range(50))
random.shuffle(L)
keep = sorted(random.sample(L, k=20))
shuffled = shuffle_with_fixed_order(L, keep)
new_locs = [shuffled.index(L[i]) for i in keep]
assert all(x < y for x, y in zip(new_locs, new_locs[1:]))
Upvotes: 1
Reputation: 179
Probably below will work for you
import random
lst=[1, 2, 5, 3, 4, 6, 7, 8]
s_lst = {5:0,3:0,4:0}
idx_lst = [random.randint(0, len(lst)) for i in range(len(s_lst))]
idx_lst.sort()
for i, s in enumerate(s_lst.keys()):
s_lst[s] = idx_lst[i]
lst.remove(s)
random.shuffle(lst)
for k, v in s_lst.items():
lst.insert(v, k)
print(lst)
Upvotes: 0
Reputation: 82805
This is one approach using random
module
Ex:
import random
A = [ 1, 2, 5, 3, 4, 6, 7, 8 ]
result = A[2:5]
del A[2:5]
while A:
l = len(A)
result.insert(random.randint(0, l), A.pop(random.randrange(l)))
print(result)
Demo
def rand_shfl(lst):
result = lst[2:5]
del lst[2:5]
while A:
l = len(A)
result.insert(random.randint(0, l), A.pop(random.randrange(l)))
return result
for _ in range(10):
A = [ 1, 2, 5, 3, 4, 6, 7, 8 ]
print((rand_shfl(A)))
Output:
[1, 7, 6, 5, 8, 3, 4, 2]
[8, 1, 2, 7, 5, 3, 6, 4]
[8, 6, 7, 2, 1, 5, 3, 4]
[1, 7, 2, 8, 5, 3, 4, 6]
[6, 8, 7, 5, 1, 3, 4, 2]
[7, 2, 5, 6, 1, 3, 4, 8]
[1, 8, 5, 2, 3, 4, 6, 7]
[5, 7, 1, 6, 3, 8, 4, 2]
[1, 5, 7, 3, 2, 4, 8, 6]
[8, 2, 7, 6, 5, 3, 4, 1]
Upvotes: 0