这里有两个非常明显的观察结果:
AxB 的平方数与 BxA 的平方数相同
如果 C>B,则 AxC 的平方数大于 AxB 的平方数
鉴于这些事实,应该清楚的是:
我们只需要考虑 A≤B 的 AxB,因为如果 A≠B 我们可以将 BxA 添加到列表中
对于给定的 A 和 N,最多有一个 B 值的平方数为 N。
下面的代码是基于上面的。它依次尝试每个 AxA,每个 AxA 检查是否有一些 B≥A 产生正确的平方计数。当 AxA 的平方数超过 N 时停止。
现在,要找到 B 的正确值,需要进行一些不太明显的观察。
-
假设 AxA 的平方数为 N。那么 (A+1)x(Ax1) 的平方数为 N + (A+1)²。
证明:AxA 中的每个正方形都可以通过其左上坐标 [i, j] 及其大小s 来识别。我会把它写成 [s: *i, j]。 (这里我假设坐标是从零开始的,从上到下,从左到右。)
对于每个这样的正方形 0 ≤ i + s j + s
现在,假设我们将每个正方形 [s: i, j] 更改为基于相同坐标但具有大小的正方形一个更大的,[s+1: i, j]。这个新正方形是 (A+1)x(A+1) 中的正方形,因为 0 ≤ i + s + 1 j)。所以这个变换给了我们 A + 1 中每个大小至少为 2 的方格。我们唯一遗漏的方格是大小为 1 的方格,它们正好有 (A+1)×(A+1) 个.
-
假设 AxB 的平方数为 N,并且 B≥A。那么 Ax(B+1) 的平方数是 N + 从 1 到 A 的每个整数之和。(这些是三角形数,即 A×(A+1)/2;我认为这是众所周知的。 )
证明:Ax(B+1) 中的正方形恰好是 AxB 中的正方形加上右侧包含 Ax(B+1) 的最后一列的正方形。所以我们只需要计算这些。有一个大小为 A 的正方形,两个大小为 A-1 的正方形,三个大小为 A-2 的正方形,依此类推,直到 A 大小为 1 的正方形。
所以对于给定的 A,我们可以计算 AxA 的平方计数以及 B 每次增加时平方计数的增量。如果增量甚至除以目标计数和 AxA 计数之间的差,那么我们'找到了 AxB。
下面的程序还依赖于另一个代数恒等式,这非常简单:两个连续的三角形数之和是一个平方。通过排列两个三角形就很明显了。较大的包含正方形的对角线。这些事实用于计算 A 的下一个基值和增量。
def finds(n):
a = 1
base = 1 # Square count for AxA
inc = 1 # Difference between count(AxB) and count(AxB+1)
rects = []
while base < n:
if (n - base) % inc == 0:
rects.append((a, a + (n - base) // inc))
a += 1
newinc = inc + a
base += inc + newinc
inc = newinc
if base == n:
return rects + [(a, a)] + list(map(lambda p:p[::-1], reversed(rects)))
else:
return rects + list(map(lambda p:p[::-1], reversed(rects)))
该函数最慢的部分是在最后添加 AxB 解决方案的倒数,我这样做只是为了简化正确计算解决方案的过程。我的第一次尝试几乎快了两倍,使用循环while base <= n,然后返回rects。但它仍然足够快。
例如:
>>> finds(1000000)
[(1, 1000000), (4, 100001), (5, 66668), (15, 8338), (24, 3341),
(3341, 24), (8338, 15), (66668, 5), (100001, 4), (1000000, 1)]
>>> finds(760760)
[(1, 760760), (2, 253587), (3, 126794), (4, 76077), (7, 27172),
(10, 13835), (11, 11530), (12, 9757), (13, 8364), (19, 4010),
(20, 3629), (21, 3300), (38, 1039), (39, 988), (55, 512),
(56, 495), (65, 376), (76, 285), (285, 76), (376, 65),
(495, 56), (512, 55), (988, 39), (1039, 38), (3300, 21),
(3629, 20), (4010, 19), (8364, 13), (9757, 12), (11530, 11),
(13835, 10), (27172, 7), (76077, 4), (126794, 3), (253587, 2),
(760760, 1)]
The last one came out of this test, which took a few seconds: (It finds each successive maximum number of solutions, if you don't feel like untangling the functional elements)
>>> from functools import reduce
>>> print('\n'.join(
map(lambda l:' '.join(map(lambda ab:"%dx%d"%ab, l)),
reduce(lambda a,b: a if len(b) <= len(a[-1]) else a + [b],
(finds(n) for n in range(2,1000001)),[[(1,1)]]))))
1x1
1x2 2x1
1x5 2x2 5x1
1x8 2x3 3x2 8x1
1x14 2x5 3x3 5x2 14x1
1x20 2x7 3x4 4x3 7x2 20x1
1x50 2x17 3x9 4x6 6x4 9x3 17x2 50x1
1x140 2x47 3x24 4x15 7x7 15x4 24x3 47x2 140x1
1x280 4x29 5x20 6x15 7x12 12x7 15x6 20x5 29x4 280x1
1x770 2x257 3x129 4x78 10x17 11x15 15x11 17x10 78x4 129x3 257x2 770x1
1x1430 2x477 3x239 4x144 10x29 11x25 12x22 22x12 25x11 29x10 144x4 239x3 477x2 1430x1
1x3080 2x1027 3x514 4x309 7x112 10x59 11x50 20x21 21x20 50x11 59x10 112x7 309x4 514x3 1027x2 3080x1
1x7700 2x2567 3x1284 4x771 7x277 10x143 11x120 20x43 21x40 40x21 43x20 120x11 143x10 277x7 771x4 1284x3 2567x2 7700x1
1x10010 2x3337 3x1669 4x1002 10x185 11x155 12x132 13x114 20x54 21x50 50x21 54x20 114x13 132x12 155x11 185x10 1002x4 1669x3 3337x2 10010x1
1x34580 2x11527 3x5764 4x3459 7x1237 12x447 13x384 19x188 20x171 38x59 39x57 57x39 59x38 171x20 188x19 384x13 447x12 1237x7 3459x4 5764x3 11527x2 34580x1
1x40040 2x13347 3x6674 4x4005 7x1432 10x731 11x610 12x517 13x444 20x197 21x180 39x64 64x39 180x21 197x20 444x13 517x12 610x11 731x10 1432x7 4005x4 6674x3 13347x2 40040x1
1x100100 2x33367 3x16684 4x10011 7x3577 10x1823 11x1520 12x1287 13x1104 20x483 21x440 25x316 39x141 55x83 65x68 68x65 83x55 141x39 316x25 440x21 483x20 1104x13 1287x12 1520x11 1823x10 3577x7 10011x4 16684x3 33367x2 100100x1
1x340340 2x113447 3x56724 4x34035 7x12157 10x6191 11x5160 12x4367 13x3744 20x1627 21x1480 34x583 39x449 55x239 65x180 84x123 123x84 180x65 239x55 449x39 583x34 1480x21 1627x20 3744x13 4367x12 5160x11 6191x10 12157x7 34035x4 56724x3 113447x2 340340x1
1x760760 2x253587 3x126794 4x76077 7x27172 10x13835 11x11530 12x9757 13x8364 19x4010 20x3629 21x3300 38x1039 39x988 55x512 56x495 65x376 76x285 285x76 376x65 495x56 512x55 988x39 1039x38 3300x21 3629x20 4010x19 8364x13 9757x12 11530x11 13835x10 27172x7 76077x4 126794x3 253587x2 760760x1