Calcule la distancia de Hausdorff

21

Introducción

La distancia de Hausdorff mide la diferencia entre dos subconjuntos de un espacio métrico. Intuitivamente, un espacio métrico es solo un conjunto con una función de distancia incorporada; En este desafío, utilizaremos números naturales con la distancia ordinaria d(a, b) := abs(a - b). La distancia de Hausdorff entre dos conjuntos finitos no vacíos Ay Bestá dada por

max(max(min(d(a, b) for b in B) for a in A),
    max(min(d(a, b) for a in A) for b in B))

en notación similar a Python. La distancia de Hausdorff se puede calcular encontrando el elemento Acuya distancia al elemento más cercano Bes máxima, y ​​el elemento Bcuya distancia al elemento más cercano Aes máxima, y ​​luego tomando el máximo de estas distancias. En otras palabras, si la distancia de Hausdorff es d, entonces cada elemento de Aestá dentro de la distancia dde algún elemento de B, y viceversa.

Entrada

Su entrada es una lista única de enteros. Solo contiene los elementos 0,1,2,3, que significan si el índice dado de la lista es un elemento de ni Atampoco B, solo A, solo Bo ambos Ay B. Por ejemplo, la entrada [0,1,1,0,2,3]significa eso A = {1,2,5}y B = {4,5}, si usamos indexación basada en 0 (lo que no hace ninguna diferencia, ya que nuestras métricas son invariables en la traducción).

Salida

Su salida es la distancia de Hausdorff entre Ay B; en el ejemplo anterior, lo es 3. Si cualquiera de los conjuntos está vacío, la distancia no está definida y usted deberá regresar -1.

Reglas

Puede escribir un programa completo o una función. El conteo de bytes más bajo gana, y las lagunas estándar no se permiten.

Casos de prueba

[] -> -1
[0] -> -1
[0,1,0] -> -1
[2,0,0,2] -> -1
[0,1,2,3] -> 1
[0,3,3,0,0,0,0,3] -> 0
[1,0,0,1,0,0,1,3,1] -> 7
[1,0,0,0,0,3,0,0,0,0,2] -> 5
[0,1,1,3,1,3,2,1,1,3,0,3] -> 2
[2,2,2,1,2,0,3,1,3,1,0,3] -> 3
[1,3,0,2,0,2,2,1,0,3,2,1,1,2,2] -> 2
[1,0,1,1,2,0,1,2,3,1,0,0,0,1,2,0] -> 4
Zgarb
fuente
En su ecuación, creo que es demasiado largo, como max(max(min(d(a, b) for b in B) for a in A))debería ser suficiente. Esto se debe a que d(a,b)devuelve el valor absoluto y, por lo tanto, ambas funciones max devolverán el mismo número cada vez.
Nathan Merrill
66
@NathanMerrill Puede ser que cada elemento de Aesté muy cerca de uno de ellos B, pero hay elementos Bmuy alejados de A(por ejemplo, si Aes un subconjunto de B). En ese caso, la fórmula corta es incorrecta.
Zgarb

Respuestas:

7

CJam, 53 52 46 38 37 bytes

3,q~f{f&:L,{L=},}$~ff{-z}_z+::e<W+:e>

Toma entrada en STDIN como una matriz de estilo CJam:

[0 1 2 3]

Aquí hay un arnés de prueba que convierte todos los casos de prueba a este formato y ejecuta el código en ellos. Aunque los resultados están en el campo de entrada, el código no los utiliza (elimínelos si no confía en mí :)).

Explicación

Primero, analizamos la entrada para obtener los dos conjuntos A y B:

3,q~f{f&:L,{L=},}$~
3,                  "Push [0 1 2]. 1 is for A, 2 is for B, and 0 we can luckily ignore
                     as we'll see later.";
  q~                "Read and evaluate the input.";
    f{          }   "Map this block onto the [0 1 2] array, copying in the input for
                     each iteration.";
      f&:L          "Take the bitwise AND with each element of the input and store the
                     result in L.";
          ,{  },    "Get the length N, and filter the range [0 .. N-1] by evaluating
                     the block for each element.";
            L=      "Check if the bitwise AND at that index yielded something non-zero.
                     This gives an empty array for 0, A for 1 and B for 2.";
                 $  "Sort the three arrays. This has two important effects: a) it moves
                     the empty array resulting from 0 to the front, and b) if only one
                     of A and B is empty, it moves the non-empty one to the end.";
                  ~ "Unwrap the array, dumping all three sets on the stack.";

Y ahora encontramos las diferencias absolutas y seleccionamos el máximo de minutos:

ff{-z}_z+::e<W+:e>
ff{-z}             "Turn A and B into a matrix of absolute differences.";
      _z           "Duplicate and transpose.";
        +          "Add the two together, so I've got one row of distances for
                    each element in either A or B.";
         ::e<      "Find the minimum of each row.";
             W+    "Add a -1 in case one set was empty.";
               :e> "Get the overall maximum.";

Tenga en cuenta que hemos mantenido la matriz vacía resultante de la inicial 0en la parte inferior de la pila todo el tiempo, pero las matrices vacías no contribuyen en nada a la salida.

Martin Ender
fuente
5

CJam, 57 56 52 bytes

Creo que esto se puede jugar un poco, pero aquí va:

q~ee_{W=2%},\{W=1>},]0ff=_W%]{~ff-{:z$1<~}%W+$W=}/e>

La entrada entra como una lista de estilo CJam, por ejemplo.

[1 0 0 0 0 3 0 0 0 0 2]

5 5

Cómo funciona :

El código se divide en dos partes:

Analizando la entrada en las listas Ay B:

q~ee_{W=2%},\{W=1>},]0ff=_W%]
q~                               "Eval the input array";
  ee                             "Enumerate and prepend index with each element. For ex:
                                  [5 3 6]ee gives [[0 5] [1 3] [2 6]]";
    _{W=2%},                     "Make a copy and filter out elements with value 1 or 3";
            \{W=1>},             "On the original, filter elements with value 2 or 3";
                    ]            "Wrap stack in an array. Stack right now contains
                                  enumerated A and B in an array";
                     0ff=        "Get the index of the enumerated arrays. Stack is [A B]";
                         _W%     "Make a copy and swap order. Stack is now [A B] [B A]";
                            ]    "Wrap this in an array";

Realizando las acciones requeridas en los dos pares de Ay B:

{~ff-{:z$1<~}%W+$W=}/e>
{                  }/            "Run this loop for both the pairs, [A B] and [B A]"
 ~ff-                            "Unwrap [A B] and take difference of every pair";
     {      }%                   "For each row in the matrix difference";
      :z$                        "abs each term and then sort";
         1<~                     "Take only the first element of the array";
              W+                 "Add -1 to compensate for an empty array";
                $W=              "Take max";
                     e>          "Take max of the two maximums";

Pruébalo en línea aquí

Optimizador
fuente
5

Lua, 235 bytes

Definitivamente no es un ganador, pero al menos es un desafío divertido.

A={}B={}c={}d={}m=math p=m.min q=m.max u=unpack for k=1,#arg do for h=0,1 do if
arg[k]/2^h%2>=1 then A[#A+1]=k for i=1,#B do l=m.abs(B[i]-k)d[i]=p(d[i]or
l,l)c[#A]=p(c[#A]or l,l)end end A,B=B,A c,d=d,c end end
print(q(q(-1,u(c)),u(d)))

La entrada funciona así:

lua hausdorff.lua <space-separated-sequence>

... y aquí hay un script de prueba:

local testcase = arg[1] or 'hausdorff.lua'
print('testing '..testcase)
local function run(args) 
    return function(expected)
        local result = tonumber(
            io.popen('lua.exe '..testcase..' '..args):read'*a':match'%S+')
        print(args..' -> '..expected..' :: '..result)
        assert(result == expected,
            ("for input %q expected %s but got %s"):format(
                args, expected, result))
    end
end
run''(-1)
run'0'(-1)
run'0 1 0'(-1)
run'2 0 0 2'(-1)
run'0 1 2 3'(1)
run'0 3 3 0 0 0 0 3'(0)
run'1 0 0 1 0 0 1 3 1'(7)
run'1 0 0 0 0 3 0 0 0 0 2'(5)
run'0 1 1 3 1 3 2 1 1 3 0 3'(2)
run'2 2 2 1 2 0 3 1 3 1 0 3'(3)
run'1 3 0 2 0 2 2 1 0 3 2 1 1 2 2'(2)
run'1 0 1 1 2 0 1 2 3 1 0 0 0 1 2 0'(4)

... produce ...

testing hausdorff.lua
 -> -1 :: -1
0 -> -1 :: -1
0 1 0 -> -1 :: -1
2 0 0 2 -> -1 :: -1
0 1 2 3 -> 1 :: 1
0 3 3 0 0 0 0 3 -> 0 :: 0
1 0 0 1 0 0 1 3 1 -> 7 :: 7
1 0 0 0 0 3 0 0 0 0 2 -> 5 :: 5
0 1 1 3 1 3 2 1 1 3 0 3 -> 2 :: 2
2 2 2 1 2 0 3 1 3 1 0 3 -> 3 :: 3
1 3 0 2 0 2 2 1 0 3 2 1 1 2 2 -> 2 :: 2
1 0 1 1 2 0 1 2 3 1 0 0 0 1 2 0 -> 4 :: 4
thenumbernine
fuente
4

Pyth, 43 40 39 38 bytes

J+m0yQQLq3.|Fb?eS.e&Yfy:J-kT+hkT0JyJ_1

Mi algoritmo opera directamente en la cadena de entrada y nunca convierte estos números. Solo calcula una vez como máximo y nunca como mínimo.

Gracias a @isaacg por guardar un byte.

Pruébelo en línea: Pyth Compiler / Executor

Explicaciones:

Primero insertaré muchos ceros delante de la entrada.

          implicit: Q = input()
    yQ    powerset(Q)
  m0yQ    map each element of the powerset to 0 (creates 2^Q zeros, I said lots)
 +    Q   zeros + Q
J         assign to J

Luego defino una función auxiliar y, que indica si los índices de una lista (como la entrada) aparecen en los conjuntos A y BEg y([0, 1, 0, 0, 1, 1]) = False, pero y([0, 1, 0, 2]) = y([3]) = True.

Lq3.|Fb
L          define a function y(b), which returns _
   .|Fb       fold b by bitwise or
 q3            == 3

Luego, primero verifico si el resultado es -1.

?...yJ_1   print ... if numbers appear in both sets (`yJ`) else -1   

Ahora a las cosas interesantes:

  .e              J    map each pair k,Y in enumerate(J) to:
    &Y                   Y and ... (acts as 0 if Y == 0 else ...)
      f          0       find the first number T >= 0, where:
       y                    indices appear in both sets in the substring
        :J-kT+hkT           J[k-T:k+T+1]
eS                     sort and take last element (maximum)

Tenga en cuenta que siempre encontraré un número T, ya que sé que los índices aparecen en ambos conjuntos en la lista J. El número es máximo length(Q). Esta es también la razón para insertar los ceros. Si hay al menos length(Q)ceros insertados, k-Tsiempre es >= 0, lo cual es necesario para el corte de la lista. Entonces, ¿por qué inserto 2^length(Q)ceros en lugar de length(Q)ceros? En el caso de prueba []necesito al menos 1 cero, de lo contrario yJdevolverá un error.

Jakube
fuente
><Cabes el mismo que :Cba.
isaacg
Es bueno que los casos de prueba no incluyan una gran entrada ...
TLW
3

Mathematica, 88 bytes

Max[Min/@#,Min/@Thread@#,-1]/.∞->-1&@Outer[Abs[#-#2]&,p=Position;p[#,1|3],p[#,2|3],1]&
alephalpha
fuente
1
Muy buena respuesta. Para un hallazgo más general de la distancia de Hausdorff, se podría usar lo m=MaxValue;Max[m[RegionDistance[#[[1]],s],s\[Element]#[[2]]]/.m[__]->-1&/@{#,Reverse@c}]& que luego se puede aplicar a objetos multidimensionales como tal%@{Sphere[],Line[{{1,1,0},{3,3,3}}]}
Kelly Lowder
3

Haskell, 145 126 124 bytes

s t x=[e|(e,i)<-zip[0..]x,t i]
d#e=maximum[minimum[abs$i-j|j<-e]|i<-d]
[]%_= -1
_%[]= -1
d%e=max(d#e)$e#d
f x=s(>1)x%s odd x

Prueba de funcionamiento:

*Main> map f [[], [0], [0,1,0], [2,0,0,2], [0,1,2,3],
              [0,3,3,0,0,0,0,3], [1,0,0,1,0,0,1,3,1],
              [1,0,0,0,0,3,0,0,0,0,2], [0,1,1,3,1,3,2,1,1,3,0,3],
              [2,2,2,1,2,0,3,1,3,1,0,3],
              [1,3,0,2,0,2,2,1,0,3,2,1,1,2,2],
              [1,0,1,1,2,0,1,2,3,1,0,0,0,1,2,0]]

[-1,-1,-1,-1,1,0,7,5,2,3,2,4]

sfiltra los números naturales según un predicado ty la lista de entrada x. #calcula la distancia máxima de sus parámetros dy e. %captura conjuntos vacíos A o B o toma el máximo final de d#ey e#d. fes la función principal que llama %con el conjunto A y B.

Editar: @Zgarb encontró muchos bytes para guardar; @ ali0sha otro 2. ¡Gracias!

nimi
fuente
El mod 2parece innecesario. También puede beneficiarse de no definir ay bexplícitamente.
Zgarb
puedes guardar 2 bytes con []%_= -1- pero superaste mi intento en esto :)
alexander-brett
3

Perl, 56 55

Se agregó +2 para -lp.

La lista de entrada debe darse en stdin sin espacios, por ejemplo:

echo 1011201231000120 | perl -lp hausdorf.pl

hausdorf.pl:

s%%$z=$_&=$p|=$'|P.$p;$q+=!!y/12/3/%eg;$_=$z=~3?$q:-1

Para admitir espacios entre los elementos de la lista de entrada, simplemente divida el final $qentre 2 por un costo de 2 trazos

Ton Hospel
fuente
2

Pitón 2, 124

Esto definitivamente se siente subóptimo. Oh bien.

lambda a,E=enumerate:-min([1]+[~l*(n<3)for i,n in E(a)for l,_ in E(a)if{0}|set(n*a+n/3*[5])>{0,n}>=set(a[max(i-l,0):i-~l])])
Feersum
fuente
1

APL (49)

{(⊂⍬)∊∆←(↓2 2⊤⍵)/¨⊂⍳⍴⍵:¯1⋄⌈/{⌈/⌊/⍵}¨(+,⍉¨)|∘.-/∆}

Casos de prueba:

      ({(⊂⍬)∊∆←(↓2 2⊤⍵)/¨⊂⍳⍴⍵:¯1⋄⌈/{⌈/⌊/⍵}¨(+,⍉¨)|∘.-/∆} ¨ testcases) ,⍨ '→',⍨ ↑ ⍕¨testcases
                               → ¯1
0                              → ¯1
0 1 0                          → ¯1
2 0 0 2                        → ¯1
0 1 2 3                        →  1
0 3 3 0 0 0 0 3                →  0
1 0 0 1 0 0 1 3 1              →  7
1 0 0 0 0 3 0 0 0 0 2          →  5
0 1 1 3 1 3 2 1 1 3 0 3        →  2
2 2 2 1 2 0 3 1 3 1 0 3        →  3
1 3 0 2 0 2 2 1 0 3 2 1 1 2 2  →  2
1 0 1 1 2 0 1 2 3 1 0 0 0 1 2 0→  4

Explicación:

  • ⍳⍴⍵: obtener una lista de números del 1 a la longitud de la lista de entrada
  • ↓2 2⊤⍵: para cada valor en la lista de entrada, obtenga el primer byte y el segundo byte
  • ∆←(... )/⊂⍳⍴⍵: para ambas listas de bytes, seleccione los valores correspondientes de ⍳⍴⍵. Guárdelos en .
  • (⊂⍬)∊∆... :¯1: si esta lista contiene la lista vacía, regrese -1. De otra manera:

  • |∘.-/∆: obtiene la diferencia absoluta entre cada par de valores, dando una matriz

  • (+,⍉¨): obtener una copia rotada y no rotada de esa matriz
  • {⌈/⌊/⍵}: para ambas matrices, obtenga el máximo de los mínimos de las filas
  • ⌈/: luego obtenga el máximo de eso
marinus
fuente
@Optimizer: de alguna manera logré copiar la salida de prueba de una versión anterior que tenía un error. El código en sí era correcto y aún lo es. Si no me crees, prueba aquí . (Tenga en cuenta que debe ingresar una lista de un elemento como ,X, para distinguirla del escalar X.)
marinus
Ah, ya veo. perezoso de mi parte no ir a un compilador en línea y probar ..
Optimizer
1

Perl, 189 176 157B

Ahora con un 500% más de estado.

use List::Util qw'max min';@I=<>;sub f{$n=($n%2)+1;map{$I[$_]&$n?$_:()}0..$#I}sub i{@t=f;max map{$b=$_;min map{abs$_-$b}@t}f}$r=max i,i;print defined$r?$r:-1

Legible:

use List::Util qw'max min';
@I=<>;
sub f {
    $n = ($n%2) + 1;
    map { $I[$_] & $n ? $_ : () } 0..$#I
}
sub i {
    @t = f;
    max map {
        $b = $_;
        min map { abs $_ - $b } @t
    } f
}
$r = max i,i;
print defined $r ? $r : -1

Ejemplo de uso:

entrada

0
1
2
3

perl golf.pl < input

alexander-brett
fuente
0

Clojure, 167 bytes

#(let[P apply F(fn[I](filter(fn[i](I(% i)))(range(count %))))A(F #{1 3})B(F #{2 3})d(fn[X Y](P min(for[x X](P max(for[y Y](P -(sort[x y])))))))](-(min(d A B)(d B A))))

Debería haber un camino más corto ... ¿Hay?

NikoNyrh
fuente