How do I do a biased shuffling of a deck of cards?

I currently have a deck of cards (in fact this deck is an array of sorted elements in descending order as indicated by the picture above), that I want to shuffle. However, the caveat is that I want the card near the top of the deck to be shuffled to a new position relatively close to the top, while a card near the bottom to be shuffled anywhere including the middle and bottom, but have a very unlikely chance of ending up near the top of the deck.

Therefore I would break down the problem as following:

A card in the top 33% of the deck has about:

• 90% chance of ending up in the top 33%
• 8% chance of ending up in the middle 33%
• 2 % chance of ending up in the bottom 33%

A card in the middle 33% of the deck has about:

• 5% chance of ending up in the top 33%
• 90% chance of ending up in the middle 33%
• 5 % chance of ending up in the bottom 33%

A card in the bottom 33% of the deck has about:

• 2% chance of ending up in the top 33%
• 8% chance of ending up in the middle 33%
• 90 % chance of ending up in the bottom 33%

The percentages of the shuffle do not need to be constant. I wouldn't mind if they were continuous probabilities across the entire deck. Would this boil down to a randomized selection problem? I have no clue as to how to approach this problem as of now.

I needed this same thing, a biased shuffling for local/proximity shuffling, but couldn't find any answers, so this is my attempt to create one.

Pseudocode:

For X passes through the array from 0 to len(array)-1:
Roll for a 50% chance to swap index and index+1
Swap direction of array traversal


Python:

import numpy as np
import matplotlib.pyplot as plt

swap_passes = 50

player_count = 100

positions = {}

player = np.arange(1,player_count+1)
for p in player:
positions[p] = []

for i in range(10000):
player = np.arange(1,player_count+1)

for j in range(swap_passes):
pre_rand = np.random.randint(0,2,player_count)
if j%2 == 0:
for i in range(len(player)-1):
if pre_rand[i] == 0:
player[i], player[i+1] = player[i+1], player[i]
else:
for i in range(len(player)-1,0,-1):
if pre_rand[i] == 0:
player[i], player[i-1] = player[i-1], player[i]

for idx, p in enumerate(player):
positions[p].append(idx)

fcs = [(1,0,0,0.3),(0,1,0,0.3),(0,0,1,0.3),(0.5,0.5,0,0.3),(0,0.5,0.5,0.3)]
plt.figure()
for idx, i in enumerate([1,30,50,70,100]):
bins = np.arange(0,player_count+1)
plt.hist(positions[i], bins=bins, fc=fcs[idx])
plt.title('Histogram of indices: 1, 30, 50, 70, 100. 10000 trials, 50 swap passes.')


Results:

Looks roughly gaussian. Increasing the number of passes will increase the spread of the distribution.

• Nearly 2 years later nice – SDG Aug 26 '20 at 2:02