Lookalike shapes

23

3

Similar figures

Two rectangles are similar if the ratios of their sides are the same.

Consider these two rectangles; a rectangle 5 lines tall and 11 chars wide:

===========
===========
===========
===========
===========

and a rectangle 10 lines tall and 22 chars wide:

======================
======================
======================
======================
======================
======================
======================
======================
======================
======================

These shapes are similar because the ratios of their sides are the same. To put it formally (with \$h\$ being the shortest side and \$w\$ being the longest side):

$$ \frac{h_1}{w_1} = \frac{h_2}{w_2} $$

You can also do:

$$ \frac{h_1}{h_2} = \frac{w_1}{w_2} $$

The challenge

Write a program or function that takes a "main" rectangle and some "other" rectangles and prints which of "others" are similar to "main".

The input

A shape and a list of shapes. Each shape consists of 2 non-zero positive integers, which denote the width and height of the rectangle. For instance, this:

(4,2), (3,9)

denotes two rectangles, a 4x2 and a 3x9. The exact format of the input may be however you desire.

The output

The indices of the "other" shapes that are similar to "main". You may choose whether the indices are 0- or 1-based, as well as the exact format and order of the output.

Sample program

In Python:

main = eval(raw_input()) # The main rectangle.
rects = eval(raw_input()) # The list of rectangles.
similar = set()
for i, rect in enumerate(rects):
    if max(main)*min(rect) == min(main)*max(rect): # Cross-multiply
        # They are similar.
        similar.add(i)

print similar

Sample input and output

Input:

(1, 2)
[(1, 2), (2, 4)]

Output:

set([0, 1])

Input:

(1, 2)
[(1, 9), (2, 5), (16, 8)]

Output:

set([2])

Winning

This is code-golf, so the shortest submission wins.

Notes

  • This should go without saying, but standard loopholes are banned.
  • No builtins for locating similar figures may be used. (I don't even know if that exists, but I would't be surprised!)

kirbyfan64sos

Posted 2015-09-03T22:42:46.537

Reputation: 8 730

Is using floating point division allowed? Would [1.0 2.0] be an acceptable input format? – Dennis – 2015-09-04T00:08:28.590

@Dennis Provided your selected language doesn't have oddly low floating point precision and therefore the test cases fail, it should be fine. ;) – kirbyfan64sos – 2015-09-04T00:20:07.847

Instead of indices may we also output the actual similar shapes themselves? – orlp – 2015-09-04T01:36:24.077

@orlp Nope!!! :D – kirbyfan64sos – 2015-09-04T02:06:10.097

@Dennis The trouble with floating point is that you run into trouble in some cases. e.g., [1.0 5.0] is not the same as [0.2 1.0] because 0.2 is not representable in binary floating point. (That is, 5 * 0.2 != 1.0.) – Chris Jester-Young – 2015-09-04T02:21:23.670

@ChrisJester-Young I know, that's why I asked. 0.2 won't be an issue here though, since the input will always be an integer (in the mathematical sense). – Dennis – 2015-09-04T02:23:28.360

What is (0,0) similar to? Also your sample program fails parsing (3+i, 4-2i) – user253751 – 2015-09-04T16:05:34.957

@immibis You only need to handle non-zero integers, not imaginary numbers. I'll edit the post. – kirbyfan64sos – 2015-09-04T17:25:32.267

So they can be negative? – user253751 – 2015-09-05T09:35:56.980

@immibis Uhh...nope. Editing... – kirbyfan64sos – 2015-09-05T15:29:56.580

3Is the output format of outputting the indices mandatory? For a test case like [(1,2), (2,4), (1,9), (2,5), (16,8)], is only [0,1,4] and [1,2,5] allowed, or could we also output [1,1,0,0,1] or [(1,2), (2,4), (16,8)]? – Kevin Cruijssen – 2019-03-15T14:38:48.793

Answers

5

Pyth, 15 bytes

fqcFS@QTcFSvzUQ

orlp

Posted 2015-09-03T22:42:46.537

Reputation: 37 067

11

Python, 61 bytes

lambda a,b,l:[i for i,(x,y)in enumerate(l)if x/y in[a/b,b/a]]

Yes, I'm using spending 9 chars to write enumerate. Takes input like 1, 2, [(1, 9), (3,6), (2, 5), (16, 8)]. For Python 2, input values need to be written as floats.

One char longer (62) in Python 3:

def f(a,b,l,i=0):
 for x,y in l:b/a!=x/y!=a/b or print(i);i+=1

xnor

Posted 2015-09-03T22:42:46.537

Reputation: 115 687

Do you mind explaining this? I'd like to know what's going on. – The_Basset_Hound – 2015-09-04T20:12:24.733

@BassetHound for each element in the input list, the comprehension unpacksi as the index, and (x,y) as the point. It then checks if the value x/y is either equal to the initial two numbers' quotient (a/b) or its reciprocal (b/a). If it is equal to one of those values, that value of i is added to the list, otherwise it is discarded. – FryAmTheEggman – 2015-09-04T21:18:33.200

9

CJam, 22 20 19 bytes

{:$::/_0=f=ee::*0-}

The above is an anonymous function that pops a single array of floating point pairs (first pair is needle) from the stack and pushes the array of 1-based indexes in return.

Try it online in the CJam interpreter.

How it works

:$                e# Sort each pair.
  ::/             e# [a b] -> a/b
     _0=          e# Push a copy of the array and extract the first float (needle).
        f=        e# Check which floats are equal to the needle.
          ee      e# Enumerate the resulting Booleans.
            ::*   e# Multiply each Boolean by its index.
                  e# This yields 0 for the needle (index 0) and for non-matching
                  e# haystack pairs (Boolean 0).
               0- e# Remove all zeroes from the array.

Dennis

Posted 2015-09-03T22:42:46.537

Reputation: 196 637

8

Haskell, 48 bytes

(a!b)l=[i|(i,(x,y))<-zip[0..]l,x/y+y/x==a/b+b/a]

Try it online!

Call this like (!) 1 2 [(1, 9), (3,6), (2, 5), (16, 8)].

A near-port of my Python answer. The expression zip[0..]l enumerates the list with its indices.

The expression x/y+y/x==a/b+b/a checks that the ratio x/y is either a/b or b/a, since the function f(z) = z + 1/z has f(z) = f(1/z) and no other collisions.

xnor

Posted 2015-09-03T22:42:46.537

Reputation: 115 687

Maybe make h an operator taking three arguments? That'd save one byte, and I think it would stay within the rules. – dfeuer – 2019-03-14T22:12:15.500

@dfeuer Sure, it's definitely allowed by modern standards though back it was fuzzier what liberties could be taken with I/O. – xnor – 2019-03-14T23:50:25.317

7

Snowman 1.0.2, 61 chars

}vgvgaC"[0-9]+"sM:10sB;aM2aG:AsO:nD;aF;aM0AAgaA*|:#eQ;AsItSsP

Pure gibberish (unless you happen to know Snowman), a.k.a. exactly in line with the language's design goal of being as confusing as possible.

Input format is same as in the post, output format is also the same minus set( and ).

Ungolfed (or unminified, really):

}vgvgaC     // read two lines of input, concatenate
"[0-9]+"sM  // use a regex to grab all numbers
:10sB;aM    // essentially map(parseInt)
2aG         // take groups of 2 (i.e. all the ordered pairs)

// now map over each ordered pair...
:
  AsO       // sort
  :nD;aF    // fold with division - with 2 array elements, this is just a[0]/a[1]
;aM

// we now have an array of short side to long side ratios
// take out the first one
0AAgaA      // active vars beg, b=array[0], g=the rest
*|          // store first ordered pair in permavar, bring the rest to top

// select indices where...
:
  #         // retrieve first ordered pair
  eQ        // equal?
;AsI

tSsP  // to-string and output

I'm pretty proud of some of the tricks I used in this one:

  • I used the same input format as in the post. But instead of trying to parse it somehow, which would get really messy, I just concatenated the two lines and then used a regex to extract all the numbers into one big array (with which I then did 2aG, i.e. get every group of 2).

  • :nD;aF is pretty fancy. It simply takes an array of two elements and divides the first one by the second one. Which seems pretty simple, but doing it the intuitive way (a[0]/a[1]) would be far, far longer in Snowman: 0aa`NiN`aA|,nD (and that's assuming we don't have to worry about messing with other existing variables). Instead, I used the "fold" method with a predicate of "divide," which, for an array of two elements, achieves the same thing.

  • 0AAgaA looks innocuous enough, but what it actually does is store a 0 to the variables, then takes all variables with an index greater than that (so, all variables except for the first one). But the trick is, instead of AaG (which would get rid of the original array and the 0), I used AAg, which keeps both. Now I use aA, at-index, using the very same 0 to get the first element of the array—furthermore, this is in consume-mode (aA instead of aa), so it gets rid of the 0 and original array too, which are now garbage for us.

    Alas, 0AAgaA*| does essentially the same thing that GolfScript does in one character: (. However, I still think it's pretty nice, by Snowman standards. :)

Doorknob

Posted 2015-09-03T22:42:46.537

Reputation: 68 138

3

APL (Dyalog Unicode), 16 13 bytesSBCS

(=.×∘⌽∨=.×)⍤1

Try it online!

-3 thanks to @ngn!

Explanation:

(=.×∘⌽∨=.×)⍤1
(     ∨   )   ⍝ "OR" together...
 =.    =.     ⍝ ...fold by equality of:
   ×∘⌽        ⍝ - the arguments multiplied by itself reversed
         x    ⍝ - the argument multiplied by itself
           ⍤1 ⍝ Applied at rank 1 (traverses)

Output format is a binary vector like 1 1 0 0 1 of which "other" rectangle is a look-a-like.

APL (Dyalog Extended), 11 bytesSBCS

=/-×⍥(⌈/)¨⌽

Try it online!

Explanation:

=/-×⍥(⌈/)¨⌽ ⍝ takes only a right argument: ⍵, shape: (main (other...))
          ⌽ ⍝ two transformations:
  -         ⍝ - left (L) vectorized negation: -⍵
          ⌽ ⍝ - right (R): reverse. (main other) => (other main)
     (⌈/)¨  ⍝ transformation: calculate the max (since L is negated, it calculates the min)
            ⍝ (/ reduces over ⌈ max)
            ⍝ this vectorizes, so the "main" side (with only one rect) will get repeated once for each "other" rect on both sides
   ×⍥       ⍝ over multiplication: apply the transformation to both sides. F(L)×F(R)
=/          ⍝ reduce the 2-element matrix (the "main" that's now the side of the "other") to check which are equal

Output format is the same as the main Dyalog answer.

Thanks to Adám for the help golfing + Extended.

Ven

Posted 2015-09-03T22:42:46.537

Reputation: 3 382

(=.×∘⌽∨=.×)⍤1 – ngn – 2019-04-12T18:28:46.060

Thanks. Will try to inspect that first – Ven – 2019-04-15T18:23:06.780

3

Mathematica, 41 bytes

Position[a=Sort@#;Sort@#/a&/@#2,{x_,x_}]&

Usage:

Position[a = Sort@#; Sort@#/a & /@ #2, {x_, x_}] &[{1, 2}, {{1, 2}, {2, 5}, {16, 8}}]
(* {{1}, {3}} *)

Martin Ender

Posted 2015-09-03T22:42:46.537

Reputation: 184 808

1I just knew Mathematica was going to come up somehow! – kirbyfan64sos – 2015-09-03T23:32:03.313

3

Pyth - 14 bytes

Filters by comparing quotients, then maps indexOf.

xLQfqcFSTcFvzQ

Test Suite.

Maltysen

Posted 2015-09-03T22:42:46.537

Reputation: 25 023

This doesn't sort the main shape, so it will give the wrong answer when the main shape's first side length is larger. See this test case

– isaacg – 2015-09-05T19:39:20.547

@isaacg good point, will fix. – Maltysen – 2015-09-05T19:40:31.363

This fails on inputs with repeated elements, for example 1,2 and [(1, 2), (2, 4), (1, 2)] will give [0, 1, 0] rather than the correct [0, 1, 2]. – orlp – 2015-09-06T06:26:31.277

I want to accept this one, since it is the shortest, but is the problem @orlp mentioned fixed? – kirbyfan64sos – 2015-09-14T16:40:48.563

1

@kirbyfan64sos No.

– orlp – 2015-09-14T16:43:18.230

2

Brachylog, 14 bytes

z{iXhpᵐ/ᵛ∧Xt}ᵘ

Try it online!

Takes input as a list containing a list containing the main rectangle and the list of other rectangles (so test case 1 is [[[1,2]],[[1,2],[2,4]]]), and outputs a list of 0-based indices through the output variable.

z                 Zip the elements of the input, pairing every "other" rectangle with the main rectangle.
 {          }ᵘ    Find (and output) every unique possible output from the following:
  iX              X is an element of the zip paired with its index in the zip.
    h             That element
      ᵐ           with both of its elements
     p            permuted
        ᵛ         produces the same output for both elements
       /          when the first element of each is divided by the second.
         ∧Xt      Output the index.

If that sort of odd and specific input formatting is cheating, it's a bit longer...

Brachylog, 18 bytes

{hpX&tiYh;X/ᵛ∧Yt}ᶠ

Try it online!

Takes input as a list containing the main rectangle and the list of other rectangles (so test case 1 is the more obvious [[1,2],[[1,2],[2,4]]]), and outputs a list of 0-based indices through the output variable.

{               }ᵘ    Find (and output) every possible output from the following:
  p                   A permutation of
 h                    the first element of the input
   X                  is X,
    &                 and
      i               a pair [element, index] from
     t                the last element of the input
       Y              is Y,
        h             the first element of which
            ᵛ         produces the same output from
           /          division
         ;            as
          X           X.
             ∧Yt      Output the index.

To determine if two width-height pairs represent similar rectangles, it just takes the four bytes pᵐ/ᵛ (which outputs the shared ratio or its reciprocal). All the rest is handling the multiple rectangles to compare, and the output being indices.

Unrelated String

Posted 2015-09-03T22:42:46.537

Reputation: 5 300

2

dzaima/APL, 7 bytes

=/⍤÷⍥>¨

Try it online!

8 bytes outputting a list of indices instead of a boolean vector

      ¨ for each (pairing the left input with each of the right)
    ⍥>    do the below over sorting the arguments
=/          equals reduce
  ⍤         after
   ÷        vectorized division of the two

dzaima

Posted 2015-09-03T22:42:46.537

Reputation: 19 048

Although it's a nice answer, we have to output the indices. So your TIO test case should result in either [0,1,4] or [1,2,5] (not sure if your language is 0- or 1-indexed). It would have been a better challenge imho if all three output-formats are allowed: indices; filter to keep the truthy values; list of truthy/falsey values (like you have now), instead of only allowed indices. – Kevin Cruijssen – 2019-03-15T14:36:16.007

@KevinCruijssen "You may choose [...] the exact format and order of the output." in APL it's very common practice to store indices as a boolean vector, but you're right, that probably should be clarified. – dzaima – 2019-03-15T14:40:08.627

Well, I read "You may choose whether the indices are 0- or 1-based, as well as the exact format and order of the output." as it can be [0,1,4], [1,2,5], 4\n0\n1, 5 2 1, etc. etc., since it still stated indices. But I've asked OP to clarify (if they response, since it's a 4 year old challenge). In my 05AB1E answer it would mean 14 bytes if the indices are mandatory vs 8 bytes if either of the other two options are allowed. Regardless, I upvoted your answer. :) – Kevin Cruijssen – 2019-03-15T14:49:45.347

2

Julia, 62 bytes

f(m,o)=find([(t=sort(m).*sort(i,rev=true);t[1]==t[2])for i=o])

The find function locates true elements in a boolean vector. .* performs elementwise multiplication of vectors.

Ungolfed:

function f(m::Array, o::Array)
    find([(t = sort(m) .* sort(i, rev=true); t[1] == t[2]) for i in o])
end

Usage:

f([1,2], {[1,9], [2,5], [16,8]})

Alex A.

Posted 2015-09-03T22:42:46.537

Reputation: 23 761

2

K5, 19 bytes

I think this will do the trick:

&(*t)=1_t:{%/x@>x}'

Takes a list of pairs where the first is the "main". Computes the ratio by dividing the sorted dimensions of each pair. Returns a list of the 0-indexed positions of the matching pairs. (arguably the input format I chose makes this -1 indexed- if this is considered invalid tack on a 1+ to the beginning and add two characters to the size of my program.)

Usage example:

  &(*t)=1_t:{%/x@>x}'(1 2;1 2;2 4;2 5;16 8)
0 1 3

This runs in oK- note that I implicitly depend on division always producing floating point results. It would work in Kona if you added a decimal point to all the numbers in the input and added a space after the _.

JohnE

Posted 2015-09-03T22:42:46.537

Reputation: 4 632

2

Octave / Matlab, 44 bytes

Using an anonymous function:

@(x,y)find((max(x))*min(y')==min(x)*max(y'))

The result is in 1-based indexing.

To use it, define the function

>> @(x,y)find((max(x))*min(y')==min(x)*max(y'));

and call it with the following format

>> ans([1 2], [1 9; 2 5; 16 8])
ans =
     3

You can try it online.


If the result can be in logical indexing (0 indicates not similar, 1 indicates similar): 38 bytes:

@(x,y)(max(x))*min(y')==min(x)*max(y')

Same example as above:

>> @(x,y)(max(x))*min(y')==min(x)*max(y')
ans = 
    @(x,y)(max(x))*min(y')==min(x)*max(y')

>> ans([1 2], [1 9; 2 5; 16 8])
ans =
 0     0     1

Luis Mendo

Posted 2015-09-03T22:42:46.537

Reputation: 87 464

1

PowerShell, 58 56 bytes

-2 bytes thanks to mazzy x2

param($x,$y,$b)$b|%{($i++)[$x/$y-($z=$_|sort)[0]/$z[1]]}

Try it online!

This slightly abuses the input may be however you desire clause by having the first shape's components come separately to save 3 bytes.

PowerShell, 61 59 bytes

param($a,$b)$b|%{($i++)[$a[0]/$a[1]-($x=$_|sort)[0]/$x[1]]}

Try it online!

Uses conditional indexing to swap between the current zero-based index and null based on whether or not the ratios line up. Luckily in this case, $i increments regardless of it being printed or not.

Veskah

Posted 2015-09-03T22:42:46.537

Reputation: 3 580

1You can save more if you use - instead -ne. – mazzy – 2019-03-14T17:54:07.003

1

PowerShell, 57 bytes

$args|%{$x,$y=$_|sort
($i++)[$r-$y/$x]
if(!$r){$r=$y/$x}}

Try it online!

Indices are 1-based.

mazzy

Posted 2015-09-03T22:42:46.537

Reputation: 4 832

1

Haskell, 75 bytes

import Data.List
a(x,y)=max x y/min x y
s r=elemIndices(True).map((==a r).a)

Leif Willerts

Posted 2015-09-03T22:42:46.537

Reputation: 1 060

56 bytes – dfeuer – 2019-03-14T22:05:34.857

54 bytes – dfeuer – 2019-03-14T22:30:52.980

0

05AB1E, 15 14 bytes

ʒ‚ε{ü/}Ë}J¹Jsk

Try it online or verify all test cases.

Explanation:

ʒ               # Filter the (implicit) input-list by:
 ‚              #  Pair the current width/height with the (implicit) input width/height
  ε             #  Map both width/height pairs to:
   {            #   Sort from lowest to highest
    ü/          #   Pair-wise divide them from each other
      }Ë        #  After the map: check if both values in the mapped list are equals
        }J      # After the filter: join all remaining pairs together to a string
          ¹J    # Also join all pairs of the first input together to a string
            s   # Swap to get the filtered result again
             k  # And get it's indices in the complete input-list
                # (which is output implicitly)

The Joins are there because 05AB1E can't determine the indices on multidimensional lists a.f.a.i.k.


If outputting the width/height pairs that are truthy, or outputting a list of truthy/falsey values based on the input-list, it could be 8 bytes instead:

ʒ‚ε{ü/}Ë

Try it online or verify all test cases.

ε‚ε{ü/}Ë

Try it online or verify all test cases.

Kevin Cruijssen

Posted 2015-09-03T22:42:46.537

Reputation: 67 575

0

Javascript (ES6), 75

(a,b)=>b.filter(e=>e.l*a.h==a.l*e.h||e.l*a.l==a.h*e.h).map(e=>b.indexOf(e))

Alternative, also 75

(a,b)=>b.map((e,i)=>e.l*a.h==a.l*e.h||e.l*a.l==a.h*e.h?i:-1).filter(e=>e+1)

Input is taken as a JSON object, and an array of JSON objects

{
    l: length of rectangle,
    h: height of rectangle
}

DankMemes

Posted 2015-09-03T22:42:46.537

Reputation: 2 769

I don't think this works with the second test case. – kirbyfan64sos – 2015-09-03T23:47:05.537

@kirbyfan64sos sorry didn't see that part. It's fixed (but I'm sure I can golf it more) – DankMemes – 2015-09-03T23:53:56.807

These are not JSON objects, these are plain javascript objects. JSON is a data transfer format. – edc65 – 2015-09-07T18:46:37.413