東京大学 情報理工学系研究科 創造情報学専攻 2013年8月実施 プログラミング
Author
Description
日本語(原巻に基づく独立した題意要約)
大学公式 PDF の保存版に基づく独立した題意要約(逐語転載ではない)。
に対する格子点集合を とし、領域 内にあるその点の個数を とする。領域の境界も含める。
例えば である。
- 浮動小数点数 を入力として、 を求めるプログラムを作る。
- 同じく を入力し、 を計算する。
- 頂点 の正三角形を とする。各辺を三等分し、中央の三分の一を底辺とする正三角形を外側に付け、その底辺を除く操作を全辺に施す。この操作を 回行った閉領域を とする。 の面積を浮動小数点数で出力する。
- 正整数 を入力し、 の面積を浮動小数点数で計算する。
- を入力し、 を計算する。
- と正整数 を入力し、 を計算する。
题目描述
令 表示格点集合 中落在区域 内的点数,边界上的点也计入。考虑正方形 和内切圆盘 ,例如 。
- 输入浮点数 ,编程计算 。
- 输入 ,计算 。
- 以顶点 的正三角形为 。每次把各边三等分,在中间一段向外添加正三角形并删除其底边;操作 次所得闭区域记为 。用浮点数输出 的面积。
- 输入正整数 ,计算 的面积。
- 输入 ,计算 。
- 输入 和正整数 ,计算 。
Kai
(1) Square count
For , each coordinate has permitted values, including zero and the upper boundary when it is a lattice coordinate. Hence
A negative spacing gives the same lattice as . For , the lattice set has just one distinct point, the origin, so . Counting integer pairs instead would incorrectly count that same point infinitely many times.
(2) Disk count
For each permitted horizontal coordinate , count integer satisfying
For decimal input , use the exact integer comparison
For fixed , take the integer square root and count
within the square's coordinate range. This is columns rather than an two-dimensional scan, apart from integer arithmetic cost. At the disk has 81 points and the requested value is .
As , the disk-to-square count ratio tends to , so the quantity in (2) tends to .
(3) and (4) Snowflake area
The initial area is . At iteration , there are new equilateral triangles, each with side . Their total area is
Summing this geometric series gives
In particular,
This formula needs constant storage and avoids generating all sides just to obtain the area.
(5) and (6) Lattice points in a snowflake
Generate the polygon in counterclockwise order. Replace each oriented side by the four edges through
The negative 60-degree turn puts the new triangle outside a counterclockwise polygon. Enumerate lattice points in the polygon's bounding box, and test membership by a winding-number algorithm that explicitly includes points on any edge. Negative lattice indices must be included: the snowflake extends below the original triangle.
Floating-point orientation tests can misclassify boundary points. The implementation below stores a polygon vertex as representing the physical point , with rational. Every construction step preserves that representation. A cross product between a rational lattice point and a polygon edge has the form ; its sign is decided exactly by signs and a comparison of with , without a tolerance. The bounding box uses exact integer square roots too.
There are sides. If the bounding box contains candidate lattice points, this direct method uses storage and arithmetic operations. It is a simple complete solution for finite input sizes; finer grids and larger may require spatial indexing or scan-line acceleration.
Complete Python program
The program uses the standard library. Decimal input is interpreted as its exact rational value, so values such as 0.1 do not acquire binary floating-point rounding before boundary tests. Parts (3) and (4) return the required floating-point areas. Save the code as koch.py; for example, python koch.py 6 --d 1 --n 2 prints 65.
from fractions import Fraction as F
from math import floor, ceil, isqrt, sqrt
def spacing(text):
# Decimal input is interpreted exactly, including scientific notation.
return abs(F(str(text)))
def count_square(d):
if d == 0:
return 1 # The set {(dp,dq)} then contains only the origin.
return (floor(F(10) / d) + 1) ** 2
def count_disk(d):
if d == 0:
return 0
a, b = d.numerator, d.denominator
limit = (10 * b) // a
total = 0
for i in range(limit + 1):
remaining = 25*b*b - (a*i - 5*b)**2
if remaining < 0:
continue
r = isqrt(remaining)
# |a*j-5*b| <= sqrt(remaining), with integer a*j-5*b.
lo = max(0, -(-(5*b-r) // a))
hi = min(limit, (5*b+r) // a)
total += max(0, hi-lo+1)
return total
def koch_vertices(n):
if n < 0:
raise ValueError('n must be nonnegative')
# Store (x,Y), meaning the physical point (x,sqrt(3)*Y).
points = [(F(0), F(0)), (F(10), F(0)), (F(5), F(5))]
for _ in range(n):
new = []
for a, b in zip(points, points[1:] + points[:1]):
dx, dy = (b[0]-a[0])/3, (b[1]-a[1])/3
first = (a[0]+dx, a[1]+dy)
# Outward = right of a counterclockwise edge: rotate by -60 deg.
tip = (first[0]+(dx+3*dy)/2, first[1]+(-dx+dy)/2)
second = (a[0]+2*dx, a[1]+2*dy)
new.extend((a, first, tip, second))
points = new
return points
def sign(v):
return (v > 0) - (v < 0)
def sign_surd(a, b):
"""Exact sign of a+b*sqrt(3), for rational a and b."""
if a == 0:
return sign(b)
if b == 0 or sign(a) == sign(b):
return sign(a)
return sign(a) * sign(a*a - 3*b*b)
def inside_closed(x, y, points):
# x,y are rational physical coordinates. Winding number includes edges.
winding = 0
for a, b in zip(points, points[1:] + points[:1]):
dx, dy = b[0]-a[0], b[1]-a[1]
cross = sign_surd(dx*y, -dx*a[1]-dy*(x-a[0]))
ya = sign_surd(y, -a[1]) # sign(y - physical a.y)
yb = sign_surd(y, -b[1])
if cross == 0 and min(a[0], b[0]) <= x <= max(a[0], b[0]) and ya*yb <= 0:
return True
if ya >= 0 and yb < 0 and cross > 0:
winding += 1
elif ya < 0 and yb >= 0 and cross < 0:
winding -= 1
return winding != 0
def floor_sqrt3(v):
"""Exact floor of sqrt(3)*v, for rational v."""
if v == 0:
return 0
if v < 0:
return -floor_sqrt3(-v)-1 # Nonzero rational times sqrt(3) is irrational.
return isqrt(3*v.numerator*v.numerator) // v.denominator
def count_koch(d, n):
points = koch_vertices(n)
if d == 0:
return int(inside_closed(F(0), F(0), points))
ilo = ceil(min(p[0] for p in points)/d)
ihi = floor(max(p[0] for p in points)/d)
jlo = -floor_sqrt3(-min(p[1] for p in points)/d)
jhi = floor_sqrt3(max(p[1] for p in points)/d)
return sum(inside_closed(d*i, d*j, points)
for i in range(ilo, ihi+1) for j in range(jlo, jhi+1))
def area_koch(n):
if n < 0:
raise ValueError('n must be nonnegative')
return 25*sqrt(3)*(8/5 - (3/5)*(4/9)**n)
def solve(part, d_text='1', n=2):
d = spacing(d_text)
if part == 1:
return count_square(d)
if part == 2:
return F(count_disk(d), 4*count_square(d))
if part == 3:
return area_koch(2)
if part == 4:
return area_koch(n)
if part == 5:
return count_koch(d, 2)
if part == 6:
return count_koch(d, n)
raise ValueError('part must be 1,...,6')
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('part', type=int)
parser.add_argument('--d', default='1')
parser.add_argument('--n', type=int, default=2)
args = parser.parse_args()
result = solve(args.part, args.d, args.n)
print(result)
if isinstance(result, F):
print(float(result))
Examples from the program:
| Quantity | Value |
|---|---|
| 121 | |
| 81 | |
| Part (2), | |
| 49 | |
| 59 | |
| 65 | |
| 67 |
The lattice counts include the boundaries.