Calculer la distance de Hausdorff

21

introduction

La distance de Hausdorff mesure la différence entre deux sous-ensembles d'un espace métrique. Intuitivement, un espace métrique n'est qu'un ensemble avec une fonction de distance intégrée; dans ce défi, nous utiliserons des nombres naturels avec la distance ordinaire d(a, b) := abs(a - b). La distance de Hausdorff entre deux ensembles finis non vides Aet Best donnée par

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 notation de type Python. La distance de Hausdorff peut être calculée en trouvant l'élément Adont la distance à l'élément le plus proche de Best maximale, et l'élément Bdont la distance à l'élément le plus proche Aest maximale, puis en prenant le maximum de ces distances. En d'autres termes, si la distance de Hausdorff est d, alors chaque élément de se Atrouve à distance dd'un élément de B, et vice versa.

Contribution

Votre entrée est une seule liste d'entiers. Il ne contient que les éléments 0,1,2,3, ce qui signifie si l'index donné de la liste est un élément ni Ani B, seulement A, seulement B, ou les deux Aet B. Par exemple, l'entrée [0,1,1,0,2,3]signifie cela A = {1,2,5}et B = {4,5}, si nous utilisons une indexation basée sur 0 (ce qui ne fait aucune différence, car nos métriques sont invariantes par rapport à la traduction).

Production

Votre sortie est la distance de Hausdorff entre Aet B; dans l'exemple ci-dessus, c'est le cas 3. Si l'un des ensembles est vide, la distance n'est pas définie et vous reviendrez -1.

Règles

Vous pouvez écrire un programme complet ou une fonction. Le nombre d'octets le plus bas l'emporte et les failles standard sont interdites.

Cas de test

[] -> -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
la source
Dans votre équation, je pense que c'est beaucoup trop long, ce qui max(max(min(d(a, b) for b in B) for a in A))devrait être suffisant. En effet, d(a,b)renvoie la valeur absolue, et donc les deux fonctions max renverront le même nombre à chaque fois.
Nathan Merrill
6
@NathanMerrill Il se peut que chaque élément de Asoit très proche de l'un B, mais il y a des éléments de Btrès loin de A(par exemple, si Aest un sous-ensemble de B). Dans ce cas, la formule courte est incorrecte.
Zgarb

Réponses:

7

CJam, 53 52 46 38 37 octets

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

Prend l'entrée sur STDIN en tant que tableau de style CJam:

[0 1 2 3]

Voici un harnais de test qui convertit tous les cas de test dans ce format et exécute le code sur eux. Bien que les résultats soient dans le champ de saisie, ils ne sont pas utilisés par le code (supprimez-les si vous ne me faites pas confiance :)).

Explication

Tout d'abord, nous analysons l'entrée pour obtenir les deux ensembles A et 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.";

Et maintenant, nous trouvons les différences absolues et sélectionnons le maximum des minutes:

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

Notez que nous avons toujours gardé le tableau vide résultant de l'initiale 0au bas de la pile, mais les tableaux vides ne contribuent en rien à la sortie.

Martin Ender
la source
5

CJam, 57 56 52 octets

Je pense que cela peut être un peu joué au golf, mais voici:

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

L'entrée va comme une liste de style CJam, par exemple.

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

5

Comment ça marche :

Le code est divisé en deux parties:

Analyser l'entrée dans les listes Aet 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";

Exécution des actions requises sur les deux paires de Aet 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";

Essayez-le en ligne ici

Optimiseur
la source
5

Lua, 235 octets

Certainement pas un gagnant, mais au moins un défi amusant.

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

L'entrée fonctionne comme ceci:

lua hausdorff.lua <space-separated-sequence>

... et voici un script de test:

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)

... produit ...

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
l'umbumbine
la source
4

Pyth, 43 40 39 38 octets

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

Mon algorithme opère directement sur la chaîne d'entrée et ne convertit jamais ces nombres. Il ne calcule qu'une fois un maximum et jamais un minimum.

Merci à @isaacg d'avoir enregistré un octet.

Essayez-le en ligne: Pyth Compiler / Executor

Explications:

Je vais d'abord insérer beaucoup de zéros devant l'entrée.

          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

Ensuite, je définis une fonction d'aide y, qui indique si les indices d'une liste (comme celle d'entrée) apparaissent dans les deux ensembles A et BEg y([0, 1, 0, 0, 1, 1]) = False, mais 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

Ensuite, je vérifie d'abord si le résultat est -1.

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

Passons maintenant aux choses intéressantes:

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

Remarquez que je trouverai toujours un nombre T, car je sais déjà que les indices apparaissent dans les deux ensembles de la liste J. Le nombre est maximal length(Q). C'est aussi la raison de l'insertion des zéros. S'il y a au moins des length(Q)zéros insérés, k-Tc'est toujours >= 0, ce qui est nécessaire pour le découpage de la liste. Alors, pourquoi dois-je insérer des 2^length(Q)zéros au lieu de length(Q)zéros? Dans le cas de test, []j'ai besoin d'au moins 1 zéro, sinon yJje retournerai une erreur.

Jakube
la source
><Cabest le même que :Cba.
isaacg
C'est une bonne chose que les cas de test n'incluent pas une grande entrée ...
TLW
3

Mathematica, 88 octets

Max[Min/@#,Min/@Thread@#,-1]/.∞->-1&@Outer[Abs[#-#2]&,p=Position;p[#,1|3],p[#,2|3],1]&
alephalpha
la source
1
Très belle réponse. Pour une découverte plus générale de la distance de Hausdorff, on pourrait utiliser m=MaxValue;Max[m[RegionDistance[#[[1]],s],s\[Element]#[[2]]]/.m[__]->-1&/@{#,Reverse@c}]& ce qui peut ensuite être appliqué à des objets multidimensionnels tels que%@{Sphere[],Line[{{1,1,0},{3,3,3}}]}
Kelly Lowder
3

Haskell, 145 126 124 octets

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

Essai:

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

sfiltre les nombres naturels en fonction d'un prédicat tet de la liste d'entrée x. #calcule la distance maximale de ses paramètres det e. %attrape les ensembles vides A ou B ou prend le maximum final de d#eet e#d. fest la fonction principale qui appelle %avec l'ensemble A et B.

Edit: @Zgarb a trouvé beaucoup d'octets à enregistrer; @ ali0sha un autre 2. Merci!

nimi
la source
Le mod 2semble inutile. Vous pouvez également bénéficier de ne pas définir aet bexplicitement.
Zgarb
vous pouvez économiser 2 octets avec []%_= -1- mais vous avez battu ma tentative haut la main :)
alexander-brett
3

Perl, 56 55

Ajout de +2 pour -lp.

La liste d'entrée doit être donnée sur stdin sans espaces, par exemple:

echo 1011201231000120 | perl -lp hausdorf.pl

hausdorf.pl:

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

Pour prendre en charge les espaces entre les éléments de la liste d'entrée, il suffit de diviser la finale $qpar 2 pour un coût de 2 coups

Ton Hospel
la source
2

Python 2, 124

Cela semble définitivement sous-optimal. Tant pis.

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
la source
1

APL (49)

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

Testcases:

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

Explication:

  • ⍳⍴⍵: obtenir une liste de nombres de 1 à la longueur de la liste d'entrée
  • ↓2 2⊤⍵: pour chaque valeur de la liste d'entrée, obtenez le premier octet et le deuxième octet
  • ∆←(... )/⊂⍳⍴⍵: pour les deux listes d'octets, sélectionnez les valeurs correspondantes dans ⍳⍴⍵. Conservez-les dans .
  • (⊂⍬)∊∆... :¯1: si cette liste contient la liste vide, retournez -1. Autrement:

  • |∘.-/∆: obtenir la différence absolue entre chaque paire de valeurs, donnant une matrice

  • (+,⍉¨): obtenir une copie pivotée et non pivotée de cette matrice
  • {⌈/⌊/⍵}: pour les deux matrices, obtenez le maximum des minimums des lignes
  • ⌈/: alors obtenez le maximum de ça
marinus
la source
@Optimizer: J'ai réussi à copier la sortie de test d'une version antérieure qui avait un bug. Le code lui-même était correct et l'est toujours. Si vous ne me croyez pas, essayez ici . (Notez que vous devez entrer une liste à un élément car ,X, pour la distinguer du scalaire X.)
marinus
Ah, je vois. paresseux de ne pas aller à un compilateur en ligne et tester ..
Optimizer
1

Perl, 189 176 157B

Maintenant avec 500% d'état en plus.

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

Lisible:

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

Exemple d'utilisation:

contribution

0
1
2
3

perl golf.pl < input

alexander-brett
la source
0

Clojure, 167 octets

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

Il devrait y avoir un moyen plus court ... Y a-t-il?

NikoNyrh
la source