Challenge
Determine how many integer lattice points there are in an ellipse
$$\frac{x^2}{a^2} + \frac{y^2}{b^2} \leq 1$$
centered at the origin with width \2ドルa\$ and height \2ドルb\$ where integers \$a, b > 0\$ .
Input
The Semi-major \$a\$ and Semi-minor \$b\$ axes.
Output
Number of interior and boundary points.
Example
Ellipse plot showing \$a=5\$ and \$b=3\$ with \41ドル\$ blue interior and \4ドル\$ red boundary points.
Input
\5ドル\$,\3ドル\$
Output
\41ドル\$,\4ドル\$
Test Cases
| a | b | Interior | Boundary |
|---|---|---|---|
| 5 | 3 | 41 | 4 |
| 5 | 15 | 221 | 12 |
| 8 | 5 | 119 | 4 |
| 8 | 1 | 15 | 4 |
| 9 | 15 | 417 | 4 |
| 15 | 15 | 697 | 12 |
| 20 | 20 | 1245 | 12 |
-
\$\begingroup\$ Is there a closed form? \$\endgroup\$qwr– qwr2024年01月18日 00:19:37 +00:00Commented Jan 18, 2024 at 0:19
-
\$\begingroup\$ I don't think there's a closed form, I got the idea from the Gauss Circle Problem which there's no formula to find the number of lattice points and involves some enumeration. \$\endgroup\$vengy– vengy2024年01月18日 00:23:00 +00:00Commented Jan 18, 2024 at 0:23
-
1\$\begingroup\$ I think it's more interesting to have separate questions for interior points and boundary points, as boundary points is a Pythagorean triple like \$(bx)^2 + (ay)^2 = (ab)^2\$. \$\endgroup\$qwr– qwr2024年01月18日 00:24:13 +00:00Commented Jan 18, 2024 at 0:24
-
\$\begingroup\$ For the Gauss circle problem, the number of interior and boundary points can be computed with Jacobi's two square theorem and the factorization of the radius length. \$\endgroup\$qwr– qwr2024年01月18日 00:26:23 +00:00Commented Jan 18, 2024 at 0:26
-
\$\begingroup\$ Oh, I'll have to take a closer look and hopefully try to understand it. ;) Thanks. \$\endgroup\$vengy– vengy2024年01月18日 00:38:06 +00:00Commented Jan 18, 2024 at 0:38
20 Answers 20
R, 76 bytes
function(a,b,z=c(a,b)^-2%*%t(expand.grid(-a:a,-b:b)^2))c(sum(z<1),sum(z==1))
May have issues due to numerical precision. Naive algorithm: loop over all lattice points in the \2ドルa \times 2b\$ grid centered around the origin, and count the points with the requisite relationship.
R, 85 bytes
function(a,b,p=a^2*b^2,z=c(b,a)^2%*%t(expand.grid(-a:a,-b:b)^2))c(sum(z<p),sum(z==p))
More precise.
R, (削除) 68 (削除ここまで) 66 bytes
\(a,b,y=b/a*(a^2-(-a:a)^2)^.5)c(B<-sum(!y%%1)-1,sum(y%/%1+.5)-B)*2
Total points of boundary plus interior is (up to precision error) $$\sum_{-a\le x \le a} \# \left\{ y \in \mathbb Z : |y| \le (b/a) \sqrt{a^2-x^2} \right\}$$
$$ = \sum_{-a \le x \le a} \left( 2 \left\lfloor (b/a) \sqrt{a^2-x^2} \right\rfloor + 1 \right)$$
\$B\$ counts boundary points, subtracting over-counted cases \$|y| = a\$.
y=b*(1-(-a:a/a)^2)^.5 actually fails the test cases due to precision errors.
-2 bytes by rearranging some terms, credit Giuseppe.
-
\$\begingroup\$ You can save another 2 bytes by reversing the order of output and a little manipulation; Attempt it online \$\endgroup\$Giuseppe– Giuseppe2024年01月18日 19:00:53 +00:00Commented Jan 18, 2024 at 19:00
Jelly, 13 bytes
rNŒp2÷2§’ṠĠẈṖ
A monadic link that takes a pair of integers and returns a pair of integers. Works perfectly for all of the examples, but there is a theoretical risk of floating point inaccuracy.
Explanation
rN | Range from a to -a and b to -b
Œp | Cartesian product
2 | Squared
÷2 | Divide by a ** 2, b ** 2
§ | Sum inner lists
’ | Subtract 1
Ṡ | Signs
Ġ | Indices of like items in groups
Ẉ | Lengths
Ṗ | Remove last (those outside the border)
Alternative Jelly, 16 bytes
rNŒp2Uḋ2_2P$ṠĠẈṖ
This version only uses integer arithmetic so should always be accurate even for large inputs (though like the other answer has \$O(m \times n)\$ complexity).
-
1\$\begingroup\$ @JonathanAllan thanks. The test cases were confusing me! \$\endgroup\$Nick Kennedy– Nick Kennedy2024年01月16日 23:29:21 +00:00Commented Jan 16, 2024 at 23:29
Google Sheets, 84 bytes
Assuming \$a\$ is in A1 and \$b\$ is in B1.
=SORT(COUNTIF(SEQUENCE(2*A1+1,1,-A1)^2/A1^2+SEQUENCE(1,2*B1+1,-B1)^2/B1^2,{"<1",1}))
=SORT(COUNTIF(MAKEARRAY(2A1+1,2B1+1,LAMBDA(x,y,SUMSQ((x-A1-1)/A1,(y-B1-1)/B1))),{"<1",1})) (削除ここまで)
=LET(a,A1,b,B1,SORT(COUNTIF(SEQUENCE(2a+1,1,-a)^2/a^2+SEQUENCE(1,2b+1,-b)^2/b^2,{"<1",1}))) (削除ここまで)
PARI/GP, 64 bytes
f(a,b)=[1+vecsum(v=2*Vec(qfrep([a,0;0,b]^2,a^2*b^2)))-t=v[#v],t]
Yes, PARI/GP has a built-in for this.
qfrep(q,B,{flag=0}): vector of (half) the number of vectors of norms from1toBfor the integral and definite quadratic formq. If flag is1, count vectors of even norm from1to2B.
Google Sheets, 88 bytes
=frequency(sort(sequence(2*A1+1,1,-A1)^2/A1^2+sequence(1,2*B1+1,-B1)^2/B1^2),{1-9^-9,1})
Put \$a\$ in cell A1, \$b\$ in cell B1 and the formula in cell C1. The output is in cells C1:C2 with residue in C3.
Generates integer \$x\$ and \$y\$ sequences for lattice points in a rectangle that bounds the ellipse and calculates the distance from origin at each point. Uses frequency() to find points within and on the ellipse. The residue shows points within the bounding rectangle but outside the ellipse. Uses sort() but as an array enabler only.
Earlier proof-of-concept version to confirm that this is easily solvable in a spreadsheet (160 bytes golfed, 279 ungolfed):
=let(a, A1, b, B1, r,
map(sequence(2 * a + 1, 1, -a), lambda(x,
map(sequence(1, 2 * b + 1, -b), lambda(y, let(
d, x^2 / a^2 + y^2 / b^2,
ifs(
d = 1, 2,
d < 1, 1,
1, 0
)
)))
)),
{ countif(r, 1), countif(r, 2) }
)
-
2\$\begingroup\$ -8
=let(a,A1,b,B1,r,tocol(map(sequence(2*a+1,1,-a),lambda(x,map(sequence(1,2*b+1,-b),lambda(y,let(c,x^2/a^2+y^2/b^2,ifs(c=1,2,c<1,1))))))),sort(countif(r,{1,2})))\$\endgroup\$z..– z..2024年01月15日 23:56:47 +00:00Commented Jan 15, 2024 at 23:56 -
1\$\begingroup\$ @z.. thanks. This was just to confirm that a simple algorithm gets the correct result. The formula can be golfed quite a bit (as your answer shows.) \$\endgroup\$doubleunary– doubleunary2024年01月16日 09:05:57 +00:00Commented Jan 16, 2024 at 9:05
05AB1E, (削除) 23 (削除ここまで) 22 bytes
Ýí€ûnR**`âOIPns-.±{Åγ¦
Outputs the pair in reversed order, so \$[boundaryCount, interiorCount]\$.
Try it online or verify all test cases.
Explanation:
Ý # Convert both values in the (implicit) input-pair to [0,value]-ranged
# lists
í # Reverse each inner list
€û # Palindromize each inner list
n # Square each inner value
R # Reverse the list
** # Multiply each to the (implicit) input-pair twice
` # Pop and push both lists separately to the stack
â # Create all possible pairs using the cartesian product
O # Sum each inner pair together
IP # Push the product of the input-pair
n # Square that
s- # Subtract the sums of the list from that value
.± # Get the sign (-1 if negative; 0 if 0; 1 if positive) of each
{ # Sort these -1s, 0s, and 1s
Åγ # Then run-length encode it, pushing list [-1,0,1] (which we won't use)
# and the list of lengths
¦ # Remove the first value (for the amount of -1s)
# (after which the resulting pair is output implicitly)
Charcoal, 46 bytes
×ばつθη2+ικI⟦ΣEυ›ι0Noυ0
Try it online! Link is to verbose version of code. Explanation: Similar approach to @vengy's C answer.
NθNη
Input a and b.
×ばつη...·±θθ2
Loop x from -a to a inclusive.
×ばつθ...·±ηη2
Loop y from -b to b inclusive.
×ばつθη2+ικ
Calculate the squared distance in from the perimeter, multiplied by a2b2.
I⟦ΣEυ›ι0Noυ0
Output the number of points for where this is positive and the number for where this is zero.
Wolfram Language(Mathematica), (削除) 135 (削除ここまで) 106 bytes
Saved 29 bytes thanks to @vengy
Here a and b both are positive integers.
Golfed version. Try it online!
f[a_,b_]:=Module[{I=0,B=0},Do[With[{t=x^2/a^2+y^2/b^2},If[t<1,I++,If[t==1,B++]]],{x,-a,a},{y,-b,b}];{I,B}]
Ungolfed version. Try it online!
CountLatticePoints[a_Integer, b_Integer] := Module[
{interiorPoints = 0, boundaryPoints = 0},
Sum[
If[IntegerQ[x] && IntegerQ[y],
If[x^2/a^2 + y^2/b^2 < 1, interiorPoints++,
If[x^2/a^2 + y^2/b^2 == 1, boundaryPoints++]
]
],
{x, -a, a}, {y, -b, b}
];
{interiorPoints, boundaryPoints}
]
Python 3.8 (pre-release), 110 bytes
lambda a,b,q=2:[sum(0>(t:=x*x*b*b+y*y*a*a-a*a*b*b)or(q:=q+(t<1))*0for x in range(-a,a)for y in range(-b,b)),q]
Consider four quarter
-
\$\begingroup\$ Oops...my inefficient answer considered four quarters. \$\endgroup\$vengy– vengy2024年01月15日 23:25:30 +00:00Commented Jan 15, 2024 at 23:25
-
-
\$\begingroup\$ @vengy 4x slower doesn't matter at all. Seems yours is shorter 4 quarter \$\endgroup\$l4m2– l4m22024年01月16日 00:29:10 +00:00Commented Jan 16, 2024 at 0:29
APL+WIN, 65 bytes
Prompts for a followed by b and outputs boundary the interior:
((×ばつa)×ばつ⌊n)×ばつ ̄1++/0=1|n×ばつ(1-(((⌽⍳a),0,⍳a)*2)÷(a←⎕)*2)*.5
Python 2, 96 bytes
lambda a,b:divmod(sum((4j,4,0)[cmp(a*b,abs(x/a*a+x%a*b*1j))]for x in range(a*b+1))-2*a-2*b+1,1j)
Returns boundary,interior. Test bed borrowed from @l4m2 and modified. Testing code prettifies integer valued complex numbers into actual integers.
How?
Uses complex numbers for two purposes:
abs with complex argument for Euclidean norm
as a means of 2d vector summation without importing any libraries. Special Python 2 semantics of div and mod for complex operands is used to recover real and imaginary parts after summing.
Pyth, 20 bytes
hMlBPx1Sm.acVdQ*Fm}_
Takes input in the form [a, b] and outputs as [interior, boundary].
Explanation
# implicitly assign Q = eval(input())
hMlBPx1Sm.acVdQ*Fm}_ddQ # implicitly add ddQ
m Q # map lambda d over Q
}_dd # inclusive_range(-d, d)
*F # cartesian product of the two ranges
m # map over lambda d
cVdQ # vectorized divide d by Q
.a # vector size
S # sort
x1 # get list of indices where 1 appears
P # remove the last element
lB # bifurcate over length
hM # map to head, this will get the first element for the list and add one for the integer
Excel VBA, 93 Bytes
An immediate window function that takes input \$a\$ from [A1] and \$b\$ from [B1] of the activesheet object and outputs to the immediate window console.
Assumes a clean console environment. Maybe re-run after executing u=0:v=0.
Note: This solution has been restricted to 32-Bit versions of Excel VBA as ^ is the LongLong type literal in 64-Bit versions
For x=[-A1]To[A1]:For y=[-B1]To[B1]:t=x^2/[A1^2]+y^2/[B1^2]:u=u-(t<1):v=v-(t=1):Next y,x:?u,v
Commented Version
For x=[-A1]To[A1] '' iter over x axis
For y=[-B1]To[B1] '' iter over y axis
t=x^2/[A1^2]+y^2/[B1^2] '' calc lattice point sumsq
u=u-(t<1) '' incr. internal point count if needed
v=v-(t=1) '' incr. boundary point count if needed
Next y,x '' loop
?u,v '' print point counts
C (gcc), (削除) 133 (削除ここまで) 121 bytes
-12 thanks to @Neil
i,x,y,l,B;e(a,b){i=B=0;for(x=-a;x<=a;x++)for(y=-b;y<=b;y++)l=a*a*b*b-x*x*b*b-y*y*a*a,l?i+=l>0:B++;printf("%d %d\n",i,B);}
-
1
-
-
-
2\$\begingroup\$ Building on @l4m2 113 bytes \$\endgroup\$ceilingcat– ceilingcat2024年01月16日 16:16:30 +00:00Commented Jan 16, 2024 at 16:16
Uiua, (削除) 43 (削除ここまで) 41 bytes
Returns the number of interior points on the top of the stack, and the number of boundary points on the bottom.
∩/+<1:=1./+n×ばつ2..⊂
∩/+<1:=1./+n×ばつ2..⊂
×ばつ2..⊂ # generates a point array spanning the [-a,a×ばつ[-b,b] rectangle,
# we also leave a copy of a and b to divide the coordinates later
÷: # divide each x coordinate by a and each y by b
n2 # square each coordinate
/+ # sum each coordinate pair
. # create a copy of the sums so we can check two conditions (interior and boundary)
=1 # boundary check
<1: # the interior point check
∩/+ # since the checks return boolean masks, sum them to get a point count.
-
1
-
\$\begingroup\$ Oh, completely missed that feature, I'll edit my submission with your link! \$\endgroup\$Maxim Khanov– Maxim Khanov2024年01月18日 22:21:17 +00:00Commented Jan 18, 2024 at 22:21
-
1
-
1\$\begingroup\$ Removing the trailing fix (¤), since join doesn't need array args. -2 bytes. \$\endgroup\$Maxim Khanov– Maxim Khanov2024年01月19日 04:36:21 +00:00Commented Jan 19, 2024 at 4:36
APL(NARS), 40 chars
+/ ̈0(>,=)⊂{+/ ̄1,2*⍨⍵÷k} ̈,(⊂k+1)×ばつk←⎕
How to use and test:
+/ ̈0(>,=)⊂{+/ ̄1,2*⍨⍵÷k} ̈,(⊂k+1)×ばつk←⎕
⎕:
5 3
41 4
{+/ ̈0(>,=)⊂{+/ ̄1,2*⍨⍵÷k} ̈,(⊂k+1)×ばつk←⍵} ̈(5 3)(5 15)(8 5)(8 1)(9 15)(15 15)(20 20)
41 4 221 12 119 4 15 4 417 4 697 12 1245 12
64-bit Aarch64 assembly, 84 bytes / 21 insns
f:
mov x0, xzr
mov x1, xzr
fneg d2, d0 // x = -a
fneg d3, d1 // y = -b
fmov d4, #1.0
fmul d5, d0, d0 // a^2
fmul d6, d1, d1 // b^2
1:
fmul d7, d3, d3 // y^2
fdiv d7, d7, d6 // y^2/b^2
fdiv d16, d2, d5 // x /a^2
fmadd d16, d16, d2, d7 // x/a^2 * x + y^2/b^2
fcmp d16, d4
cinc x0, lt
cinc x1, eq
fadd d2, d2, d4
fcmp d2, d0
b.le 1b
fneg d2, d0
fadd d3, d3, d4
fcmp d3, d1
b.le 1b
ret
This is a crude, brute-force search from (-a, -b) to (a, b) checking whether the lattice points match. It's not well-optimised, but I don't really see a better way out of it. It expects a, b as doubles in d0, d1 and (hopefully*) returns the number of points on the interior in x0 and on the perimiter in x1. Its signature would look something like struct answer { uint64_t interior; uint64_t perimiter }; extern answer f(double, double);
It should* work for a reasonable range of input numbers.
Notes:
- *I haven't tested it, but the compiler emits what looks like closely enough similar code at a glance, but it wastes instructions setting up temporary registers for the return values, and doesn't perform a
fmaddfuse instead choosing to just multiply and add separately (why? this is a conceptually pretty trivial optimisation that both GCC and Clang fail to do even on-Os)
Explore related questions
See similar questions with these tags.