Berechnen Sie die Hausdorff-Distanz

21

Einführung

Die Hausdorff-Distanz misst die Differenz zwischen zwei Teilmengen eines metrischen Raums. Intuitiv ist ein metrischer Raum nur ein Teil mit einer eingebauten Distanzfunktion. In dieser Herausforderung werden wir natürliche Zahlen mit der gewöhnlichen Distanz verwenden d(a, b) := abs(a - b). Der Hausdorff-Abstand zwischen zwei nicht leeren endlichen Mengen Aund Bist gegeben durch

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

in Python-ähnlicher Notation. Die Hausdorff-Distanz kann berechnet werden, indem man das Element findet, Afür das die Distanz zum nächsten Element von Bmaximal ist, und das Element, Bfür das die Distanz zum nächsten Element von Amaximal ist, und dann das Maximum dieser Distanzen nimmt. Mit anderen Worten, wenn die Hausdorff-Distanz ist d, dann ist jedes Element von Ainnerhalb einer Distanz dvon einem Element von Bund umgekehrt.

Eingang

Ihre Eingabe ist eine einzelne Liste von ganzen Zahlen. Es enthält nur die Elemente 0,1,2,3, die angeben, ob der angegebene Index der Liste weder Anoch B, nur Anoch Boder beide Aund enthält B. Zum Beispiel [0,1,1,0,2,3]bedeutet die Eingabe , dass A = {1,2,5}und B = {4,5}, wenn wir eine 0-basierte Indizierung verwenden (was keinen Unterschied macht, da unsere Metriken übersetzungsinvariant sind).

Ausgabe

Ihre Ausgabe ist die Hausdorff-Distanz zwischen Aund B; im obigen Beispiel ist es 3. Wenn einer der Sätze leer ist, ist die Entfernung nicht definiert, und Sie kehren zurück -1.

Regeln

Sie können ein vollständiges Programm oder eine Funktion schreiben. Die niedrigste Byteanzahl gewinnt, und Standardlücken sind nicht zulässig.

Testfälle

[] -> -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
quelle
In Ihrer Gleichung glaube ich, dass es viel zu lang ist, als max(max(min(d(a, b) for b in B) for a in A))dass es ausreichen sollte. Dies liegt daran, dass d(a,b)der absolute Wert zurückgegeben wird und daher beide max-Funktionen jedes Mal dieselbe Zahl zurückgeben.
Nathan Merrill
6
@ NathanMerrill Es kann sein, dass jedes Element von Asehr nahe an einem von ist B, aber es gibt Elemente von Bsehr weit entfernt A(zum Beispiel, wenn Aes eine Teilmenge von ist B). In diesem Fall ist die Kurzformel falsch.
Zgarb

Antworten:

7

CJam, 53 52 46 38 37 Bytes

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

Übernimmt die Eingabe in STDIN als Array im CJam-Stil:

[0 1 2 3]

Hier ist eine Testumgebung, die alle Testfälle in dieses Format konvertiert und den Code darauf ausführt. Obwohl sich die Ergebnisse im Eingabefeld befinden, werden sie vom Code nicht verwendet (entfernen Sie sie, wenn Sie mir nicht vertrauen :)).

Erläuterung

Zuerst analysieren wir die Eingabe, um die beiden Mengen A und B zu erhalten:

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.";

Und jetzt finden wir die absoluten Unterschiede und wählen das Maximum der Minuten:

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.";

Beachten Sie, dass wir das leere Array, das sich aus der Initiale 0am Ende des Stapels ergibt, die ganze Zeit beibehalten haben, aber leere Arrays nichts zur Ausgabe beitragen.

Martin Ender
quelle
5

CJam, 57 56 52 Bytes

Ich denke, das kann ein bisschen golfen werden, aber hier geht:

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

Die Eingabe erfolgt wie in einer CJam-Liste, z.

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

5

Wie es funktioniert :

Der Code ist in zwei Teile geteilt:

Analysieren der Eingabe in die Listen Aund 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";

Durchführen der erforderlichen Aktionen für die beiden Paare von Aund 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";

Probieren Sie es hier online aus

Optimierer
quelle
5

Lua, 235 Bytes

Auf keinen Fall ein Gewinner, aber zumindest eine lustige Herausforderung.

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

Die Eingabe funktioniert folgendermaßen:

lua hausdorff.lua <space-separated-sequence>

... und hier ist ein Testskript:

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)

... produziert ...

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
quelle
4

Pyth, 43 40 39 38 Bytes

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

Mein Algorithmus arbeitet direkt mit der Eingabezeichenfolge und konvertiert diese Zahl nie. Es wird nur einmal maximal und niemals minimal berechnet.

Vielen Dank an @isaacg für das Speichern eines Bytes.

Probieren Sie es online aus: Pyth Compiler / Executor

Erklärungen:

Zuerst füge ich viele Nullen vor der Eingabe ein.

          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

Dann definiere ich eine yHilfsfunktion y([0, 1, 0, 0, 1, 1]) = False, die angibt , ob die Indizes einer Liste (wie die Eingabe) in beiden Mengen A und BEg vorkommen , aber 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

Danach überprüfe ich zuerst, ob das Ergebnis ist -1.

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

Nun zu den interessanten Sachen:

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

Beachten Sie, dass ich immer eine Nummer finden werde T, da ich bereits weiß, dass Indizes in beiden Mengen in der Liste J vorkommen. Die Anzahl ist maximal length(Q). Dies ist auch der Grund für das Einfügen der Nullen. Wenn mindestens length(Q)Nullen eingefügt sind, k-Tist immer >= 0, was für das Aufteilen der Liste notwendig ist. Warum füge ich also 2^length(Q)Nullen anstelle von length(Q)Nullen ein? Im Testfall []brauche ich mindestens 1 Null, sonst yJwird ein Fehler zurückgegeben.

Jakube
quelle
><Cabist das gleiche wie :Cba.
Isaacg
Es ist eine gute Sache, dass die Testfälle keinen großen Input beinhalten ...
TLW
3

Mathematica, 88 Bytes

Max[Min/@#,Min/@Thread@#,-1]/.∞->-1&@Outer[Abs[#-#2]&,p=Position;p[#,1|3],p[#,2|3],1]&
Alephalpha
quelle
1
Sehr nette Antwort. Für eine allgemeinere Ermittlung der Hausdorff-Distanz könnte man verwenden, m=MaxValue;Max[m[RegionDistance[#[[1]],s],s\[Element]#[[2]]]/.m[__]->-1&/@{#,Reverse@c}]& was dann auf mehrdimensionale Objekte wie diese angewendet werden kann%@{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

Testlauf:

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

sfiltert die natürlichen Zahlen nach einem Prädikat tund der Eingabeliste x. #berechnet den maximalen Abstand seiner Parameter dund e. %fängt leere Mengen A oder B oder nimmt das letzte Maximum von d#eund e#d. fist die Hauptfunktion, die %mit Set A und B aufruft .

Edit: @Zgarb hat viele Bytes zum Speichern gefunden; @ ali0sha noch 2. Danke!

nimi
quelle
Das mod 2scheint unnötig. Sie können auch von nicht definieren profitieren aund bexplizit.
Zgarb
Sie können 2 Bytes mit speichern []%_= -1- aber Sie schlagen meinen Versuch Hände nach unten auf diese :)
Alexander-Brett
3

Perl, 56 55

+2 für hinzugefügt -lp.

Die Eingabeliste sollte auf stdin ohne Leerzeichen angegeben werden, zB:

echo 1011201231000120 | perl -lp hausdorf.pl

hausdorf.pl:

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

Um Leerzeichen zwischen den Elementen der Eingabeliste zu unterstützen, teilen Sie das Finale einfach $qdurch 2, was 2 Striche kostet

Tonne Hospel
quelle
2

Python 2, 124

Das fühlt sich definitiv suboptimal an. Naja.

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
quelle
1

APL (49)

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

Testfälle:

      ({(⊂⍬)∊∆←(↓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

Erläuterung:

  • ⍳⍴⍵: Ermittelt eine Liste mit Zahlen von 1 bis zur Länge der Eingabeliste
  • ↓2 2⊤⍵: Für jeden Wert in der Eingabeliste das erste und das zweite Byte abrufen
  • ∆←(... )/⊂⍳⍴⍵: Wählen Sie für beide Bytelisten die entsprechenden Werte aus ⍳⍴⍵. Bewahren Sie diese in .
  • (⊂⍬)∊∆... :¯1: Wenn diese Liste die leere Liste enthält, kehren Sie zurück -1. Andernfalls:

  • |∘.-/∆: Ermittelt den absoluten Unterschied zwischen jedem Wertepaar und liefert eine Matrix

  • (+,⍉¨): Erhalte eine gedrehte und eine nicht gedrehte Kopie dieser Matrix
  • {⌈/⌊/⍵}: Ermitteln Sie für beide Matrizen das Maximum des Minimums der Zeilen
  • ⌈/: dann holen Sie das Maximum davon
Marinus
quelle
@Optimizer: Ich habe es irgendwie geschafft, die Testausgabe einer früheren Version zu kopieren, die einen Fehler hatte. Der Code selbst war und ist korrekt. Wenn Sie mir nicht glauben, versuchen Sie es hier . (Beachten Sie, dass Sie eine einelementige Liste eingeben müssen , wie ,Xes aus dem Skalare zu unterscheiden X.)
marinus
Ah ich sehe. faul von mir, nicht zu einem Online-Compiler zu gehen und zu testen ..
Optimierer
1

Perl, 189 176 157B

Jetzt mit 500% mehr Zustand.

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

Lesbar:

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

Anwendungsbeispiel:

Eingang

0
1
2
3

perl golf.pl < input

Alexander-Brett
quelle
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))))

Es sollte einen kürzeren Weg geben ... Gibt es da?

NikoNyrh
quelle