Ordinamento casuale cieco


18

Ecco un modello abbastanza comune per gli algoritmi di ordinamento:

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]

Questi algoritmi funzionano bene perché gli indici ie jsono scelti con cura, in base allo stato della lista l.

Tuttavia, se non potessimo vedere le dovessimo scegliere alla cieca? Quanto velocemente potremmo ordinare l'elenco allora?


La tua sfida è scrivere una funzione che genera una coppia casuale di indici, data solo la lunghezza di l. In particolare, è necessario generare due indici i, j, con 0 <= i < j < len(l). La tua funzione dovrebbe funzionare su qualsiasi lunghezza dell'elenco, ma verrà assegnata un punteggio su un elenco di lunghezza 100.

Il tuo punteggio è il numero medio di scelte di indice necessarie per ordinare un elenco mescolato in modo uniformemente casuale secondo lo schema sopra, in cui gli indici sono scelti in base alla tua funzione.

Segnerò le osservazioni, prendendo il numero medio di scelte di indice su 1000 prove in un elenco mischiato in modo uniformemente casuale di lunghezza 100 senza voci ripetute.

Mi riservo il diritto di eseguire meno prove se la presentazione è chiaramente non competitiva o non termina e eseguirò più prove per differenziare i migliori concorrenti per trovare un singolo vincitore. Se più invii principali rimangono entro il margine di errore al limite delle mie risorse computazionali, dichiarerò il precedente invio il vincitore, fino a quando ulteriori risorse computazionali potranno essere utilizzate.


Ecco un esempio di programma di punteggio, in 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

La tua funzione potrebbe non mantenere uno stato mutabile, interagire con variabili globali, influire sull'elenco l, ecc. L'unico input della tua funzione deve essere la lunghezza dell'elenco le deve generare una coppia ordinata di numeri interi nell'intervallo [0, len(l)-1](o appropriato per la tua lingua indicizzazione elenco). Sentiti libero di chiedere se qualcosa è permesso nei commenti.

Gli invii possono essere in qualsiasi lingua gratuita. Si prega di includere un imbragatura se uno non è già stato pubblicato nella tua lingua. Puoi pubblicare un punteggio provvisorio, ma lascerò un commento con il punteggio ufficiale.

Il punteggio è il numero medio di passaggi per un elenco ordinato in un elenco mischiato in modo uniformemente casuale di lunghezza 100. Buona fortuna.


2
@JoKing Infatti - la tua presentazione è una distribuzione
isaacg,

2
Perché non permetti lo stato mutabile? Autorizzarlo significa che gli invii possono mettere a punto meglio i loro algoritmi, invece di sperare che vengano scelti gli oggetti giusti.
Nathan Merrill,

3
@NathanMerrill Se fosse consentito lo stato mutevole, il vincitore sarebbe solo una rete di smistamento che è già un problema ben studiato.
Anders Kaseorg,

3
@NathanMerrill Se vuoi pubblicare quella domanda, sentiti libero. Non è questa domanda, comunque.
isaacg,

3
@NathanMerrill Oh, certo. La sfida "Progettare la migliore rete di smistamento", mentre una domanda interessante, è stata studiata molto nel mondo della ricerca CS. Di conseguenza, le proposte migliori consisterebbero probabilmente solo in implementazioni di articoli di ricerca, come l'ordinamento bitonico di Batcher. La domanda che ho posto qui è originale per quanto ne so, e quindi dovrebbe avere più spazio per l'innovazione.
isaacg,

Risposte:


10

Python, punteggio = 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 confermato.

Python, punteggio = 11009

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

Apparentemente un ordinamento a bolle randomizzato non fa molto peggio di un ordinamento a bolle normale.

Distribuzioni ottimali per piccole lunghezze

Non è possibile estenderlo a 100, ma è comunque interessante osservarlo. Ho calcolato le distribuzioni ottimali per piccoli casi (lunghezza ≤ 7) usando la pendenza del gradiente e molta algebra matriciale. La colonna K mostra la probabilità di ogni scambio alla distanza 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

Il tuo punteggio: 11009
isaacg,

2
Puoi spiegare un po 'la tua emivita 3? Il punto è solo quello di inclinare il numero casuale verso la parte anteriore dell'elenco?
Max

1
Le distribuzioni ottimali per piccole lunghezze sono molto interessanti - noto che la distorsione verso il centro è utile, specialmente per una maggiore distanza di scambio.
isaacg,

@Max L'intero problema riguarda la differenziazione dei numeri casuali in modi utili; in questo modo è stato utile. Si noti che hè la distanza tra gli elementi scambiati; non rappresenta la parte anteriore o posteriore.
Anders Kaseorg,

1
Il tuo punteggio emivita: 4508 su 10000 campioni.
isaacg,

7

Punteggio: 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)

Provalo online!

Emette indici casuali la cui distanza è scelta uniformemente da [1,1,4,16]. L'idea è quella di avere un mix di swap a 1 passaggio con swap su scale più grandi.

Ho modificato a mano questi valori per elenchi di lunghezza 100 e probabilmente sono tutt'altro che ottimali. Alcune ricerche automatiche potrebbero probabilmente ottimizzare la distribuzione su distanze per la strategia di coppia casuale con la distanza scelta.


1
Il tuo punteggio: 4627 su 10.000 campioni. Lo eseguirò di nuovo con più campioni se sarai tra i leader dopo pochi giorni.
isaacg,

3

Punteggio: 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])

Provalo online!

Questa soluzione seleziona semplicemente valori distinti per xe ycasualmente dall'intervallo e li restituisce in ordine. Per quanto posso dire, questo funziona meglio di scegliere xquindi scegliere ytra i valori rimanenti.


Il tuo punteggio: 28493
isaacg,

3

Python, punteggio: 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

Provalo online.


Il tuo punteggio: 39525
isaacg,

2

Python, punteggio ≈ 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

Provato con un sacco di valori epsilon, 0,25 sembra essere il migliore.

Punteggio ≈ 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

Un approccio diverso. Non altrettanto buono, e muore orribilmente con una lunghezza non divisibile per il numero di segmenti, ma comunque divertente da costruire.


I tuoi punteggi: Distanza esponenziale: 5055. Shuffle segmentato: 8901
isaacg

1

Punteggio: 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)

Provalo online!

Non ho idea del perché. Ho appena provato sequenze elencate su Wikipedia artistico per shellsort . E questo sembra funzionare meglio. Ottiene un punteggio simile con quello xnor pubblicato .


Il tuo punteggio: 4583 su 10.000 campioni. Lo eseguirò di nuovo con più campioni se tra qualche giorno sarai tra i leader.
isaacg,

Inoltre, sto eseguendo un programma più veloce che campiona la stessa distribuzione, in modo da poter ottenere più campioni.
isaacg,

2
@isaacg Per migliorare le prestazioni dei test, candidatesuscire dalla funzione come variabile globale dovrebbe funzionare.
TSH

1
Grazie, è molto più veloce di quello che stavo facendo.
isaacg,

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)])

Provalo online!


Il tuo punteggio: 4871 su 10000 campioni
isaacg,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.