如何使用 Python 洗牌数字数组.数字只允许从其原点位置移动一步



我如何让这段代码通过将数字从其原始位置仅移动一步来洗牌数组。代码洗牌很好,但它将数字从其原始位置洗牌了不止一步?

import random
def _shuffle_list_values():
ids_list = [21,22,23,24,25,26,27,28]
random_movements = {}
for n_id in ids_list:
movement = random.randrange(0,2)
if movement == 0:
movement-=1
random_movements[n_id] = movement
print(movement)
left = -1
right = 1
shuffled_list = [x for x in ids_list]
for n_id in ids_list:
if not(ids_list.index(n_id) == 0 and random_movements[n_id] == left) or
not(ids_list.index(n_id) == len(ids_list) - 1 and random_movements[n_id] ==
right):
shuffled_list.insert(ids_list.index(n_id) + random_movements[n_id],
shuffled_list.pop(ids_list.index(n_id)))
return shuffled_list

你可以像这样递归地做到这一点:

import random
def shuffle_list(l, idx=0):
# Base case:
# when reached final element of the list,
# there is nothing to flip, so return. 
if idx >= len(l) - 1:
return
# flip will be either 0 or 1
flip = random.randrange(0,2)
if flip:
# if flip is 1, then flip elements at position idx and idx + 1
# then recursively call for position idx + 2 , 
# since prev two elements are already flipped.
l[idx], l[idx + 1] = l[idx + 1], l[idx]
shuffle_list(l, idx + 2)
else:
# if flip is 0, then element at idx position remains untouched.
# so, recursively call for idx + 1 position. 
shuffle_list(l, idx + 1)
ll = [21,22,23,24,25,26,27,28]
shuffle_list(ll)
print(ll)
# [22, 21, 24, 23, 26, 25, 27, 28]

或者你可以像这样迭代地做:

import random
def shuffle_list(l):
idx = 0
while idx < len(l) - 1:
# For an equal distribution on the shuffle,
# make flip choose from -1 to 1 when idx = 0
# where -1: move left, 0: stay as it is, 1: move right
# so, it has 1/3 possibility of moving right,
# and in turn, second element has 1/3 possibility of moving left.
# and it follows for subsequent elements.
# Note that for idx = 0, there is no space on left to move,
# so, it only moves when flip == 1
if idx == 0:
flip = random.randrange(-1, 2)
else:
flip = random.randrange(0, 2)
if flip == 1:
l[idx], l[idx + 1] = l[idx + 1], l[idx]
idx += 2
else:
idx += 1
ll = [21,22,23,24,25,26,27,28]
shuffle_list(ll)
print(ll)
# [22, 21, 24, 23, 26, 25, 27, 28]

由于随机翻转,结果可能会每次更改,但会遵循规则。

逻辑:

1. For every element i, decide if we want to flip it or not.
a. If yes, flip element i with i+1, and repeat step 1 for element i+2.
b. If no, skip element i, and repeat step 1 for element i+1
2. Do this until you reach at the end of the list.

更新:

根据 Martijn 的评论,原始方法在洗牌上没有均匀分布,因此更新了翻转决策(在迭代版本中(来解决它。

测试:

nums = 6
position = {i:{'left':0, 'no-change':0, 'right':0} for i in range(nums)}
for i in range(10000):
ll = list(range(nums))
shuffle_list(ll)
for i in range(nums):
if i - 1 >= 0 and ll[i-1] == i:
position[i]['left'] += 1
elif ll[i] == i:
position[i]['no-change'] += 1
elif ll[i + 1] == i:
position[i]['right'] += 1
for d in position.items():
print(d)

测试输出:

(0, {'left': 0, 'no-change': 6739, 'right': 3261})
(1, {'left': 3261, 'no-change': 3354, 'right': 3385})
(2, {'left': 3385, 'no-change': 3333, 'right': 3282})
(3, {'left': 3282, 'no-change': 3451, 'right': 3267})
(4, {'left': 3267, 'no-change': 3408, 'right': 3325})
(5, {'left': 3325, 'no-change': 6675, 'right': 0})

基于超过10k迭代的简单测试,它表明所有元素都有大约1/3的可能性向左或向右移动,或保持在同一位置(除了第一个和最后一个元素不能移动到极端末端(。

最新更新