Bestimmen Sie, ob 4 Punkte ein Quadrat bilden

29

Schreiben Sie eine Funktion, die 4 Punkte in der Ebene als Eingabe verwendet und true zurückgibt, wenn die 4 Punkte ein Quadrat bilden. Die Punkte haben ganzzahlige Koordinaten mit absoluten Werten <1000.

Sie können eine beliebige sinnvolle Darstellung der 4 Punkte als Eingabe verwenden. Die Punkte werden nicht in einer bestimmten Reihenfolge vergeben.

Kürzester Code gewinnt.

Beispielquadrate:

(0,0),(0,1),(1,1),(1,0)    # standard square
(0,0),(2,1),(3,-1),(1,-2)  # non-axis-aligned square
(0,0),(1,1),(0,1),(1,0)    # different order

Beispiel Nichtquadrate:

(0,0),(0,2),(3,2),(3,0)  # rectangle
(0,0),(3,4),(8,4),(5,0)  # rhombus
(0,0),(0,0),(1,1),(0,0)  # only 2 distinct points
(0,0),(0,0),(1,0),(0,1)  # only 3 distinct points

Sie können für das entartete Quadrat entweder true oder false zurückgeben (0,0),(0,0),(0,0),(0,0)

Keith Randall
quelle
Wir sprechen hier über 3D-Punkte, oder?
Gnibbler
3
@knibbler der "on the plane" teil der frage macht 3d punkte unwahrscheinlich.
JB
Sind die Punkte in der richtigen Reihenfolge angegeben?
JB
@JB, ich dachte, dass es bedeutete, dass sich die Punkte auf einer Ebene befanden, aber ich stellte mir aus irgendeinem Grund eine Ebene im 3D-Raum vor :)
gnibbler
1
@eBusiness: -1 dass Sie 11 Stimmen abgegeben haben: 7 von ihnen sind down.
Eelvex

Antworten:

12

Python 176 90 79 Bytes

def S(A):c=sum(A)/4.0;return set(A)==set((A[0]-c)\*1j\*\*i+c for i in range(4))

Die Funktion S verwendet eine Liste komplexer Zahlen als Eingabe (A). Wenn wir sowohl die Mitte als auch eine Ecke eines Quadrats kennen, können wir das Quadrat rekonstruieren, indem wir die Ecke um 90, 180 und 270 Grad um den Mittelpunkt drehen (c). Auf der komplexen Ebene erfolgt die Drehung um 90 Grad um den Ursprung, indem der Punkt mit i multipliziert wird . Wenn unsere ursprüngliche Form und das rekonstruierte Quadrat die gleichen Punkte haben, muss es ein Quadrat gewesen sein.

Papierpferd
quelle
Ein paar Optimierungen: 1) benutze "S" anstelle von "is_square" 2) setze alles in eine Zeile mit; 3) iteriere direkt über die 4 Richtungen "für i in (1,1j, -1, -1j)" 4) brauche nicht [] in set argument.
Keith Randall
Danke Keith. (Ich habe (3)
weggelassen,
2
@ Keith Randall - Warum wurde dies akzeptiert, wenn JB eine viel kürzere Lösung hat?
aaaaaaaaaaa
1
Zwei Gründe. Erstens würde J immer gewinnen. Also normalisiere ich gerne ein bisschen nach Sprache. Außerdem gefällt mir diese Antwort besser, weil sie nicht unter demselben Problem leidet wie die Nur-Distanz-Antworten, bei denen andere Zahlen (zugegebenermaßen nur irrationale) falsch positive Ergebnisse liefern.
Keith Randall
5
@ Keith Randall - Zitate aus der Frage: "Die Punkte haben ganzzahlige Koordinaten" "Kürzester Code gewinnt." Es ist vollkommen in Ordnung, wenn Sie verschiedene Kriterien für die Auswahl einer Antwort auswählen, auch subjektive Kriterien, aber dann sollten Sie dies in der Frage angeben.
aaaaaaaaaaa 18.03.11
13

J, 28 17 25 27

J hat eigentlich keine Funktionen, aber hier ist ein monadisches Verb, das einen Vektor von Punkten aus der komplexen Ebene entnimmt:

4 8 4-:#/.~&(/:~&:|&,&(-/~))

Die Methode ist eine Mischung aus Michael Spencer (arbeitet ausschließlich mit Inter-Vertex-Längen, aber er versagt momentan bei meiner Raute2) und Eelvex (prüfe die Größe der Sets). Lesung von rechts nach links:

  • -/~ Berechnen Sie alle Punktdifferenzen
  • , ebnen
  • | Größe extrahieren
  • /:~ sortieren
  • #/.~ Noppen und zählen
  • 4 8 4 -:Muss genau 4 Äquidistanten (bei 0) haben, 8 etwas größer (Länge 1, Seiten), 4 noch größer (Länge sqrt 2, Diagonalen)

Demonstration:

   NB. give the verb a name for easier use
   f =: 4 8 4-:#/.~&(/:~&:|&,&(-/~))

   NB. standard square
   f 0 0j1 1j1 1
1

   NB. non-axis-aligned square
   f 0 2j1 3j_1 1j_2
1

   NB. different order
   f 0 1j1 0j1 1
1

   NB. rectangle
   f 0 0j2 3j2 3
0

   NB. rhombus 1
   f 0 3j4 8j4 5
0

   NB. rhombus 2
   f 0 1ad_60 1ad0 1ad60
0

Aus Speichergründen meine vorherige Methode (geordnete Eckpunkte erforderlich, aber reguläre Polygone beliebiger Reihenfolge erkennbar):

*./&(={.)&(%1&|.)&(-1&|.)

Erklärungen und Demo finden Sie im Verlauf. Die aktuelle Methode könnte wahrscheinlich auf andere Polygone erweitert werden, die 4 8 4einer Binomialverteilung sehr ähnlich sehen.

JB
quelle
Können Sie auf diese Sprache verlinken?
Sargun Dhillon
1
@knibbler: Warum nicht? Ich bin mir ziemlich sicher, dass es so ist.
Eelvex
1
Tatsächlich gibt es eine nicht quadratische Figur, die die Bedingungen erfüllt, auf die Sie prüfen, ein reguläres Dreieck plus einen Punkt Seitenlänge von einer Spitze des Dreiecks auf dem erweiterten Median. Aber die Frage, die für die Eingabe von Ganzzahlen gestellt wurde, ist in Ordnung.
aaaaaaaaaaa
1
Ach ja ok Ich dachte an gleichseitige Dreiecke mit dem 4. Punkt als Mittelpunkt, aber das wird durch die ganzzahligen Koordinaten ausgeschlossen
Gnibbler
1
Sie könnten 3 Zeichen ausschneiden, indem Sie es in eine explizite Definition 3 :'4 8 4-:#/.~/:~|,-/~y'
ändern
5

Python, 71 42

lambda A: len(set(A))==4 and len(set(abs(i-j)for i in A for j in A))==3

Update 1) um 4 verschiedene Punkte zu fordern (würde früher für wiederholte Punkte falsch positive Ergebnisse liefern - gibt es noch andere?) 2) um eine Funktion pro Spezifikation zu definieren

Für ein Quadrat muss der Vektor zwischen zwei beliebigen Punkten 0 (derselbe Punkt), eine Seite oder eine Diagonale sein. Die Menge der Größe dieser Vektoren muss also die Länge 3 haben.

# Accepts co-ordinates as sequences of complex numbers

SQUARES=[
 (0+0j,0+1j,1+1j,1+0j),  # standard square
 (0+0j,2+1j,3-1j,1-2j),  # non-axis-aligned square
 (0+0j,1+1j,0+1j,1+0j)   # different order
]

NONSQUARES=[
 (0+0j,0+2j,3+2j,3+0j),  # rectangle
 (0+0j,3+4j,8+4j,5+0j),  # rhombus
 (0+0j,0+1j,1+1j,0+0j),   # duplicated point
 (0+0j,1+60j,1+0j,1-60j)  # rhombus 2 (J B)
] 

test = "lambda A: len(set(A))==4 and len(set(abs(i-j)for i in A for j in A))==3"
assert len(test)==71

is_square=lambda A: len(set(A))==4 and len(set(abs(i-j)for i in A for j in A))==3    

for A in SQUARES:
    assert is_square(A)

for A in NONSQUARES:
    assert not is_square(A)
mbomb007
quelle
Ich denke, die Frage enthielt ausdrücklich eine Liste von Punkten und keinen Vektor.
Sargun Dhillon
Fehlalarm.
aaaaaaaaaaa
1
Also (0 + 0j, 0 + 0j, 1 + 0j, 0 + 1j) ist ein Quadrat?
mhagger
Meine Raute 2 ist nicht 1 +/- 60j, sondern eher exp (i j pi / 3) für Werte von -1, 0, 1. Beachten Sie, dass eBusiness darauf hinweist, dass nicht alle integraler Natur sein können, also nicht wirklich der Umfang der Frage.
JB
3

Haskell, 100 Zeichen

So würde ich die J-Lösung von JB in Haskell schreiben. Ohne den Versuch, die Lesbarkeit zu beeinträchtigen, indem nicht benötigte Zeichen entfernt werden, handelt es sich um 132 Zeichen:

import Data.List
d (x,y) (x',y') = (x-x')^2 + (y-y')^2
square xs = (== [4,8,4]) . map length . group . sort $ [d x y | x<-xs, y<-xs]

Sie können es auf 100 herunterkratzen, indem Sie überschüssige Leerzeichen entfernen und einige Dinge umbenennen

import Data.List
d(x,y)(a,b)=(x-a)^2+(y-b)^2
s l=(==[4,8,4]).map length.group.sort$[d x y|x<-l,y<-l]

Verwenden wir QuickCheck, um sicherzustellen, dass beliebige Quadrate mit einem Scheitelpunkt bei (x, y) und einem Kantenvektor (a, b) akzeptiert werden:

prop_square (x,y) (a,b) = square [(x,y),(x+a,y+b),(x-b,y+a),(x+a-b,y+b+a)]

Versuche es in ghci:

ghci> quickCheck prop_square
*** Failed! Falsifiable (after 1 test):  
(0,0)
(0,0)

Oh ja, das leere Quadrat wird hier nicht als Quadrat betrachtet, also überarbeiten wir unseren Test:

prop_square (x,y) (a,b) =
   (a,b) /= (0,0) ==> square [(x,y),(x+a,y+b),(x-b,y+a),(x+a-b,y+b+a)]

Und versuche es nochmal:

ghci> quickCheck prop_square
+++ OK, passed 100 tests.

quelle
1
Sparen Sie 11 Zeichen, indem Sie die Funktion ausrollen d. s l=[4,8,4]==(map length.group.sort)[(x-a)^2+(y-b)^2|(x,y)<-l,(a,b)<-l]
Ray
3

Faktor

Eine Implementierung in der Programmiersprache Factor :

USING: kernel math math.combinatorics math.vectors sequences sets ;

: square? ( seq -- ? )
    members [ length 4 = ] [
        2 [ first2 distance ] map-combinations
        { 0 } diff length 2 =
    ] bi and ;

Und einige Unit-Tests:

[ t ] [
    {
        { { 0 0 } { 0 1 } { 1 1 } { 1 0 } }   ! standard square
        { { 0 0 } { 2 1 } { 3 -1 } { 1 -2 } } ! non-axis-aligned square
        { { 0 0 } { 1 1 } { 0 1 } { 1 0 } }   ! different order
        { { 0 0 } { 0 4 } { 2 2 } { -2 2 } }  ! rotated square
    } [ square? ] all?
] unit-test

[ f ] [
    {
        { { 0 0 } { 0 2 } { 3 2 } { 3 0 } }   ! rectangle
        { { 0 0 } { 3 4 } { 8 4 } { 5 0 } }   ! rhombus
        { { 0 0 } { 0 0 } { 1 1 } { 0 0 } }   ! only 2 distinct points
        { { 0 0 } { 0 0 } { 1 0 } { 0 1 } }   ! only 3 distinct points
    } [ square? ] any?
] unit-test
mrjbq7
quelle
3

OCaml, 145, 164

let(%)(a,b)(c,d)=(c-a)*(c-a)+(d-b)*(d-b)
let t a b c d=a%b+a%c=b%c&&d%c+d%b=b%c&&a%b=a%c&&d%c=d%b
let q(a,b,c,d)=t a b c d||t a c d b||t a b d c

Laufen Sie wie folgt:

q ((0,0),(2,1),(3,-1),(1,-2))

Lassen Sie uns etwas deobfuscieren und erklären.

Zuerst definieren wir eine Norm:

let norm (ax,ay) (bx,by) = (bx-ax)*(bx-ax)+(by-ay)*(by-ay)

Sie werden feststellen, dass sqrt nicht angerufen wird und hier nicht benötigt wird.

let is_square_with_fixed_layout a b c d =
  (norm a b) + (norm a c) = norm b c
  && (norm d c) + (norm d b) = norm b c
  && norm a b = norm a c
  && norm d c = norm d b

Hier sind a, b, c und d Punkte. Wir gehen davon aus, dass diese Punkte so angeordnet sind:

a - b
| / |
c - d

Wenn wir ein Quadrat haben, müssen alle diese Bedingungen erfüllt sein:

  • abc ist ein rechtwinkliges Dreieck
  • bcd ist ein rechtwinkliges Dreieck
  • Die kleineren Seiten jedes rechtwinkligen Dreiecks haben die gleichen Normen

Beachten Sie, dass immer gilt:

is_square_with_fixed_layout r s t u = is_square_with_fixed_layout r t s u

Wir werden das nutzen, um unsere Testfunktion weiter unten zu vereinfachen.

Since our input is not ordered, we also have to check all permutations. Without loss of generality we can avoid permuting the first point:

let is_square (a,b,c,d) =
  is_square_with_fixed_layout a b c d
  || is_square_with_fixed_layout a c b d
  || is_square_with_fixed_layout a c d b
  || is_square_with_fixed_layout a b d c
  || is_square_with_fixed_layout a d b c
  || is_square_with_fixed_layout a d c b

After simplification:

let is_square (a,b,c,d) =
  is_square_with_fixed_layout a b c d
  || is_square_with_fixed_layout a c d b
  || is_square_with_fixed_layout a b d c

Edit: followed M.Giovannini's advice.

bltxd
quelle
Nice. We haven't seen much OCaml here :)
Eelvex
Use an operator instead of n for a reduction in 20 characters: let t a b c d=a%b+a%c=b%c&&d%c+d%b=b%c&&a%b=a%c&&d%c=d%b.
Matías Giovannini
2

Python (105)

Points are represented by (x,y) tuples. Points can be in any order and only accepts squares. Creates a list, s, of pairwise (non-zero) distances between the points. There should be 12 distances in total, in two unique groups.

def f(p):s=filter(None,[(x-z)**2+(y-w)**2for x,y in p for z,w in p]);return len(s)==12and len(set(s))==2
Hoa Long Tam
quelle
You could leave out the filter and check if the len of the set is 3. This suffers from the same false positive problem as my answer though.
gnibbler
>>> f([(0,0),(0,4),(2,2),(-2,2)]) = True
Sargun Dhillon
2
f([(0,0),(0,4),(2,2),(-2,2)]) is a square
gnibbler
2

Python - 42 chars

Looks like its an improvement to use complex numbers for the points

len(set(abs(x-y)for x in A for y in A))==3

where A = [(11+13j), (14+12j), (13+9j), (10+10j)]

old answer:

from itertools import*
len(set((a-c)**2+(b-d)**2 for(a,b),(c,d)in combinations(A,2)))==2

Points are specified in any order as a list, eg

A = [(11, 13), (14, 12), (13, 9), (10, 10)]
gnibbler
quelle
>>> A=[(0,0),(0,0),(1,1),(0,0)] >>> len(set((a-c)**2+(b-d)**2 for(a,b),(c,d)in combinations(A,2)))==2 True
Sargun Dhillon
@Sargun, that's a special case of a whole class of inputs that don't work. I'm trying to think of a fix that doens't blow out the size of the answer. Meanwhile, Can work out the general class of failing cases?
gnibbler
A=[(0,0),(0,4),(2,2),(-2,2)]; len(set((a-c)**2+(b-d)**2 for(a,b),(c,d)in combinations(A,2)))==2
Sargun Dhillon
@Sargun: that example is a square.
Keith Randall
to get rid of duplicated points you can add -set([0])
Keith Randall
2

C# -- not exactly short. Abusing LINQ. Selects distinct two-combinations of points in the input, calculates their distances, then verifies that exactly four of them are equal and that there is only one other distinct distance value. Point is a class with two double members, X and Y. Could easily be a Tuple, but meh.

var points = new List<Point>
             {
                 new Point( 0, 0 ), 
                 new Point( 3, 4 ), 
                 new Point( 8, 4 ), 
                 new Point( 5, 0 )
              };    
var distances = points.SelectMany(
    (value, index) => points.Skip(index + 1),
    (first, second) => new Tuple<Point, Point>(first, second)).Select(
        pointPair =>
        Math.Sqrt(Math.Pow(pointPair.Item2.X - pointPair.Item1.X, 2) +
                Math.Pow(pointPair.Item2.Y - pointPair.Item1.Y, 2)));
return
    distances.Any(
        d => distances.Where( p => p == d ).Count() == 4 &&
                distances.Where( p => p != d ).Distinct().Count() == 1 );
Daniel Coffman
quelle
2

PHP, 82 characters


//$x=array of x coordinates
//$y=array of respective y coordinates
/* bounding box of a square is also a square - check if Xmax-Xmin equals Ymax-Ymin */
function S($x,$y){sort($x);sort($y);return ($x[3]-$x[0]==$y[3]-$y[0])?true:false};

//Or even better (81 chars):
//$a=array of points - ((x1,y1), (x2,y2), (x3,y3), (x4,y4))
function S($a){sort($a);return (bool)($a[3][0]-$a[0][0]-abs($a[2][1]-$a[3][1]))};
arek
quelle
But just because the bounding box is square doesn't mean the points lie in a square. Necessary but not sufficient condition. Consider (0,0), (5,5), (10,0), (0,-5). Bounding box is square (0:10, -5:5); figure is not.
Floris
2

K - 33

Translation of the J solution by J B:

{4 8 4~#:'=_sqrt+/'_sqr,/x-/:\:x}

K suffers here from its reserved words(_sqr and _sqrt).

Testing:

  f:{4 8 4~#:'=_sqrt+/'_sqr,/x-/:\:x}

  f (0 0;0 1;1 1;1 0)
1

  f 4 2#0 0 1 1 0 1 1 0
1

  f 4 2#0 0 3 4 8 4 5 0
0
isawdrones
quelle
2

OCaml + Batteries, 132 characters

let q l=match List.group(-)[?List:(x-z)*(x-z)+(y-t)*(y-t)|x,y<-List:l;z,t<-List:l;(x,y)<(z,t)?]with[[s;_;_;_];[d;_]]->2*s=d|_->false

(look, Ma, no spaces!) The list comprehension in q forms the list of squared norms for each distinct unordered pair of points. A square has four equal sides and two equal diagonals, the squared lengths of the latter being twice the squared lengths of the former. Since there aren't equilateral triangles in the integer lattice the test isn't really necessary, but I include it for completeness.

Tests:

q [(0,0);(0,1);(1,1);(1,0)] ;;
- : bool = true
q [(0,0);(2,1);(3,-1);(1,-2)] ;;
- : bool = true
q [(0,0);(1,1);(0,1);(1,0)] ;;
- : bool = true
q [(0,0);(0,2);(3,2);(3,0)] ;;
- : bool = false
q [(0,0);(3,4);(8,4);(5,0)] ;;
- : bool = false
q [(0,0);(0,0);(1,1);(0,0)] ;;
- : bool = false
q [(0,0);(0,0);(1,0);(0,1)] ;;
- : bool = false
Matías Giovannini
quelle
2

Mathematica 65 80 69 66

Checks that the number of distinct inter-point distances (not including distance from a point to itself) is 2 and the shorter of the two is not 0.

h = Length@# == 2 \[And] Min@# != 0 &[Union[EuclideanDistance @@@ Subsets[#, {2}]]] &;

Usage

h@{{0, 0}, {0, 1}, {1, 1}, {1, 0}}       (*standard square *)
h@{{0, 0}, {2, 1}, {3, -1}, {1, -2}}     (*non-axis aligned square *)
h@{{0, 0}, {1, 1}, {0, 1}, {1, 0}}       (*a different order *)

h@{{0, 0}, {0, 2}, {3, 2}, {3, 0}}       (* rectangle *)
h@{{0, 0}, {3, 4}, {8, 4}, {5, 0}}       (* rhombus   *)
h@{{0, 0}, {0, 0}, {1, 1}, {0, 0}}       (* only 2 distinct points *)
h@{{0, 0}, {0, 1}, {1, 1}, {0, 1}}       (* only 3 distinct points *)

True
True
True
False
False
False
False

N.B.: \[And] is a single character in Mathematica.

DavidC
quelle
1
Are you telling me that Mathematica doesn't have a built-in IsSquare function?
goodguy
2

Jelly, 8 bytes

_Æm×ıḟƊṆ

Try it online!

Takes a list of complex numbers as a command line argument. Prints 1 or 0.

_Æm        Subtract mean of points from each point (i.e. center on 0)
   ×ıḟƊ    Rotate 90°, then compute set difference with original.
       Ṇ   Logical negation: if empty (i.e. sets are equal) then 1 else 0.

This seems like an enjoyable challenge to revive!

Lynn
quelle
1

Haskell (212)

import Data.List;j=any f.permutations where f x=(all g(t x)&&s(map m(t x)));t x=zip3 x(drop 1$z x)(drop 2$z x);g(a,b,c)=l a c==sqrt 2*l a b;m(a,b,_)=l a b;s(x:y)=all(==x)y;l(m,n)(o,p)=sqrt$(o-m)^2+(n-p)^2;z=cycle

Naive first attempt. Checks the following two conditions for all permutations of the input list of points (where a given permutation represents, say, a clockwise ordering of the points):

  • all angles are 90 degrees
  • all sides are the same length

Deobfuscated code and tests

j' = any satisfyBothConditions . permutations
          --f
    where satisfyBothConditions xs = all angleIs90 (transform xs) && 
                                     same (map findLength' (transform xs))
          --t
          transform xs = zip3 xs (drop 1 $ cycle xs) (drop 2 $ cycle xs)
          --g
          angleIs90 (a,b,c) = findLength a c == sqrt 2 * findLength a b
          --m
          findLength' (a,b,_) = findLength a b
          --s
          same (x:xs) = all (== x) xs
          --l
          findLength (x1,y1) (x2,y2) = sqrt $ (x2 - x1)^2 + (y2 - y1)^2


main = do print $ "These should be true"
          print $ j [(0,0),(0,1),(1,1),(1,0)]
          print $ j [(0,0),(2,1),(3,-1),(1,-2)]
          print $ j [(0,0),(1,1),(0,1),(1,0)]
          print $ "These should not"
          print $ j [(0,0),(0,2),(3,2),(3,0)]
          print $ j [(0,0),(3,4),(8,4),(5,0)]
          print $ "also testing j' just in case"
          print $ j' [(0,0),(0,1),(1,1),(1,0)]
          print $ j' [(0,0),(2,1),(3,-1),(1,-2)]
          print $ j' [(0,0),(1,1),(0,1),(1,0)]
          print $ j' [(0,0),(0,2),(3,2),(3,0)]
          print $ j' [(0,0),(3,4),(8,4),(5,0)]
Dan Burton
quelle
1

Scala (146 characters)

def s(l:List[List[Int]]){var r=Set(0.0);l map(a=>l map(b=>r+=(math.pow((b.head-a.head),2)+math.pow((b.last-a.last),2))));print(((r-0.0).size)==2)}
Gareth
quelle
1

JavaScript 144 characters

Mathematically equal to J Bs answer. It generates the 6 lengths and assert that the 2 greatest are equal and that the 4 smallest are equal. Input must be an array of arrays.

function F(a){d=[];g=0;for(b=4;--b;)for(c=b;c--;d[g++]=(e*e+f*f)/1e6)e=a[c][0]-a[b][0],f=a[c][1]-a[b][1];d.sort();return d[0]==d[3]&&d[4]==d[5]} //Compact function
testcases=[
[[0,0],[1,1],[1,0],[0,1]],
[[0,0],[999,999],[999,0],[0,999]],
[[0,0],[2,1],[3,-1],[1,-2]],
[[0,0],[0,2],[3,2],[3,0]],
[[0,0],[3,4],[8,4],[5,0]],
[[0,0],[0,0],[1,1],[0,0]],
[[0,0],[0,0],[1,0],[0,1]]
]
for(v=0;v<7;v++){
    document.write(F(testcases[v])+"<br>")
}

function G(a){ //Readable version
    d=[]
    g=0
    for(b=4;--b;){
        for(c=b;c--;){
            e=a[c][0]-a[b][0]
            f=a[c][1]-a[b][1]
            d[g++]=(e*e+f*f)/1e6 //The division tricks the sort algorithm to sort correctly by default method.
        }
    }
    d.sort()
    return (d[0]==d[3]&&d[4]==d[5])
}
aaaaaaaaaaaa
quelle
1

PHP, 161 158 characters

function S($a){for($b=4;--$b;)for($c=$b;$c--;){$e=$a[$c][0]-$a[$b][0];$f=$a[$c][1]-$a[$b][1];$d[$g++]=$e*$e+$f*$f;}sort($d);return$d[0]==$d[3]&&$d[4]==$d[5];}

Proof (1x1): http://codepad.viper-7.com/ZlBpOB

This is based off of eBuisness's JavaScript answer.

Kevin Brown
quelle
It's a bit unclear from the problem statement the points would come in ordered. I'll go ask.
J B
1
I don't think this will properly handle lots of cases. For example, it will incorrectly label rhombuses as squares.
Keith Randall
Updated this to match one of the JavaScript answers, should handle all cases.
Kevin Brown
1

JavaScript 1.8, 112 characters

Update: saved 2 characters by folding the array comprehensions together.

function i(s)(p=[],[(e=x-a,f=y-b,d=e*e+f*f,p[d]=~~p[d]+1)for each([a,b]in s)for each([x,y]in s)],/8,+4/.test(p))

Another reimplementation of J B's answer. Exploits JavaScript 1.7/1.8 features (expression closures, array comprehensions, destructuring assignment). Also abuses ~~ (double bitwise not operator) to coerce undefined to numeric, with array-to-string coercion and a regexp to check that the length counts are [4, 8, 4] (it assumes that exactly 4 points are passed). The abuse of the comma operator is an old obfuscated C trick.

Tests:

function assert(cond, x) { if (!cond) throw ["Assertion failure", x]; }

let text = "function i(s)(p=[],[(e=x-a,f=y-b,d=e*e+f*f,p[d]=~~p[d]+1)for each([a,b]in s)for each([x,y]in s)],/8,+4/.test(p))"
assert(text.length == 112);
assert(let (source = i.toSource()) (eval(text), source == i.toSource()));

// Example squares:
assert(i([[0,0],[0,1],[1,1],[1,0]]))    // standard square
assert(i([[0,0],[2,1],[3,-1],[1,-2]]))  // non-axis-aligned square
assert(i([[0,0],[1,1],[0,1],[1,0]]))    // different order

// Example non-squares:
assert(!i([[0,0],[0,2],[3,2],[3,0]]))  // rectangle
assert(!i([[0,0],[3,4],[8,4],[5,0]]))  // rhombus
assert(!i([[0,0],[0,0],[1,1],[0,0]]))  // only 2 distinct points
assert(!i([[0,0],[0,0],[1,0],[0,1]]))  // only 3 distinct points

// Degenerate square:
assert(!i([[0,0],[0,0],[0,0],[0,0]]))   // we reject this case
ecatmur
quelle
1

GoRuby - 66 characters

f=->a{z=12;a.pe(2).m{|k,l|(k-l).a}.so.go{|k|k}.a{|k,l|l.sz==z-=4}}

expanded:

f=->a{z=12;a.permutation(2).map{|k,l|(k-l).abs}.sort.group_by{|k|k}.all?{|k,l|l.size==(z-=4)}}

Same algorithm as J B's answer.

Test like:

p f[[Complex(0,0), Complex(0,1), Complex(1,1), Complex(1,0)]]

Outputs true for true and blank for false

Nemo157
quelle
Never heard of GoRuby. Is there anything official written about it? stackoverflow.com/questions/63998/hidden-features-of-ruby/…
Jonas Elfström
@Jonas: I haven't seen anything really official about it, the best blog post I have seen is this one. I wasn't actually able to get it built and working but an alternative is to just copy the golf-prelude into the same folder and run ruby -r ./golf-prelude.rb FILE_TO_RUN.rb and it will work exatcly the same.
Nemo157
it is not necessary to sort before group_by. .sort.group_by {...} should be written as .group_by {...}
user102008
1

Python 97 (without complex points)

def t(p):return len(set(p))-1==len(set([pow(pow(a-c,2)+pow(b-d,2),.5)for a,b in p for c,d in p]))

This will take lists of point tuples in [(x,y),(x,y),(x,y),(x,y)] in any order, and can handle duplicates, or the wrong number of points. It does NOT require complex points like the other python answers.

You can test it like this:

S1 = [(0,0),(1,0),(1,1),(0,1)]   # standard square
S2 = [(0,0),(2,1),(3,-1),(1,-2)] # non-axis-aligned square
S3 = [(0,0),(1,1),(0,1),(1,0)]   # different order
S4 = [(0,0),(2,2),(0,2),(2,0)]   #
S5 = [(0,0),(2,2),(0,2),(2,0),(0,0)] #Redundant points

B1 = [(0,0),(0,2),(3,2),(3,0)]  # rectangle
B2 = [(0,0),(3,4),(8,4),(5,0)]  # rhombus
B3 = [(0,0),(0,0),(1,1),(0,0)]  # only 2 distinct points
B4 = [(0,0),(0,0),(1,0),(0,1)]  # only 3 distinct points
B5 = [(1,1),(2,2),(3,3),(4,4)]  # Points on the same line
B6 = [(0,0),(2,2),(0,2)]        # Not enough points

def tests(f):
    assert(f(S1) == True)
    assert(f(S2) == True)
    assert(f(S3) == True)
    assert(f(S4) == True)
    assert(f(S5) == True)

    assert(f(B1) == False)
    assert(f(B2) == False)
    assert(f(B3) == False)
    assert(f(B4) == False)
    assert(f(B5) == False)
    assert(f(B6) == False)

def t(p):return len(set(p))-1==len(set([pow(pow(a-c,2)+pow(b-d,2),.5)for a,b in p for c,d in p]))

tests(t)

This will take a little explaining, but the overall idea is that there are only three distances between the points in a square (Side, Diagonal, Zero(point compared to itself)):

def t(p):return len(set(p))-1==len(set([pow(pow(a-c,2)+pow(b-d,2),.5)for a,b in p for c,d in p]))
  • for a list p of tuples (x,y)
  • Remove duplicates using set(p) and then test the length
  • Get every combination of points (a,b in p for c,d in p)
  • Get list of the distance from every point to every other point
  • Use set to check there are only three unique distances -- Zero (point compared to itself) -- Side length -- Diagonal length

To save code characters I am:

  • using a 1 char function name
  • using a 1 line function definition
  • Instead of checking the number of unique points is 4, I check that it is -1 the different point lengths (saves ==3==)
  • use list and tuple unpacking to get a,b in p for c,d in p, instead of using a[0],a[1]
  • uses pow(x,.5) instead of including math to get sqrt(x)
  • not putting spaces after the )
  • not putting a leading zero on the float

I fear someone can find a test case that breaks this. So please do and Ill correct. For instance the fact I just check for three distances, instead of doing an abs() and checking for side length and hypotenuse, seems like an error.

First time I've tried code golf. Be kind if I've broken any house rules.

Jagu
quelle
1

Clojure, 159 chars.

user=> (def squares
         [[[0,0] [0,1] [1,1]  [1,0]]   ; standard square
         [[0,0] [2,1] [3,-1] [1,-2]]  ; non-axis-aligned square
         [[0,0] [1,1] [0,1]  [1,0]]]) ; different order
#'user/squares
user=> (def non-squares
         [[[0,0] [0,2] [3,2] [3,0]]    ; rectangle
          [[0,0] [3,4] [8,4] [5,0]]])  ; rhombus
#'user/non-squares
user=> (defn norm
         [x y]
         (reduce + (map (comp #(* % %) -) x y)))
#'user/norm
user=> (defn square?
         [[a b c d]]
         (let [[x y z] (sort (map #(norm a %) [b c d]))]
           (and (= x y) (= z (* 2 x)))))
#'user/square?
user=> (every? square? squares)
true
user=> (not-any? square? non-squares)
true

Edit: To also explain a little bit.

  • First define a norm which basically gives the distance between two given points.
  • Then calculate the distance of the first point to the other three points.
  • Sort the three distances. (This allows any order of the points.)
  • The two shortest distances must be equal to be a square.
  • The third (longest) distance must be equal to the square root of the sum of the squares of the short distances by the theorem of Pythagoras.

(Note: the square rooting is not needed and hence in the code saved above.)

Meikel
quelle
1

C#, 107 characters

return p.Distinct().Count()==4&&
(from a in p from b in p select (a-b).LengthSquared).Distinct().Count()==3;

Where points is List of Vector3D containing the points.

Computes all distances squared between all points, and if there are exactly three distinct types (must be 0, some value a, and 2*a) and 4 distinct points then the points form a square.

YOU
quelle
1

Python, 66

Improving paperhorse's answer from 76 to 66:

def U(A):c=sum(A)/4;d=A[0]-c;return{d+c,c-d,d*1j+c,c-d*1j}==set(A)
Reinstate Monica
quelle
1

Python 2, 49 bytes

lambda l:all(1j*z+(1-1j)*sum(l)/4in l for z in l)

Try it online!

Takes a list of four complex numbers as input. Rotates each point 90 degrees around the average, and checks that each resulting point is in the original list.

Same length (though shorter in Python 3 using {*l}).

lambda l:{1j*z+(1-1j)*sum(l)/4for z in l}==set(l)

Try it online!

xnor
quelle
Why not use Python 3 if that is shorter? Also, if it is allowed to return arbitrary truthy / falsy values in Python, ^ can be used instead of ==.
Joel
@Joel Python 2 is mostly preference, and that this is a really old challenge from 2011 when Python 2 was pretty much assumed Python golfing. And the challenge says to return true or false, so I stuck with that. If this was posted today, it would probably specify outputting truthy/falsey or one of two distinct values, and it might even by OK to assume that by default.
xnor
1

Wolfram Language (Mathematica), 32 31 bytes

Tr[#^2]==Tr[#^3]==0&[#-Mean@#]&

Try it online!

Takes a list of points represented by complex numbers, calculates the second and third central moment, and checks that both are zero.

Un-golfed:

S[p_] := Total[(p - Mean[p])^2] == Total[(p - Mean[p])^3] == 0

or

S[p_] := CentralMoment[p, 2] == CentralMoment[p, 3] == 0

proof

This criterion works on the entire complex plane, not just on the Gaussian integers.

  1. First, we note that the central moments do not change when the points are translated together. For a set of points

    P = Table[c + x[i] + I*y[i], {i, 4}]
    

    the central moments are all independent of c (that's why they are called central):

    {FreeQ[FullSimplify[CentralMoment[P, 2]], c], FreeQ[FullSimplify[CentralMoment[P, 3]], c]}
    (*    {True, True}    *)
    
  2. Second, the central moments have a simple dependence on overall complex scaling (scaling and rotation) of the set of points:

    P = Table[f * (x[i] + I*y[i]), {i, 4}];
    FullSimplify[CentralMoment[P, 2]]
    (*    f^2 * (...)    *)
    FullSimplify[CentralMoment[P, 3]]
    (*    f^3 * (...)    *)
    

    This means that if a central moment is zero, then scaling and/or rotating the set of points will keep the central moment equal to zero.

  3. Third, let's prove the criterion for a list of points where the first two points are fixed:

    P = {0, 1, x[3] + I*y[3], x[4] + I*y[4]};
    

    Under what conditions are the real and imaginary parts of the second and third central moments zero?

    C2 = CentralMoment[P, 2] // ReIm // ComplexExpand // FullSimplify;
    C3 = CentralMoment[P, 3] // ReIm // ComplexExpand // FullSimplify;
    Solve[Thread[Join[C2, C3] == 0], {x[3], y[3], x[4], y[4]}, Reals] // FullSimplify
    (*    {{x[3] -> 0, y[3] -> -1, x[4] -> 1, y[4] -> -1},
           {x[3] -> 0, y[3] -> 1, x[4] -> 1, y[4] -> 1},
           {x[3] -> 1/2, y[3] -> -1/2, x[4] -> 1/2, y[4] -> 1/2},
           {x[3] -> 1/2, y[3] -> 1/2, x[4] -> 1/2, y[4] -> -1/2},
           {x[3] -> 1, y[3] -> -1, x[4] -> 0, y[4] -> -1},
           {x[3] -> 1, y[3] -> 1, x[4] -> 0, y[4] -> 1}}    *)
    

    All of these six solutions represent squares: enter image description here Therefore, the only way that a list of points of the form {0, 1, x[3] + I*y[3], x[4] + I*y[4]} can have zero second and third central moments is when the four points form a square.

Due to the translation, rotation, and scaling properties demonstrated in points 1 and 2, this means that any time the second and third central moments are zero, we have a square in some translation/rotation/scaling state. ∎

generalization

The k-th central moment of a regular n-gon is zero if k is not divisible by n. Enough of these conditions must be combined to make up a sufficient criterion for detecting n-gons. For the case n=4 it was enough to detect zeros in k=2 and k=3; for detecting, e.g., hexagons (n=6) it may be necessary to check k=2,3,4,5 for zeros. I haven't proved the following, but suspect that it will detect any regular n-gon:

isregularngon[p_List] :=
  And @@ Table[PossibleZeroQ[CentralMoment[p, k]], {k, 2, Length[p] - 1}]

The code challenge is essentially this code specialized for length-4 lists.

Roman
quelle
The solution looks fairly interesting. Could you explain why it gives the correct answer?
Joel
@Joel I've added a proof.
Roman
Thanks a lot. It would be ideal that there could be a more intuitive mathematical explanation of this nice solution.
Joel
@Joel I can give you the thread that led me to this solution. I started by noticing that squares (as lists of coordinates, not complex numbers) have a covariance matrix that is proportional to the unit matrix; however, this condition is not sufficient (false positives). The third central moment must be zero for any structure of point symmetry. So I switched to complex representation to place a condition on the second and third central moments, and to my surprise it turned out that the second central moment is zero for squares.
Roman
Great. Thanks for showing the path to this solution.
Joel
0

J, 31 29 27 26

3=[:#[:~.[:,([:+/*:@-)"1/~

checks if the 8 smallest distances between the points are the same. checks if there are exactly three kinds of distances between the points (zero, side length and diagonal length).

f 4 2 $ 0 0 2 1 3 _1 1 _2
1
f 4 2 $ 0 0 0 2 3 2 3 0
0

4 2 $ is a way of writing an array in J.

Eelvex
quelle
This fails the rhombus test.
J B
@JB: I had a typo. I changed the method anyway now.
Eelvex
Eeew... you're taking the same method I was stealing. Except my version's shorter :p
J B
@JB: really? I didn't notice that. Who else checks (3 == #distances)?
Eelvex
@JB: oic... some check for combinations of 2. :-/
Eelvex
0

Smalltalk for 106 characters

s:=Set new.
p permutationsDo:[:e|s add:((e first - e second) dotProduct:(e first - e third))].
s size = 2

where p is a collection of points, e.g.

p := { 0@0. 2@1. 3@ -1. 1@ -2}. "twisted square"

I think the math is sound...


quelle
Checking for 2 distinct dot products doesn't cut it. Points placed in the same position may produce false positives.
aaaaaaaaaaaa
0

Mathematica, 123 characters (but you can do better):

Flatten[Table[x-y,{x,a},{y,a}],1]
Sort[DeleteDuplicates[Abs[Flatten[Table[c.d,{c,%},{d,%}]]]]]
%[[1]]==0&&%[[3]]/%[[2]]==2

Where 'a' is the input in Mathematica list form, eg: a={{0,0},{3,4},{8,4},{5,0}}

The key is to look at the dot products between all the vectors and note that they must have exactly three values: 0, x, and 2*x for some value of x. The dot product checks both perpendicularity AND length in one swell foop.

I know there are Mathematica shortcuts that can make this shorter, but I don't know what they are.

barrycarter
quelle
I think this one is wrong as well, but I can't figure what the code does.
aaaaaaaaaaaa
It calculates all the vectors between the 4 points, takes all of the dot products (absolute value), and expects the result to consist exactly of 0, x, 2*x for some value of x.
barrycarter
So 16 vectors -> 256 dot products, and you check that the high value is 2 times the low, but you don't how many there is of each value. Is that correct understood?
aaaaaaaaaaaa
Yes, that correctly describes my algorithm. And I now think you're right: you could construct a scenario in which all 3 values occurred, but not in the right quantity. Rats. Should be fixable though?
barrycarter
@barrycarter You can save characters by using Union instead of Sort@DeleteDuplicates. I put your 3 lines together also:#[[1]] == 0 && #[[3]]/#[[2]] == 2 &[ Union@Abs@Flatten[Table[c.d, {c, #}, {d, #}]] &[ Flatten[Table[x - y, {x, a}, {y, a}], 1]]]
DavidC
0

Haskell, "wc -c" reports 110 characters. Does not check that the input has 4 elements.

import Data.List
k [a,b]=2*a==b
k _=0<1
h ((a,b):t)=map (\(c,d)->(a-c)^2+(b-d)^2) t++h t
h _=[]
j=k.nub.sort.h

I tested on

test1 = [(0,0),(3,4),(-4,3),(-1,7)] -- j test1 is True
test2 = [(0,0),(3,4),(-3,4),(0,8)]  -- j test2 is False
Chris Kuklewicz
quelle
Note the above never gets the distance from a point to itself, so the presence of a distance of 0 would indicate a repeated point in the input list, and this will show up in the sorted list as k [0,b] so 2*0==b will always fail since b cannot be same the as 0.
Chris Kuklewicz