Golfe: Quantos quadrados de unidade de comprimento em uma lista de coordenadas 2D?

8

Dada uma lista de coordenadas 2d (x, y), determine quantos quadrados de unidade (comprimento da aresta 1 unidade) podem ser formados usando as coordenadas.

  • A entrada será uma matriz de 0 ou mais pares de coordenadas:
    por exemplo, em JavaScript:numofsq([[0,0], [1,0], [1,1], [0,1]])
  • Nenhuma coordenada duplicada na entrada
  • A ordem de entrada será aleatória (coordenadas 2d aleatórias).
  • Formulário de coordenadas: [coordenada x, coordenada y] (duh)
  • Ordem das coordenadas: [0,0], [1,0], [1,1], [0,1] e [0,0], [0,1], [1,1], [1,0 ] denota o mesmo quadrado da unidade (a ser contado apenas uma vez)
  • As coordenadas podem ser números inteiros negativos ou positivos (duh)
  • A função retornará apenas o número de quadrados de unidade possível, 0 ou mais.

Casos de teste:

Input Coordinates Pairs                                               Expected Output
[0,0], [0,1], [1,1], [1,0], [0,2], [1,2]                              2
[0,0], [0,1], [1,1], [1,0], [0,2], [1,2], [2,2], [2,1]                3
[0,0], [0,1], [1,1], [1,0], [0,2], [1,2], [2,2], [2,1], [2,0]         4
[0,0], [0,1], [1,1], [1,0], [0,2], [1,2], [2,2], [2,1], [2,0], [9,9]  4

Alerta de spoiler: solução aqui em diante [JS]

Abordagem sem força de golfe, rápida e suja, com força bruta (incluída para fornecer algumas instruções).

//cartesian distance function
function dist(a, b) {
    if (b === undefined) {
        b = [0, 0];
    }
    return Math.sqrt((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]));
}

//accepts 4 coordinate pairs and checks if they form a unit square
//this could be optimized by matching x,y coordinates of the 4 coordinates
function isUnitSquare(a) {
    var r = a.slice(),
        d = [],
        c = [],
        i,
        j = 0,
        counter = 0;

    for (i = 1; i < 4; i++) {
        if (dist(a[0], a[i]) === 1) {
            d.push(a[i]);
            r[i] = undefined;
            counter++;
        }
    }
    r[0] = undefined;
    c = d.concat(r.filter(function(a) {return undefined !== a}));

    if (dist(c[0], c[1]) === 1) {j++};
    if (dist(c[1], c[2]) === 1) {j++};
    if (dist(c[2], c[0]) === 1) {j++};
    return counter === 2 && j === 2;
}

//a powerset function (from rosetta code)
//however, we will need only "sets of 4 coordinates"
//and not all possible length combinations (sets of 3 coords or
//sets of 5 coords not required). Also, order doesn't matter.
function powerset(ary) {
    var ps = [[]];
    for (var i=0; i < ary.length; i++) {
        for (var j = 0, len = ps.length; j < len; j++) {
            ps.push(ps[j].concat(ary[i]));
        }
    }
    return ps;
}

//so to capture only sets of 4 coordinates, we do
var res = powerset([[0,0], [0,1], [1,1], [1,0], [0,2], [1,2], [2,2], [2,1], [2,0]])
          .filter(function (a) {return a.length === 8;});

//and a little bit of hoopla to have a nice set of sets of 4 coordinates.
//(Dizzy yet? Wait for the generalized, 3D, cube of any edge length version ;))
var squareQuads = res.map(function(ary) {
    return ary.join().match(/\d\,\d/g)
       .map(function(e) {
           return [+e.split(',')[0], +e.split(',')[1]];
        });
});

//Finally, the last bit
var howManyUnitSquares = 0;
squareQuads.map(function(quad) {
    if (isUnitSquare(quad)) {
        howManyUnitSquares++;
    }
});

console.log(howManyUnitSquares);

//Cleaning up and putting those in-line stuff into a function
function howManySquares(r) { //r = [[x,y], [x,y], [x,y], [x,y], ......];
    var res = powerset(r)
          .filter(function (a) {return a.length === 8;});
    var squareQuads = res.map(function(ary) {
        return ary.join().match(/\d\,\d/g)
               .map(function(e) {
                   return [+e.split(',')[0], +e.split(',')[1]];
                });
    });

    var answer = 0;
    squareQuads.map(function(quad) {
        if (isUnitSquare(quad)) {
            answer++;
        }
    });

    return answer;
}

fonte
1
é [-1,0],[0,-1],[1,0],[0,1]um quadrado?
Johannes Kuhn
@JohannesKuhn Não, seus comprimentos de aresta não são unidade, mas sqrt (2), portanto não são quadrados unitários.
Mas [-2,0],[0,-2],[2,0],[0,2]tem um comprimento de borda de 2. Quadrado?
Johannes Kuhn
2
@JohannesKuhn Square? Sim. UNIT Square? #

Respostas:

5

APL (Dyalog), 30

+/{(2-/⍵)≡2-/,⍳2 2}¨,∘.,⍨⍣2⊂¨⎕

Bem, na maioria dos casos, a legibilidade e a contagem de caracteres são proporcionais.


Saída de amostra

⎕:
      (0 0)(0 1)(1 0)(1 1)(0 2)(1 2)(2 2)(2 1)(2 0)
4

Explicação

Portanto, 4 pontos forma um quadrado de unidade se, e somente se, suas posições relativas forem (1,1), (1,2), (2,1), (2,2),
{(2-/⍵)≡2-/,⍳2 2}é uma função que retorna 1 ou 0 (true / false) dado um conjunto de 4 pontos como entrada com base em se eles estão em posições relativas e classificados na ordem (1,1), (1,2), (2,1), (2,2):
⍳2 2Gere um 2 × 2 matriz dos pontos (1,1), (1,2), (2,1), (2,2)
,Desvendar essa matriz para uma matriz de pontos A
2-/subtração pareada reduz: (1,1) - ( 1,2); (1,2) - (2,1); (2,1) - (2,2), que fornece a matriz [(0, -1), (- 1,1), (0, -1)] A
(2-/⍵)subtração em pares reduz a entrada
Verifique se as duas matrizes iguais

O programa principal
Pega entrada e avalia. Coisas como são (0 0)(0 1)(1 0)(1 1)avaliadas para uma matriz aninhada (equivalente a [[0,0],[0,1],[1,0],[1,1]]em JS).
⊂¨Para cada ponto ( ¨), coloque-a em um escalar ( ) ( [[[0,0]],[[0,1]],[[1,0]],[[1,1]]]).
∘.,⍨⍣2Para cada par de elementos da matriz, concatene-os para formar uma nova matriz. ( [ [[0,0],[0,0]],[[0,0],[0,1]]...) Repita uma vez. Isso fornece todos os conjuntos de 4 pontos que podem ser feitos usando os pontos fornecidos. Em alguns desses conjuntos, o mesmo ponto seria usado várias vezes, mas estes não seriam quadrados de unidades, portanto, não é necessário desperdiçar as teclas pressionadas para removê-las. ( ,concatena 2 matrizes, ∘.significa "faça isso para cada par de elementos", significa "use o operando direito como operandos esquerdo e direito" e ⍣2significa "faça isso duas vezes")
,Como a operação anterior daria uma matriz 4-dimensional (observe que matrizes aninhadas e matrizes multidimensionais são coisas diferentes no APL), temos que desvendá-lo para obter uma matriz de conjuntos (de 4 pontos).
¨Para cada um dos conjuntos,
{...}execute a função mencionada acima. O resultado seria uma matriz de 0s e 1s indicando se o conjunto é um quadrado de unidade. Observe que, como a função também verifica a ordem, as duplicatas são eliminadas.
+/Finalmente, some a matriz resultante para obter a contagem.

TwiNight
fonte
3

Mathematica 65 caracteres

f@d_ := Length@Select[Subsets[d, {4}], Sort[g@#] == {1, 1, 1, 1, √2, √2} &]

Testes

f[{{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 2}, {1, 2}}]

2

f[{{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 2}, {1, 2}, {2, 2}, {2, 1}}]

3

f[{{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 2}, {1, 2}, {2, 2}, {2, 1}, {2,0}}]

4

f[{{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 2}, {1, 2}, {2, 2}, {2, 1}, {2,0}, {9, 9}}]

4

Explicação

Gera todos os subconjuntos de 4 pontos e verifica todas as distâncias entre pontos. As distâncias entre pontos classificadas para um quadrado unitário são: `{1, 1, 1, 1, √2, √2}.

Length então conta o número de quadrados da unidade.

DavidC
fonte
Qual é a definição de g?
amigos estão dizendo sobre alephalpha
1
Aqui está a minha solução mais curta:f=Count[#-#[[1]]&/@(Sort/@#~Subsets~{4}.{1,I}),{0,I,1,1+I}]&
alephalpha
1

Ruby, 164 161 153 147 caracteres

f=->a{a.combination(4).map(&:sort).uniq.count{|x|[x[1][0]==l=x[0][0],x[3][0]==m=x[2][0],x[2][1]==n=x[0][1],x[3][1]==o=x[1][1],m-l==1,o-n==1].all?}}

Na verdade, é muito legível, exceto pela parte que testa se é um quadrado de unidade ou não.

Provavelmente muitas melhorias possíveis, tentando encontrá-las agora.

Amostras (todas elas funcionam):

puts f[[[0,0], [0,1], [1,1], [1,0], [0,2], [1,2]]]                             #--> 2
puts f[[[0,0], [0,1], [1,1], [1,0], [0,2], [1,2], [2,2], [2,1]]]               #--> 3
puts f[[[0,0], [0,1], [1,1], [1,0], [0,2], [1,2], [2,2], [2,1], [2,0]]]        #--> 4
puts f[[[0,0], [0,1], [1,1], [1,0], [0,2], [1,2], [2,2], [2,1], [2,0], [9,9]]] #--> 4

Talvez eu consiga encontrar um truque transpose, mas estou tentando há um tempo e não consigo. Aqui está o que ele faz:

irb(main):001:0> a = [[5, 10], [5, 11], [6, 10], [6, 11]]
=> [[5, 10], [5, 11], [6, 10], [6, 11]]
irb(main):002:0> a.transpose
=> [[5, 5, 6, 6], [10, 11, 10, 11]]
Maçaneta da porta
fonte
Concordo com a parte legível - Como alguém que nunca programou em Ruby, ainda posso entender claramente as etapas. Pena que o JS não possui algumas combinações integradas. #
0

Python, 61 caracteres

f=lambda l:sum(1for x,y in l if{(x+1,y),(x,y+1),(x+1,y+1)}<l)

Amostra:

>>> f({(0,0), (0,1), (1,1), (1,0), (0,2), (1,2)})
2
>>> f({(0,0), (0,1), (1,1), (1,0), (0,2), (1,2), (2,2), (2,1)})
3
>>> f({(0,0), (0,1), (1,1), (1,0), (0,2), (1,2), (2,2), (2,1), (2,0)})
4
>>> f({(0,0), (0,1), (1,1), (1,0), (0,2), (1,2), (2,2), (2,1), (2,0), (9,9)})
4
Daniel Lubarov
fonte
0

Mathematica, 56 caracteres

f=Count[#-#[[1]]&/@Subsets[{}⋃#.{1,I},{4}],{0,I,1,1+I}]&
alefalpha
fonte