Слепая случайная сортировка

18

Вот довольно распространенный шаблон для алгоритмов сортировки:

def sort(l):
    while not is_sorted(l):
         choose indices i, j
         assert i < j
         if l[i] > l[j]:
             l[i], l[j] = l[j], l[i]

Эти алгоритмы работают хорошо, потому что индексы iи jвыбираются тщательно, исходя из состояния списка l.

Однако, что, если мы не могли видеть l, и просто пришлось выбирать вслепую? Как быстро мы могли бы отсортировать список тогда?


Ваша задача - написать функцию, которая выводит случайную пару индексов, учитывая только длину l. В частности, вы должны вывести два индекса i, j, с 0 <= i < j < len(l). Ваша функция должна работать с любой длиной списка, но она будет оценена в списке длиной 100.

Ваша оценка - это среднее число вариантов выбора индексов, необходимых для сортировки равномерно случайно перемешанного списка в соответствии с описанным выше шаблоном, где индексы выбираются в соответствии с вашей функцией.

Я оцениваю количество заявок, принимая среднее число вариантов индекса за 1000 испытаний в равномерно случайно перемешанном списке длиной 100 без повторяющихся записей.

Я оставляю за собой право проводить меньше испытаний, если заявка явно неконкурентная или не прекращается, и я буду проводить больше испытаний, чтобы выделить лучших конкурентов и найти единственного победителя. Если на верхнем уровне моих вычислительных ресурсов останется несколько лучших заявок, я объявлю более раннюю отправку победителем до тех пор, пока не будут использованы дополнительные вычислительные ресурсы.


Вот пример программы скоринга в Python:

import random
def is_sorted(l):
    for x in range(len(l)-1):
        if l[x] > l[x+1]:
            return False
    return True

def score(length, index_chooser):
    steps = 0
    l = list(range(length))
    random.shuffle(l)

    while not is_sorted(l):
        i, j = index_chooser(length)
        assert (i < j)
        if l[i] > l[j]:
            l[i], l[j] = l[j], l[i]
        steps += 1
    return steps

Ваша функция может не поддерживать любое изменяемое состояние, взаимодействовать с глобальными переменными, влиять на список lи т. Д. Единственным вводом вашей функции должна быть длина списка l, и она должна выводить упорядоченную пару целых чисел в диапазоне [0, len(l)-1](или в соответствии с вашим языком. индексация списка). Не стесняйтесь спрашивать, разрешено ли что-то в комментариях.

Материалы могут быть на любом бесплатном языке. Пожалуйста, включите привязной ремень, если он еще не был опубликован для вашего языка. Вы можете опубликовать предварительную оценку, но я оставлю комментарий с официальным результатом.

Оценка - это среднее число шагов в отсортированном списке в равномерно случайно перемешанном списке длиной 100. Удачи.

isaacg
источник
2
@JoKing Действительно - ваше представление является рассылкой
isaacg
2
Почему вы не допускаете изменчивое состояние? Разрешение означает, что представления могут лучше настроить их алгоритмы, а не надеяться, что будут выбраны нужные элементы.
Натан Меррилл
3
@NathanMerrill Если бы изменяемое состояние было разрешено, победителем была бы просто сеть сортировки, которая уже является хорошо изученной проблемой.
Андерс Касеорг
3
@NathanMerrill Если вы хотите оставить этот вопрос, не стесняйтесь. Это не тот вопрос, однако.
Исаак
3
@NathanMerrill О, конечно. Задача «Спроектировать лучшую сортировочную сеть», хотя и является интересным вопросом, много изучалась в мире исследований CS. В результате, лучшие материалы, вероятно, будут состоять из реализаций исследовательских работ, таких как Битчер. Насколько я знаю, вопрос, который я здесь задал, является оригинальным, и поэтому у него должно быть больше возможностей для инноваций.
Исаак

Ответы:

10

Питон, оценка = 4508

def half_life_3(length):
    h = int(random.uniform(1, (length / 2) ** -3 ** -0.5) ** -3 ** 0.5)
    i = random.randrange(length - h)
    return i, i + h

Half-Life 3 подтвержден.

Python, счет = 11009

def bubble(length):
    i = random.randrange(length - 1)
    return i, i + 1

По-видимому, рандомизированная пузырьковая сортировка не делает все намного хуже, чем нормальная пузырьковая сортировка.

Оптимальные распределения для малой длины

Это никак не может быть расширено до длины 100, но в любом случае интересно посмотреть. Я вычислил оптимальные распределения для небольших случаев (длина ≤ 7), используя градиентный спуск и множество матричной алгебры. В столбце k показана вероятность каждого обмена на расстоянии k .

length=1
score=0.0000

length=2
1.0000
score=0.5000

length=3
0.5000 0.0000
0.5000
score=2.8333

length=4
0.2957 0.0368 0.0000 
0.3351 0.0368 
0.2957 
score=7.5106

length=5
0.2019 0.0396 0.0000 0.0000 
0.2279 0.0613 0.0000 
0.2279 0.0396 
0.2019 
score=14.4544

length=6
0.1499 0.0362 0.0000 0.0000 0.0000 
0.1679 0.0558 0.0082 0.0000 
0.1721 0.0558 0.0000 
0.1679 0.0362 
0.1499 
score=23.4838

length=7
0.1168 0.0300 0.0041 0.0000 0.0000 0.0000 
0.1313 0.0443 0.0156 0.0000 0.0000 
0.1355 0.0450 0.0155 0.0000 
0.1355 0.0443 0.0041 
0.1313 0.0300 
0.1168 
score=34.4257
Андерс Касеорг
источник
Ваша оценка: 11009
isaacg
2
Можете ли вы объяснить свой ответ Half Life 3 немного? Смысл в том, чтобы сместить случайное число в начале списка?
Макс
1
Оптимальные распределения для малой длины очень интересны - я заметил, что смещение к центру полезно, особенно для большего расстояния свопа.
Исаак
@Max Вся проблема заключается в смещении случайных чисел полезными способами; этот способ оказался полезным. Обратите внимание, что hэто расстояние между замененными элементами; это не представляет спереди или сзади.
Андерс Касеорг
1
Ваш период полураспада: 4508 на 10000 образцов.
Исаак
7

Счет: 4627

def rand_step(n):
	step_size = random.choice([1, 1, 4, 16])
	
	if step_size > n - 1:
		step_size = 1 
	
	start = random.randint(0, n - step_size - 1)
	return (start, start + step_size)

Попробуйте онлайн!

Вывод случайных индексов, расстояние между которыми выбирается равномерно [1,1,4,16]. Идея состоит в том, чтобы иметь комбинацию одношаговых свопов со свопами в больших масштабах.

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

XNOR
источник
1
Ваша оценка: 4627 на 10000 образцов. Я проведу его снова с большим количеством образцов, если вы попадете в число лидеров через несколько дней.
Исаак
3

Счет: 28493

def x_and_y(l):
    x = random.choice(range(l))
    y = random.choice(range(l))
    while y == x and l != 1: y = random.choice(range(l))
    return sorted([x,y])

Попробуйте онлайн!

Это решение просто выбирает различные значения для xи yслучайным образом из диапазона и возвращает их в отсортированном порядке. Насколько я могу судить, это работает лучше, чем выбор, а xзатем выбор yиз оставшихся значений.

Джо Кинг
источник
Ваш счет: 28493
Исаак
3

Питон, оценка: 39525

def get_indices(l):
    x = random.choice(range(l-1))
    y = random.choice(range(x+1,l))
    return [x,y]

[0,l1)x
x[x+1,l)y

Попробуйте онлайн.

Кевин Круйссен
источник
Ваш счет: 39525
Исаак
2

Питон, оценка ≈ 5000

def exponentialDistance(n):
    epsilon = 0.25
    for dist in range(1, n):
        if random.random() < epsilon:
            break
    else:
        dist = 1
    low = random.randrange(0, n - dist)
    high = low + dist
    return low, high

Опробовано несколько значений эпсилона, 0,25 кажется лучшим.

Оценка ≈ 8881

def segmentedShuffle(n):
    segments = 20
    segmentLength = (n - 1) // segments + 1

    if random.random() < 0.75:
        a = b = 0
        while a == b or a >= n or b >= n:
            segment = random.randrange(segments)
            a = random.randrange(segmentLength) + segment * segmentLength
            b = random.randrange(segmentLength) + segment * segmentLength
        return sorted([a, b])

    highSegment = random.randrange(1, segments)
    return highSegment * segmentLength - 1, highSegment * segmentLength

Другой подход. Не так хорошо, и он ужасно умирает с длиной, не делимой на количество сегментов, но все же интересно строить.


источник
Твои оценки: Экспоненциальная дистанция: 5055. Сегментированная перестановка: 8901
Исаак
1

Счет: 4583

def rand_shell(l):
    steps = [1, 3, 5, 9, 17, 33, 65, 129]
    candidates = [(left, left + step)
            for (step, nstep) in zip(steps, steps[1:])
            for left in range(0, l - step)
            for i in range(nstep // step)
    ]
    return random.choice(candidates)

Попробуйте онлайн!

Понятия не имею почему. Я только что попробовал последовательности, перечисленные в Википедии, artical для сортировки оболочек . И этот, кажется, работает лучше всего. Он получил аналогичный счет с одним опубликованным xnor .

ТТГ
источник
Ваша оценка: 4583 на 10000 образцов. Я попробую еще раз с большим количеством сэмплов, если вы попадете в лидеры через несколько дней.
Исаак
Кроме того, я запускаю более быструю программу, которая пробует тот же дистрибутив, чтобы я мог получить больше семплов.
Исаак
2
@isaacg Для лучшей производительности тестирования, candidatesвыход из функции в качестве глобальной переменной должен работать.
TSH
1
Спасибо, это намного быстрее, чем я делал.
Исаак
1

Python 2 , 4871

import random
def index_chooser(length):
    e= random.choice([int(length/i) for i in range(4,length*3/4)])
    s =random.choice(range(length-e))
    return [s,s+e]
def score(length, index_chooser):
    steps = 0
    l = list(range(length))
    random.shuffle(l)
    while True:
        for x in range(length-1):
            if l[x] > l[x+1]:
                break
        else:
            return steps
        i, j = index_chooser(length)
        assert(i < j)
        if l[i] > l[j]:
            l[i], l[j] = l[j], l[i]
        steps += 1

print sum([score(100, index_chooser) for t in range(100)])

Попробуйте онлайн!

l4m2
источник
Ваш счет: 4871 на 10000 образцов
Исаак