Fortran n=17, 323 s
C Find strictly convex polygons of minimum area on square grid
C Author: Hugo Pfoertner, 2018
implicit integer (A-Z)
parameter (vlarge=2147483647)
C Number of vertices
parameter (n=17)
C Number of coordinate pairs to be used as polygon edges,
C read from external list
parameter (ms=2048)
C Length of lists for segments, coordinates, areas
C Must be extended for n>nm
parameter (nm=25)
dimension xd(ms), yd(ms), x(nm), y(nm), a(nm), as(nm), nn(nm)
equivalence
& (n1,nn(1)),(n2,nn(2)),(n3,nn(3)),(n4,nn(4)),(n5,nn(5)),
& (n6,nn(6)),(n7,nn(7)),(n8,nn(8)),(n9,nn(9)),(n10,nn(10)),
& (n11,nn(11)),(n12,nn(12)),(n13,nn(13)),(n14,nn(14)),
& (n15,nn(15)),(n16,nn(16)),(n17,nn(17)),(n18,nn(18)),
& (n19,nn(19)),(n20,nn(20)),(n21,nn(21)),(n22,nn(22)),
& (n23,nn(23)),(n24,nn(24)),(n25,nn(25))
C Number of polygons with minimal area found
integer*8 count
C File names of external files, command argument
character*15 bspirx, bspiry, carg
C Progress indicator line
character pline*150
C CPU time
real cptime
C function to calculate d^2 of enclosing circle,
C to be replaced by function encirc
C if exact enclosing circle is needed
integer diamet
external diamet
C variables needed in diameter calculation
doubleprecision xc, yc, rc, d, diamin, diamax
C Some choices for OEIS files describing spirals
C Square spiral
C data bspirx, bspiry /'b174344.txt', 'b274923.txt' /
C Circular rings (Sloane)
C data bspirx, bspiry /'b283307.txt', 'b283308.txt' /
C Circular rings
data bspirx, bspiry /'../b305575.txt', '../b305576.txt' /
C Statement function: Double area of triangle
triar(x1,y1, x2,y2, x3,y3) =
& x1*(y2-y3) + x2*(y3-y1) + x3*(y1-y2)
C Progress indicator
pline = '....+....1....+....2....+....3....+....4....+....5' //
& '....+....6....+....7....+....8....+....9....+....A' //
& '....+....B....+....C....+....D....+....E....+....F'
C
C read external files with coordinates of points in spiral
C X
open ( unit=10, file=bspirx, status='old',
& form='formatted', iostat=ios)
if ( ios .ne. 0 ) stop 'Error opening bfile spiral x'
do 1 i = 1, ms
read (10,*,end=999) k, xd(i)
1 continue
close (unit=10)
C Y
open ( unit=10, file=bspiry, status='old',
& form='formatted', iostat=ios)
if ( ios .ne. 0 ) stop 'Error opening bfile spiral y'
do 2 i = 1, ms
read (10,*,end=999) k, yd(i)
2 continue
close (unit=10)
C
C For convenience: write first nonnegative (x,y) pairs to terminal
do 3 i = 2, 120
if (xd(i) .ge. 0 .and. yd(i) .ge. 0 ) write (*,1003)i,xd(i),yd(i)
1003 format ( 3 i3 )
3 continue
C
C preset minimum area
ami = vlarge
C if an upper bound is known: least area + 1
C ami = 183
C preset diameter extreme values
diamin = 1.0D20
diamax = 0.0D0
C Total number of polygons with same minimum area
count = 0
C get number of list items from first parameter of program call
CALL get_command_argument(1, carg)
read (carg, *) m
if ( m .gt. ms ) stop 'm exceeds length of segment list'
write (*,*) 'Segments used:', m
C get index of
CALL get_command_argument(2, carg)
read (carg, *) n2first
if ( xd(n2first) .lt. 0 .or. yd(n2first) .lt. 0 )
& stop 'illegal negative start step'
C limit range, assuming first coordinate pair on files is (0,0)
n2first = max(2,min (m,n2first))
write (*,*) 'First start step:', xd(n2first), yd(n2first)
C
C Start building the polygon
C
C Freeze first point
x(1) = 0
y(1) = 0
n1 = 0
C
C loop over second point
do 20 n2 = n2first, m
L = 2
x(L) = xd(nn(L))
y(L) = yd(nn(L))
C
C Limit to angle 0 <= Pi/2
if ( x(L) .lt. 0 .or. y(L) .lt. 0 .or. y(L) .gt. x(L) ) goto 20
C
C optional: Exclusion of extremely long segments
C if (dble(x(L)**2 + y(L)**2) .gt. diamin) goto 20
write (*,1006) count, n2, xd(nn(L)), yd(nn(L))
1006 format (/,'Min area polygons found so far: ', i0,
& ', next n2 = ', i0, ' (',i0,',',i0,')')
C if wanted: progress indicator
WRITE(*, 1004, ADVANCE='NO') pline(1:1)
1004 format (A1)
C loop over third point
do 30 n3 = 2, m
C Progress indicator
WRITE(*, 1004, ADVANCE='NO') pline(n3:n3)
L = 3
x(L) = x(L-1) + xd(nn(L))
y(L) = y(L-1) + yd(nn(L))
a(L) = triar (x(1),y(1), x(L-1),y(L-1), x(L),y(L))
as(L) = a(L)
if ( a(L) .le. 0 ) goto 30
if ( a(L) .gt. ami-n+L ) goto 30
C The following blocks are repeated in code with adaptation for
C current segment number (code easily generated by a small script
C or preprocessor)
do 40 n4 = 2, m
L = 4
C try extension by segment from list
x(L) = x(L-1) + xd(nn(L))
y(L) = y(L-1) + yd(nn(L))
C area contribution
a(L) = triar (x(1),y(1), x(L-1),y(L-1), x(L),y(L))
if ( a(L) .le. 0 ) goto 40
C left turn?
if (triar(x(L-2),y(L-2), x(L-1),y(L-1), x(L),y(L)) .le. 0) goto 40
C start point still left of straight line through endpoints of segment?
if ( triar(x(1),y(1), x(2),y(2), x(L),y(L)) .le. 0 ) goto 40
as(L) = as(L-1) + a(L)
if ( as(L) .gt. ami-n+L ) goto 40
C
do 50 n5 = 2, m
L = 5
x(L) = x(L-1) + xd(nn(L))
y(L) = y(L-1) + yd(nn(L))
a(L) = triar ( x(1),y(1), x(L-1),y(L-1), x(L),y(L))
if ( a(L) .le. 0 ) goto 50
if (triar(x(L-2),y(L-2), x(L-1),y(L-1), x(L),y(L)) .le. 0) goto 50
if ( triar (x(L),y(L), x(1),y(1), x(2),y(2)) .le. 0 ) goto 50
as(L) = as(L-1) + a(L)
if ( as(L) .gt. ami-n+L ) goto 50
C
do 60 n6 = 2, m
L = 6
x(L) = x(L-1) + xd(nn(L))
y(L) = y(L-1) + yd(nn(L))
a(L) = triar (x(1),y(1), x(L-1),y(L-1), x(L),y(L))
if ( a(L) .le. 0 ) goto 60
if (triar(x(L-2),y(L-2), x(L-1),y(L-1), x(L),y(L)) .le. 0) goto 60
if ( triar (x(L),y(L), x(1),y(1), x(2),y(2)) .le. 0 ) goto 60
as(L) = as(L-1) + a(L)
if ( as(L) .gt. ami-n+L ) goto 60
C
do 70 n7 = 2, m
L = 7
x(L) = x(L-1) + xd(nn(L))
y(L) = y(L-1) + yd(nn(L))
a(L) = triar (x(1),y(1), x(L-1),y(L-1), x(L),y(L))
if ( a(L) .le. 0 ) goto 70
if (triar(x(L-2),y(L-2), x(L-1),y(L-1), x(L),y(L)) .le. 0) goto 70
if ( triar (x(L),y(L), x(1),y(1), x(2),y(2)) .le. 0 ) goto 70
as(L) = as(L-1) + a(L)
if ( as(L) .gt. ami-n+L ) goto 70
C
do 80 n8 = 2, m
L = 8
x(L) = x(L-1) + xd(nn(L))
y(L) = y(L-1) + yd(nn(L))
a(L) = triar (x(1),y(1), x(L-1),y(L-1), x(L),y(L))
if ( a(L) .le. 0 ) goto 80
if (triar(x(L-2),y(L-2), x(L-1),y(L-1), x(L),y(L)) .le. 0) goto 80
if ( triar (x(L),y(L), x(1),y(1), x(2),y(2)) .le. 0 ) goto 80
as(L) = as(L-1) + a(L)
if ( as(L) .gt. ami-n+L ) goto 80
C
do 90 n9 = 2, m
L = 9
x(L) = x(L-1) + xd(nn(L))
y(L) = y(L-1) + yd(nn(L))
a(L) = triar (x(1),y(1), x(L-1),y(L-1), x(L),y(L))
if ( a(L) .le. 0 ) goto 90
if (triar(x(L-2),y(L-2), x(L-1),y(L-1), x(L),y(L)) .le. 0) goto 90
if ( triar (x(L),y(L), x(1),y(1), x(2),y(2)) .le. 0 ) goto 90
as(L) = as(L-1) + a(L)
if ( as(L) .gt. ami-n+L ) goto 90
C
do 100 n10 = 2, m
L = 10
x(L) = x(L-1) + xd(nn(L))
y(L) = y(L-1) + yd(nn(L))
a(L) = triar (x(1),y(1), x(L-1),y(L-1), x(L),y(L))
if ( a(L) .le. 0 ) goto 100
if (triar(x(L-2),y(L-2), x(L-1),y(L-1), x(L),y(L)) .le. 0)goto 100
if ( triar (x(L),y(L), x(1),y(1), x(2),y(2)) .le. 0 ) goto 100
as(L) = as(L-1) + a(L)
if ( as(L) .gt. ami-n+L ) goto 100
C
do 110 n11 = 2, m
L = 11
x(L) = x(L-1) + xd(nn(L))
y(L) = y(L-1) + yd(nn(L))
a(L) = triar (x(1),y(1), x(L-1),y(L-1), x(L),y(L))
if ( a(L) .le. 0 ) goto 110
if (triar(x(L-2),y(L-2), x(L-1),y(L-1), x(L),y(L)) .le. 0)goto 110
if ( triar (x(L),y(L), x(1),y(1), x(2),y(2)) .le. 0 ) goto 110
as(L) = as(L-1) + a(L)
if ( as(L) .gt. ami-n+L ) goto 110
C
do 120 n12 = 2, m
L = 12
x(L) = x(L-1) + xd(nn(L))
y(L) = y(L-1) + yd(nn(L))
a(L) = triar (x(1),y(1), x(L-1),y(L-1), x(L),y(L))
if ( a(L) .le. 0 ) goto 120
if (triar(x(L-2),y(L-2), x(L-1),y(L-1), x(L),y(L)) .le. 0)goto 120
if ( triar (x(L),y(L), x(1),y(1), x(2),y(2)) .le. 0 ) goto 120
as(L) = as(L-1) + a(L)
if ( as(L) .gt. ami-n+L ) goto 120
C
do 130 n13 = 2, m
L = 13
x(L) = x(L-1) + xd(nn(L))
y(L) = y(L-1) + yd(nn(L))
a(L) = triar (x(1),y(1), x(L-1),y(L-1), x(L),y(L))
if ( a(L) .le. 0 ) goto 130
if (triar(x(L-2),y(L-2), x(L-1),y(L-1), x(L),y(L)) .le. 0)goto 130
if ( triar (x(L),y(L), x(1),y(1), x(2),y(2)) .le. 0 ) goto 130
as(L) = as(L-1) + a(L)
if ( as(L) .gt. ami-n+L ) goto 130
C
do 140 n14 = 2, m
L = 14
x(L) = x(L-1) + xd(nn(L))
y(L) = y(L-1) + yd(nn(L))
a(L) = triar (x(1),y(1), x(L-1),y(L-1), x(L),y(L))
if ( a(L) .le. 0 ) goto 140
if (triar(x(L-2),y(L-2), x(L-1),y(L-1), x(L),y(L)) .le. 0)goto 140
if ( triar (x(L),y(L), x(1),y(1), x(2),y(2)) .le. 0 ) goto 140
as(L) = as(L-1) + a(L)
if ( as(L) .gt. ami-n+L ) goto 140
C
do 150 n15 = 2, m
L = 15
x(L) = x(L-1) + xd(nn(L))
y(L) = y(L-1) + yd(nn(L))
a(L) = triar (x(1),y(1), x(L-1),y(L-1), x(L),y(L))
if ( a(L) .le. 0 ) goto 150
if (triar(x(L-2),y(L-2), x(L-1),y(L-1), x(L),y(L)) .le. 0)goto 150
if ( triar (x(L),y(L), x(1),y(1), x(2),y(2)) .le. 0 ) goto 150
as(L) = as(L-1) + a(L)
if ( as(L) .gt. ami-n+L ) goto 150
C
do 160 n16 = 2, m
L = 16
x(L) = x(L-1) + xd(nn(L))
y(L) = y(L-1) + yd(nn(L))
a(L) = triar (x(1),y(1), x(L-1),y(L-1), x(L),y(L))
if ( a(L) .le. 0 ) goto 160
if (triar(x(L-2),y(L-2), x(L-1),y(L-1), x(L),y(L)) .le. 0)goto 160
if ( triar (x(L),y(L), x(1),y(1), x(2),y(2)) .le. 0 ) goto 160
as(L) = as(L-1) + a(L)
if ( as(L) .gt. ami-n+L ) goto 160
C
do 170 n17 = 2, m
L = 17
x(L) = x(L-1) + xd(nn(L))
y(L) = y(L-1) + yd(nn(L))
a(L) = triar (x(1),y(1), x(L-1),y(L-1), x(L),y(L))
if ( a(L) .le. 0 ) goto 170
if (triar(x(L-2),y(L-2), x(L-1),y(L-1), x(L),y(L)) .le. 0)goto 170
if ( triar (x(L),y(L), x(1),y(1), x(2),y(2)) .le. 0 ) goto 170
as(L) = as(L-1) + a(L)
C in last line of repeated code part n=L
c if ( as(L) .gt. ami-n+L ) goto 170
C example how to continue for n>17
c do 180 n18 = 2, m
c L = 18
c x(L) = x(L-1) + xd(nn(L))
c y(L) = y(L-1) + yd(nn(L))
c a(L) = triar (x(1),y(1), x(L-1),y(L-1), x(L),y(L))
c if ( a(L) .le. 0 ) goto 180
c if (triar(x(L-2),y(L-2), x(L-1),y(L-1), x(L),y(L)) .le. 0)goto 180
c if ( triar (x(L),y(L), x(1),y(1), x(2),y(2)) .le. 0 ) goto 180
c as(L) = as(L-1) + a(L)
c if ( as(L) .gt. ami-n+L ) goto 180
C ...
C ...
C
C Update minimum
if ( as(L) .lt. ami ) then
count = 0
ami = as(L)
C
C alternative with exact enclosing circle
C call encirc ( 1, n, x, y, xc, yc, rc )
C diamin = 4*rc**2
call cpu_time (cptime)
C type cast assumed to work diamin (real*8) = diamet (integer)
diamin = diamet (n,x,y)
write (*,1000) n, as(L), diamin, (x(k),y(k),k=1,n)
1000 format (/,i2, 1X, i0, f14.6, 2x, 25('(',i0,',',i0,')',:,',') )
write (*,1001) cptime, nn(2:n)
1001 format ( F12.4,' s: ',i0, 25(1X,i0,:) )
endif
C
C check for multiple solutions with same mimimum area
if ( as(L) .eq. ami ) then
call cpu_time (cptime)
d = diamet(n,x,y)
C call encirc ( 1, n, x, y, xc, yc, rc )
C d = 4*rc**2
count = count + 1
if ( d .lt. diamin ) then
diamin = d
write (*,1000) n, as(L), diamin, (x(k),y(k),k=1,n)
write (*,1001) cptime, nn(2:n)
endif
if ( d .gt. diamax ) then
diamax = d
write (*,1000) n, as(L), -diamax, (x(k),y(k),k=1,n)
write (*,1001) cptime, nn(2:n)
endif
endif
250 continue
240 continue
230 continue
220 continue
210 continue
200 continue
190 continue
180 continue
170 continue
160 continue
150 continue
140 continue
130 continue
120 continue
110 continue
100 continue
90 continue
80 continue
70 continue
60 continue
50 continue
40 continue
30 continue
20 continue
C
call cpu_time ( cptime )
write (*,1007) cptime, count
1007 format (/,'CPU time: ', f12.4, ' s',/,
& 'Number of polygons with minimum area: ', i0)
999 continue
end
C
C Maximum of mutual point distance sufficient as an estimate.
C Exact enclosing circle needs a more sophisticated method,
C e.g., Welz's algorithm
integer function diamet (n, x, y)
integer n, x(*), y(*)
id = 0
do 10 i = 1, n-1
do 20 j = i+1, n
jd = (x(i)-x(j))**2 + (y(i)-y(j))**2
id = max (id,jd)
20 continue
10 continue
diamet = id
end
This is essentially the first version of the program I started with in 2018 just for illustration with no tweaks. It's more to have a place to make some general notes on pitfalls of this problem that I think are important. When those are scattered in comments on individual answers, it's hard to keep track of.
The program only handles the case n=17, which I chose because it is the smallest n without a proof of optimality. In order to run the program, 2 external files are required, namely the b-files of the OEIS sequences A305575 and A305576, which should be one directory above the program.
A typical output looks like this:
.\17.exe 56 1
2 1 0
3 0 1
...
114 6 1
115 1 6
Segments used: 56
First start step: 1 0
Min area polygons found so far: 0, next n2 = 2 (1,0)
...
17 185 373.000000 (0,0),(1,0),(1,1),(0,3),(-1,4),(-4,6),(-6,7),(-9,8),(-13,9),(-14,9),(-16,8),(-17,7),(-17,6),(-16,5),(-13,3),(-11,2),(-8,1)
0.0469 s: 2 3 16 7 41 17 33 53 4 18 8 5 9 45 21 37
17 185 -373.000000 (0,0),(1,0),(1,1),(0,3),(-1,4),(-4,6),(-6,7),(-9,8),(-13,9),(-14,9),(-16,8),(-17,7),(-17,6),(-16,5),(-13,3),(-11,2),(-8,1)
0.0469 s: 2 3 16 7 41 17 33 53 4 18 8 5 9 45 21 37
17 178 370.000000 (0,0),(1,0),(1,1),(0,3),(-2,5),(-5,7),(-7,8),(-10,9),(-14,10),(-15,10),(-16,9),(-16,8),(-15,6),(-14,5),(-11,3),(-9,2),(-6,1)
0.0938 s: 2 3 16 23 41 17 33 53 4 8 5 20 9 45 21 37
17 159 306.000000 (0,0),(1,0),(1,1),(0,3),(-2,6),(-3,7),(-5,8),(-8,9),(-12,10),(-13,10),(-14,9),(-14,8),(-13,6),(-12,5),(-9,3),(-7,2),(-4,1)
0.1094 s: 2 3 16 40 7 17 33 53 4 8 5 20 9 45 21 37
17 159 296.000000 (0,0),(1,0),(1,1),(0,3),(-2,6),(-3,7),(-6,9),(-8,10),(-11,11),(-12,11),(-13,10),(-13,9),(-12,7),(-10,4),(-9,3),(-7,2),(-4,1)
0.1094 s: 2 3 16 40 7 41 17 33 4 8 5 20 44 9 21 37
17 157 265.000000 (0,0),(1,0),(1,1),(0,4),(-1,6),(-3,9),(-4,10),(-6,11),(-9,12),(-10,12),(-11,11),(-11,10),(-10,7),(-9,5),(-8,4),(-5,2),(-3,1)
0.2969 s: 2 3 32 16 40 7 17 33 4 8 5 36 20 9 45 21
.+.
17 157 232.000000 (0,0),(1,0),(2,1),(2,2),(1,4),(0,5),(-3,7),(-5,8),(-8,9),(-9,9),(-11,8),(-12,7),(-12,6),(-11,4),(-10,3),(-8,2),(-5,1)
0.9844 s: 2 6 3 16 7 41 17 33 4 18 8 5 20 9 21 37
17 151 202.000000 (0,0),(1,0),(2,1),(2,2),(1,4),(-1,7),(-2,8),(-5,10),(-7,11),(-8,11),(-9,10),(-10,8),(-10,7),(-9,5),(-8,4),(-5,2),(-3,1)
1.0469 s: 2 6 3 16 40 7 41 17 4 8 19 5 20 9 45 21
17 151 193.000000 (0,0),(1,0),(2,1),(2,2),(1,5),(0,7),(-1,8),(-3,9),(-6,10),(-7,10),(-9,9),(-10,8),(-10,7),(-9,5),(-8,4),(-5,2),(-3,1)
1.1406 s: 2 6 3 32 16 7 17 33 4 18 8 5 20 9 45 21
17 151 137.000000 (0,0),(1,0),(2,1),(3,3),(3,4),(2,7),(1,9),(0,10),(-2,11),(-3,11),(-5,10),(-6,9),(-7,7),(-7,6),(-6,4),(-5,3),(-2,1)
2.1562 s: 2 6 15 3 32 16 7 17 4 18 8 19 5 20 9 45
...1....+....2....+....3....+....4....+....5....+.
Min area polygons found so far: 24, next n2 = 6 (1,1)
....+....1....+....2....+....3....+....4....+....5....+.
Min area polygons found so far: 48, next n2 = 10 (2,0)
....+....1....+....2....+....3....+....4....+....5....+.
Min area polygons found so far: 48, next n2 = 14 (2,1)
....+....1....+....2....+....3....+....4....+....5....+.
Min area polygons found so far: 65, next n2 = 22 (2,2)
....+....1....+....2....+....3....+....4....+....5....+.
Min area polygons found so far: 65, next n2 = 26 (3,0)
....+....1....+....2....+....3....+....4....+....5....+.
Min area polygons found so far: 65, next n2 = 30 (3,1)
....+....1....+....2....+....3....+....4....+....5....+.
Min area polygons found so far: 70, next n2 = 38 (3,2)
....+....1....+....2....+....3....+....4....+....5....+.
Min area polygons found so far: 76, next n2 = 46 (4,0)
....+....1....+....2....+....3....+....4....+....5....+.
Min area polygons found so far: 76, next n2 = 50 (4,1)
....+....1....+....2....+....3....+....4....+....5....+.
CPU time: 323.0781 s
Number of polygons with minimum area: 76
For n=17 there is not only the compact solution found by everyone, but also exotic needle-shaped solutions, like Squared diameter 1361.
This is the one I've known so far with the largest diameter. As far as I know, it has not been proven that no extreme solutions of this kind with a smaller area exist. If one could show that there are no other solutions with even greater stretching, then that would be an important step towards a proof of optimality for this n.
Heavily stretched polygons
I don't want to spoil anyone's good mood, but if you all only find the solutions that I gave 4 years ago, the doubts remain whether we are making things too easy for ourselves when searching. I have already pointed out the existence of very strongly stretched polygons with likewise minimal areas. As a test case for your programs, you can try to find at least one even slimmer solution than the following at n=19:
n=19, 2*Area=213, Diameter^2=1105
My program finds the shown and -intentionally undisclosed- slimmer solutions (squared diameters = 5*prime number, prime, ..) after about 200 s (17700 s for the prime squared diameter) using 1000 points from the spiral files.
If other programs also find these solutions, then that would increase my confidence considerably.
Update
In the meantime I have found that all of the strongly distorted and needle-shaped polygons found by my programs can be reduced to the already known slightly deformed shapes by applying shear transformations. So far I haven't found any exceptions to this observation. Apparently, allowing longer polygon sides does not bring any advantage in terms of further reducing the area. In a way, this contradicts the asymptotic elliptical shape described in the work of Bárány, I., Tokushige, N. (2004) or version without paywall with semi-axes \$a=0.003573 n^2\$ and \$b=1.656 n\$, which at \$n=27\$ gives an axis ratio of about \$1/15\$. The observed ratio of the solution with \$2*A(27)=625\$, which has meanwhile also been found by my own program, is only about \$1/2\$.
Fortran revised, n=17, 8.8 s
See Lattice-Polygons on GitHub. Another faster version exists (approx. 70% of run time), with explicit expansion of the inner loops, similar to code shown above, but the GitHub version is cleaner.