Ordenar una lista de valores desconocidos con la menor cantidad de comparaciones

8

En este desafío de optimización, escribirá un programa que clasifica una sola matriz al comparar solo elementos al pedirle al usuario en stdout que ingrese los resultados de comparación en stdin.

El siguiente protocolo está basado en líneas, por lo que cada vez que imprime en stdout o lee desde stdin se supone que sigue una nueva línea. A lo largo de la siguiente pregunta, se supone que el usuario (léase: el programa de puntuación) tiene una matriz que desea ordenar en una matriz indexada basada en 0 llamada array. Por razones técnicas, sugiero enjuagar después de cada impresión para que sea robusto.

  1. Como primer paso, su programa debe leer el tamaño nde la matriz de stdin.
  2. Luego, con la frecuencia que desee, puede imprimir a < ben stdout, con dos enteros 0 <= a, b < n. Entonces el usuario ingresará 1en stdin si array[a] < array[b], y de lo 0contrario.
  3. Finalmente, una vez que su programa está convencido de que ha deducido correctamente el orden de la matriz, debe imprimir a ...en stdout, donde ...hay una lista de enteros separados por espacios que indican el orden. Entonces, si su programa genera a 3 0 1 4 2, significa que su programa ha deducido

    array[3] <= array[0] <= array[1] <= array[4] <= array[2]
    

    Tenga en cuenta que su programa nunca supo el contenido de array, y nunca lo sabrá.

Solo puede pedir <en stdin para ordenar la matriz. Puede obtener otras operaciones de comparación mediante estas equivalencias:

a > b        b < a
a <= b       !(b < a)
a >= b       !(a < b)
a != b       (a < b) || (b < a)
a == b       !(a < b) && !(b < a)

Finalmente, dado que su programa interactúa con el programa de puntuación en stdout, imprima la información de depuración en stderr.


Su programa se puntuará utilizando el siguiente programa de Python:

from __future__ import print_function
from subprocess import Popen, PIPE
import sys, random

def sort_test(size):
    array = [random.randrange(0, size) for _ in range(size)]
    pipe = Popen(sys.argv[1:], stdin=PIPE, stdout=PIPE, bufsize=0, universal_newlines=True)
    print(str(size), file=pipe.stdin); pipe.stdin.flush()
    num_comparisons = 0
    while True:
        args = pipe.stdout.readline().strip().split()
        if args and args[0] == "a":
            answer_array = [array[int(n)] for n in args[1:]]
            if list(sorted(array)) != answer_array:
                raise RuntimeError("incorrect sort for size {}, array was {}".format(size, array))
            return num_comparisons
        elif len(args) == 3 and args[1] == "<":
            a, b = int(args[0]), int(args[2])
            print(int(array[a] < array[b]), file=pipe.stdin); pipe.stdin.flush()
            num_comparisons += 1
        else:
            raise RuntimeError("unknown command")

random.seed(0)
total = 0
for i in range(101):
    num_comparisons = sort_test(i)
    print(i, num_comparisons)
    total += num_comparisons
print("total", total)

Usted puntúa su programa escribiendo python score.py yourprogram. La puntuación se realiza haciendo que su programa clasifique una matriz aleatoria de cada tamaño de 0 a 100 y contando la cantidad de comparaciones que solicita su programa. Estas matrices aleatorias pueden tener duplicados , su algoritmo debe ser capaz de manejar elementos iguales. En el caso de que haya duplicados, no hay requisitos en el orden de los elementos iguales. Entonces, si la matriz es [0, 0], no importa si la salida a 0 1o a 1 0.

No puede optimizar específicamente para las matrices particulares que genera el programa de puntuación. Puedo cambiar la semilla de RNG en cualquier momento. Las respuestas que utilizan algoritmos de clasificación incorporados pueden publicarse por interés, pero no son competitivas.

Programa con menor puntaje gana.

orlp
fuente
1
¿Qué versión de Python estás usando?
Leaky Nun
¿Cuánto tiempo llevó ejecutar su algoritmo de puntuación?
Leaky Nun
¿Qué suposiciones podemos hacer sobre el tamaño máximo de la matriz de entrada?
helloworld922
@ helloworld922 Teóricamente, ninguno: su respuesta debería funcionar para cualquier tamaño. Pero si se ahorra esfuerzo en un lenguaje como C, se requiere soporte para todo e incluir 100 elementos.
orlp
Es importante aclarar la versión de Python utilizada por el programa de puntuación, porque Python 2 y 3 generan diferentes secuencias aleatorias. O quizás sería mejor si la aleatoriedad provenía de una fuente determinista bien especificada como el hashlib.
Anders Kaseorg

Respuestas:

3

Python, puntaje 23069

Esto utiliza el algoritmo de clasificación de inserción de combinación Ford – Johnson.

from __future__ import print_function
import sys

def less(x, y):
    print(x, '<', y)
    sys.stdout.flush()
    return bool(int(input()))

def insert(x, a, key=lambda z: z):
    if not a:
        return [x]
    m = len(a)//2
    if less(key(x), key(a[m])):
        return insert(x, a[:m], key) + a[m:]
    else:
        return a[:m + 1] + insert(x, a[m + 1:], key)

def sort(a, key=lambda z: z):
    if len(a) <= 1:
        return a
    m = len(a)//2
    key1 = lambda z: key(z[-1])
    b = sort([insert(x, [y], key) for x, y in zip(a[:m], a[m:2*m])], key=key1)
    if len(a) % 2:
        b += [[a[-1], None]]
    for k in range(m, len(a)):
        l, i, (x, y) = max((-i.bit_length(), i, t) for i, t in enumerate(b) if len(t) == 2)
        b[:i + 1] = insert([x], b[:i], key=key1) + [[y]]
    if len(a) % 2:
        b.pop()
    return [x for x, in b]

print('a', ' '.join(map(str, sort(range(int(input()))))))
Anders Kaseorg
fuente
1

Python 3, 28462 puntaje

Ordenación rápida. El pivote es el elemento más a la izquierda.

El puntaje se espera porque:

\ displaystyle \ sum_ {i \ mathop = 0} ^ {100} i \ log_2i = 29945.648687873225

from __future__ import print_function
import sys
size = int(input())

def less(a, b):
    print(a, "<", b); sys.stdout.flush()
    return bool(int(input()))

def quicksort(array):
    if len(array) < 2:
        return array
    pivot = array[0]
    left = []
    right = []
    for i in range(1,len(array)):
        if less(array[i],pivot):
            left.append(array[i])
        else:
            right.append(array[i])
    return quicksort(left)+[pivot]+quicksort(right)

array = list(range(size))
array = quicksort(array)

print("a", " ".join(str(i) for i in array)); sys.stdout.flush()

Puntuaciones para cada tamaño probado:

size score
0 0
1 0
2 1
3 3
4 6
5 8
6 11
7 12
8 15
9 17
10 23
11 33
12 29
13 32
14 37
15 45
16 58
17 47
18 52
19 79
20 72
21 60
22 85
23 138
24 87
25 98
26 112
27 107
28 128
29 142
30 137
31 136
32 158
33 143
34 145
35 155
36 169
37 209
38 163
39 171
40 177
41 188
42 167
43 260
44 208
45 210
46 230
47 276
48 278
49 223
50 267
51 247
52 263
53 293
54 300
55 259
56 319
57 308
58 333
59 341
60 306
61 295
62 319
63 346
64 375
65 344
66 346
67 370
68 421
69 507
70 363
71 484
72 491
73 417
74 509
75 495
76 439
77 506
78 484
79 458
80 575
81 505
82 476
83 500
84 535
85 501
86 575
87 547
88 522
89 536
90 543
91 551
92 528
93 647
94 530
95 655
96 580
97 709
98 671
99 594
100 637
total 28462
Monja permeable
fuente
@orlp aquí , la última línea se traduce como "WindowsError: [Error 193]% 1 no es una aplicación Win32 correcta".
Leaky Nun
1

Python 2 (no competidor), puntaje 23471

from __future__ import print_function
import sys
size = int(input())

def cmp(a, b):
    print(a, "<", b); sys.stdout.flush()
    return [1,-1][bool(int(input()))]

array = list(range(size))
array = sorted(array,cmp=cmp)

print("a", " ".join(str(i) for i in array)); sys.stdout.flush()

Puntuaciones para cada tamaño probado:

size score
0 0
1 0
2 1
3 4
4 5
5 7
6 9
7 14
8 17
9 20
10 21
11 26
12 30
13 35
14 37
15 41
16 45
17 51
18 52
19 57
20 63
21 63
22 72
23 79
24 79
25 80
26 91
27 93
28 96
29 105
30 110
31 116
32 124
33 123
34 131
35 130
36 142
37 144
38 156
39 154
40 163
41 168
42 177
43 178
44 183
45 183
46 192
47 194
48 212
49 207
50 216
51 221
52 227
53 239
54 238
55 243
56 255
57 257
58 260
59 270
60 281
61 284
62 292
63 293
64 303
65 308
66 312
67 321
68 328
69 328
70 342
71 348
72 352
73 358
74 367
75 371
76 381
77 375
78 387
79 400
80 398
81 413
82 415
83 427
84 420
85 435
86 438
87 448
88 454
89 462
90 462
91 479
92 482
93 495
94 494
95 502
96 506
97 520
98 521
99 524
100 539
total 23471
Monja permeable
fuente
Creo que Python ya implementa una muy buena función de clasificación, observando el número óptimo de comparaciones aquí: en.wikipedia.org/wiki/… Parece que esto será una referencia para una muy buena línea de base.
justhalf
1

Python, 333300 puntaje

from __future__ import print_function
import sys

size = int(input())

def less(a, b):
    print(a, "<", b); sys.stdout.flush()
    return bool(int(input()))

array = list(range(size))
for _ in range(size):
    for i in range(0, size-1):
        if less(array[i+1], array[i]):
            array[i], array[i+1] = array[i+1], array[i]

print("a", " ".join(str(i) for i in array)); sys.stdout.flush()

Este es un programa de ejemplo, implementando la forma más simple de bubbleort, siempre haciendo n*(n-1)comparaciones por arreglo.

Marqué este programa guardándolo como sort.pyy escribiendo python score.py python sort.py.

orlp
fuente
Si es exacto, es Python 3. Lo veo printpareciendo una función.
user48538
1
@ zyabin101 Ahora son ambos :)
orlp