11
\$\begingroup\$

I started a CodeWars kata in Python, just two days ago, related to code golf.

Task: Given two congruent circles a and b of radius r, return the area of their intersection rounded down to the nearest integer, in less than 128 chars.

  • Has to be a one-liner.

  • Version: Python 3.6

  • External libraries: You may import NumPy as an external library.

  • Function name: circleIntersection

  • Input example: circleIntersection([0,0],[0,10], 10)

This is the closest I could come (129):

from math import*;circleIntersection=lambda a,b,r:r*r*(lambda h:h<1and acos(h)-h*(1-h*h)**.5)(hypot(b[0]-a[0],b[1]-a[1])/r/2)//.5
\$\endgroup\$
  • \$\begingroup\$ You mention that 3.6 is the latest version provided -- is Python 2 an option? If so, I have a way to save bytes with that. It could also help to link the site's problem page if we can view that without login. \$\endgroup\$ – xnor Feb 5 at 23:27
  • \$\begingroup\$ Yes, Python 2 is an option. You need to login in order to view the problem :( \$\endgroup\$ – Leo oeL Feb 6 at 0:07
4
\$\begingroup\$

Direct translation to NumPy: 127 bytes

from numpy import*
circleIntersection=lambda a,b,r:r*r*(lambda h:h<1and arccos(h)-h*(1-h*h)**.5)(hypot(*subtract(b,a))/r/2)//.5

Try it online!

Converting your code to numpy seems to save the required bytes. While using hypot is still ugly, being able to splat the result of subtract is still enough shorter that the loss of converting to arccos doesn't matter.

It is possible this can save more since it doesn't error on h<1 and will return nan in the case that the circles do not intersect. I don't know if that is permissible though since nan is not equal to zero.

I'm not convinced this is optimal, in particular I didn't look at improving the algorithm at all.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I can't thank you enough! I tried NaN in one of my desperate tries and it was not acceptable. However, your piece of code did the job and it is successfully passing all tests! It is amazing how 2 bytes kept me awake all night haha! \$\endgroup\$ – Leo oeL Feb 5 at 20:27
  • \$\begingroup\$ Subtract did the magic and I will be honest with you, this is the first time I see it. Need to dig into it and see how it works... once again, thanks for your effort and your time! \$\endgroup\$ – Leo oeL Feb 5 at 20:45
  • \$\begingroup\$ @LeooeL No problem! I found subtract in this list of numpy functions, for reference. \$\endgroup\$ – FryAmTheEggman Feb 5 at 20:56
7
\$\begingroup\$

Use Python 2's arg unpacking: 124 bytes

from math import*;circleIntersection=lambda(v,w),(x,y),r:r*r*(lambda h:h<1and acos(h)-h*(1-h*h)**.5)(hypot(v-x,w-y)/r/2)//.5

Try it online!

Python 2 has parameter unpacking, allowing the input point arguments to be taken directly as pairs (v,w) and (x,y), where the input lists like [0,0] and [0,10] will be unpacked into the respective variables. This feature was removed in Python 3 -- a pity, in my opinion, since the old syntax seems more readable. But, the site has a Python 2 option, and nothing in your solution relies on Python 3.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Works like a charm for now as sooner or later they plan to remove Python 2 from their platform. Thank you once again ^_^ \$\endgroup\$ – Leo oeL Feb 6 at 0:36
  • \$\begingroup\$ This was actually the first thing I tried, but I assumed when I got a syntax error that I had just misremembered where you were allowed to do that. Thanks for finding it and explaining what had happened! \$\endgroup\$ – FryAmTheEggman Feb 6 at 6:03
5
\$\begingroup\$

Use def: 125 bytes

You have a classic dilemma of wanting to assign to a variable without losing the brevity of a lambda function. Your method of using an inner lambda for assignment is one solution. The problem site not having Python 3.8 means the walrus operator is unfortunately off limits.

But, it's shorter here to just use def and suck up the bytes of writing out return. This is generically a 7-byte cost according to this reference. Within the def, we can just write the statement h= to do the assignment. We put everything on one line separated with ; to avoid needing to indent.

125 bytes

from math import*
def circleIntersection(a,b,r):h=hypot(b[0]-a[0],b[1]-a[1])/r/2;return(h<1and acos(h)-h*(1-h*h)**.5)*r*r//.5

Try it online! (vs original)

I've changed the multiplication order from return r*r*(...)//.5 to return(...)*r*r//.5 to cut the space after return.

There are likely other byte saves, including FryAmTheEggman's switch to numpy, but refactoring from lambda to def is already enough to get below 128 bytes.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Hello, @xnor and thank you for your time. Your idea is brilliant and so well thought, however, I do apologise as I didn't mention that another requirement is that it has to be a one-liner. Will update the description asap. \$\endgroup\$ – Leo oeL Feb 6 at 0:04
  • 2
    \$\begingroup\$ @LeooeL Ah, too bad. When you say one line, do you mean literally that the code needs to not contain any newlines? Like, if there weren't an import, would a def like here work? \$\endgroup\$ – xnor Feb 6 at 0:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.