Как слегка перетасовать список в python - PullRequest
0 голосов
/ 17 июня 2020

У меня возникла проблема, когда я хотел бы перетасовать список, но только немного. Скажем, мне нужно переместить только небольшое количество элементов. Есть ли простой способ сделать это?

Сейчас лучшее, что я могу придумать, - это создать свой собственный метод вручную, но есть ли способ использовать библиотеку random, чтобы сделать это за меня?

Ответы [ 6 ]

2 голосов
/ 19 июня 2020

, чтобы показать, что делают некоторые из этих решений. Я считаю, что это помогает запустить алгоритм Монте-Карло много раз и посмотреть на распределение.

сначала приведенная в порядок версия решения @meta4, поскольку это было конкретизировано:

from random import randrange

def partial_shuffle(l, factor=5):
    n = len(l)
    for _ in range(factor):
        a, b = randrange(n), randrange(n)
        l[b], l[a] = l[a], l[b]

, которое мы можем запускать много раз, выполняя:

import numpy as np

n = 8
orig = list(range(n))
occur = np.zeros((n, n), int)

for _ in range(100000):
    x = orig[:]
    partial_shuffle(x,1)
    occur[orig,x] += 1

если мы распечатаем таблицу вхождений в виде процентов, мы получим:

[[33.5  9.6  9.5  9.4  9.4  9.6  9.5  9.5]
 [ 9.6 33.2  9.7  9.5  9.6  9.6  9.4  9.4]
 [ 9.5  9.6 33.2  9.5  9.6  9.5  9.6  9.5]
 [ 9.5  9.3  9.6 33.4  9.5  9.5  9.5  9.6]
 [ 9.4  9.6  9.4  9.6 33.3  9.5  9.7  9.5]
 [ 9.6  9.5  9.6  9.6  9.4 33.3  9.5  9.6]
 [ 9.4  9.7  9.5  9.5  9.5  9.6 33.2  9.7]
 [ 9.5  9.5  9.6  9.5  9.7  9.5  9.6 33.2]]

каждая строка представляет вероятность перемещения элемента в столбец. в этом случае (когда n=8) алгоритм будет иметь тенденцию оставлять элементы там, где они были ~ 33% времени, а затем равномерно выбирать остаток

Затем я могу запустить (приведенную в порядок) версию p Код js с:

from random import gauss

orderliness = 2

occur = np.zeros((n, n), int)

for _ in range(100000):
    x = sorted(orig, key=lambda i: gauss(i * orderliness, 1))
    occur[orig,x] += 1

, который дает совсем другой результат:

[[91.9  7.9  0.1  0.   0.   0.   0.   0. ]
 [ 7.9 84.1  7.8  0.1  0.   0.   0.   0. ]
 [ 0.1  7.8 84.1  7.9  0.1  0.   0.   0. ]
 [ 0.   0.1  7.9 84.1  7.7  0.1  0.   0. ]
 [ 0.   0.   0.1  7.7 84.2  7.8  0.1  0. ]
 [ 0.   0.   0.   0.1  7.9 84.2  7.7  0.1]
 [ 0.   0.   0.   0.   0.1  7.7 84.2  7.9]
 [ 0.   0.   0.   0.   0.   0.1  7.9 91.9]]

т.е. элементы, как правило, остаются близко к тому месту, где они были начаты

этот вид of table отлично справляется с обнаружением смещения в распределении, о чем, похоже, нет свидетельств выше. но, например, с решением Артема (shuffle(x, lambda: random() / 5)) дает следующее:

[[  0.   37.4   0.    0.    0.   16.7  23.8  22.1]
 [  0.    0.  100.    0.    0.    0.    0.    0. ]
 [  0.    0.    0.  100.    0.    0.    0.    0. ]
 [  0.    0.    0.    0.  100.    0.    0.    0. ]
 [  1.7   0.    0.    0.    0.   83.3  11.9   3. ]
 [  9.    7.4   0.    0.    0.    0.   64.2  19.4]
 [ 26.7  17.9   0.    0.    0.    0.    0.   55.5]
 [ 62.6  37.4   0.    0.    0.    0.    0.    0. ]]

, что, вероятно, не то, что хотел OP. высокая вероятность не по диагонали представляет собой поворот массива на один элемент

2 голосов
/ 18 июня 2020

Одна из интерпретаций - строго или слабо сохранить исходный порядок. Самым слабым удержанием будет полностью случайное перемешивание, самым сильным - отсутствие отклонения от первоначального порядка.

Это может быть достигнуто путем создания кортежа, состоящего из исходного индекса, масштабированного константой, плюс некоторая случайность, за которым следует значение. Отсортируйте кортежи, затем выполните итерацию, чтобы восстановить исходные значения в новом порядке. Если коэффициент масштабирования индекса близок к нулю, новый порядок будет случайным. Если он близок к 1, вещи будут иметь тенденцию к сильному, но не идеально сохранению своего первоначального порядка. Если он больше, маловероятно, что результат будет перемешан.

import random

orderliness = 0.75

def tuplify(x, y):
    return (orderliness * y + random.gauss(0,1), x)

values = [i+1 for i in range(20)]
print(values)
pairs = list(map(tuplify, values, range(len(values))))
pairs.sort()
partially_ordered_values = [p[1] for p in pairs]
print(partially_ordered_values)

Это дает, например:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]  # initial ordering
[2, 1, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 11, 14, 17, 16, 15, 18, 19, 20]  # weakly shuffled

Тенденция к перемешиванию будет определяться относительными величинами orderliness и стандартное отклонение в random.gauss().

1 голос
/ 17 июня 2020
from random import randint

def partial_shuffle(l, factor=5):
    for _ in range(factor):
        a, b = randint(0, len(l)), randint(0, len(l)) # pick two random indexes
        l[b], l[a] = l[a], l[b] # swap the values at those indexes
    return l

Рекомендуется частичное перемешивание Fisher-Yates @rossum.

''.join(partial_shuffle(list('abcdefghijklmnopqrstuvwxyz'), 2))

В этом примере получается «abcdef n hijklm g opqrs y uvwx t z ", из одного прогона, но даст что-то еще для другого прогона.

0 голосов
/ 17 июня 2020

Можно также интерпретировать слегка перетасовку в том смысле, что существует вероятность перетасовки элементов на каждом шаге алгоритма Фишера-Йейтса, упомянутых @rossum и @ meta4 (вместо фиксированного количества элементов перетасовано).

def conditional_fy(l, p):
    """Shuffle elements of a list with a given probability

    Args:
        l: list
        p: shuffle probability
            (0: elements are never shuffled,
             1: elements are always shuffled)

    """
    assert 0 <= p <= 1

    for i in range(len(l) - 1, 0, -1):
        shuffle = random.random()
        if shuffle < p:
            j = random.randint(0, i - 1)
            l[i], l[j] = l[j], l[i]
0 голосов
/ 17 июня 2020

есть shuffle в модуле random, который принимает 1 обязательный аргумент list объект, а второй random аргумент random принимает объект функции, который возвращает float число из От 0,0 до 1,0 , вы можете написать только новую функцию, которая обычно будет возвращать меньше 0,5

import random

def rand():
    return random.random() / 5

arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
random.shuffle(arr, random=rand)

ВЫХОД

[9, 3, 4, 5, 6, 7, 8, 1, 2]
0 голосов
/ 17 июня 2020

Используйте перемешивание Фишера-Йейтса , но не запускайте его для всего списка. Просто выполните один шаг для каждой записи, которую вы хотите переместить: 5 шагов для перемещения 5 записей, 10 шагов для перемещения 10 записей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...