将正方形放在笛卡尔平面上并根据序列号计算出它们的 X、Y 坐标
Placing squares on a Cartesian plane and working out their X,Y coords based on sequence number
工作中的一群人和我自己都在努力解决这个问题,但找不到一种干净有效的数学方法来解决这个问题。这是:
给定一个标准的笛卡尔平面和一系列图像(都是正方形且尺寸相同,例如我们只说 1 个单位)我们如何以这样的方式组织它们,使它们围绕原点旋转( 0,0) 平面的左上角。但更具体地说,如果给定数字 25(序列中的第 25 个方块),它的 X、Y 坐标是什么?
希望这张粗略的图像有助于解释序列。放置在网格上的第一个方块将是红色,然后是蓝色、黄色、紫色、绿色、黑色、棕色,然后依此类推,如圆点所示。
我们希望有一个 "relatively" 简单的数学公式来解决这个问题,但这也许只是一厢情愿。
假设您使用的是基于 1 的索引,您可以使用以下算法找到您的答案(不确定是否存在封闭形式的解决方案)。
这背后的想法是找出你在哪一层(你的 x 或 y 离 0 有多远)。然后找到你在哪个象限。然后找出你在哪一侧然后找出坐标。
get_coords(n)
layer = 1
while (n > 3 * layer * layer + (layer+1)*(layer+1))
++layer
layer_ind = n - 3 * (layer - 1) * (layer - 1) - layer * layer
if (layer_ind < 2 * layer + 1) # in bottom right
if (layer_ind <= layer + 1)
return (layer, - layer_ind + 1)
else
return (2 * layer + 1 - layer_ind, -layer)
else if ((layer_ind - 2 * layer - 1)/(2 * layer - 1) < 1) # bottom left
layer_ind = layer_ind - 2 * layer - 1
if (layer_ind <= layer)
return (-layer_ind+1, -layer+1)
else
return (-layer+1, -2*layer + 1 + layer_ind)
else if ((layer_ind - 2 * layer - 1)/(2 * layer - 1) < 2) # top left
layer_ind = layer_ind - 4 * layer
if (layer_ind <= layer)
return (-layer+1, layer_ind)
else
return (layer_ind - 2 * layer, layer)
else # top right
layer_ind = layer_ind - 6 * layer + 1
if (layer_ind <= layer)
return (layer_ind-1, layer)
else
return (layer-1, 2 * layer - layer_ind)
我之前的答案(现在归入修订历史)是一个递归解决方案。下面是我称之为 "cyclic" 的解决方案。
我们的想法是我们围绕一个正方形进行多次旋转。在任何给定索引处:
- 我们正在围绕一个较小的正方形外侧进行革命
- 我们在这个广场的一侧
- 我们部分位于这个广场的一侧
通过跟踪正方形、边和边的距离,我们可以索引每个点。
我们通过画 4 条相等的线绕一个正方形。 (称他们为"segments"。)我们在第一段的中间开始每一圈。
例如:
第 1 圈围绕一个大小为 0、长度为 1 的正方形:
O OO
O OO OO OO
第 2 圈围绕一个大小为 4、长度为 3 的正方形:
O OOOO OOOO
OO OO OOO OOO OOOO
OOO OOO OOOO OOOO OOOO
O OOOO OOOO OOOO OOOO
第 3 次旋转是围绕一个大小为 16、长度为 5 的正方形:
O OOOOOO OOOOOO
OOOO OOOO OOOOO OOOOO OOOOOO
OOOO OOOO OOOOO OOOOO OOOOOO
OOOOO OOOOO OOOOOO OOOOOO OOOOOO
OOOOO OOOOO OOOOOO OOOOOO OOOOOO
O OOOOOO OOOOOO OOOOOO OOOOOO
下面是 Python 中的一个实现。
import math
def makeSquare(main_index, offset_size):
step_index = int(math.sqrt(main_index))/2 # How many revolutions have we made?
last_square = int(math.pow(step_index*2,2)) # Whats the largest square less than main_index?
segment_length = (2 * step_index) + 1 # How long is the side of the square we've made so far? The segment length is 1 more than that.
main_index = main_index + step_index + 1 # Skip ahead so we start in the middle of the right side instead of the top. We do some modulo stuff below to wrap around.
segment_index = (main_index - last_square) / segment_length # Which segment are we on?
dot_index = main_index - segment_index*segment_length - last_square # How far along the segment are we?
draw_functions = [
lambda i, size: [size, size - i], # Draw the right side of a square
lambda i, size: [size - i, 0 ], # Draw the bottom of a square
lambda i, size: [0, i ], # Draw the left side of a square
lambda i, size: [i, size ], # Draw the top of a square
]
x, y = draw_functions[segment_index % 4](dot_index % (4 * segment_length), segment_length)
return [ x + offset_size - step_index - 1, y + offset_size - step_index - 1]
# Print the points to the console in sequence
import time, os
step = 4
points = [makeSquare(i, step) for i in range(int(math.pow(step*2,2)))]
board = [[" " for x in range(step*2)] for x in range(step*2)]
for p in range(len(points)):
print
print p, "------------------"
board[step*2-1 - points[p][1]][points[p][0]] = "O" # Do some coordinate wrangling so we have normal X and Y
print (os.linesep).join([''.join(row) for row in board])
time.sleep(.1)
(编辑时:我添加了第二个函数,可以直接获取笛卡尔坐标版本。)
在我的头爆炸之前我已经走了这么远。它是封闭形式,因为它给出了比方说百万分之一平方的坐标,而不需要将它们一个接一个地放在一个循环中。把它写下来不会给出一个很好的公式,但你可以写成一个由 8 个部分组成的分段定义的公式。答案以基于 1 的网格坐标给出。第一个坐标告诉您在 y 轴的右边或左边有多少个方块,第二个坐标告诉您向上或向下有多远。从这些数字中可以相对容易地得出例如得到他们左上角的笛卡尔坐标。我在 Python 中实现了它:
from math import sqrt, ceil
def coordinates(m):
n = ceil(sqrt(m)/2) #shell number
i = m - 4*(n-1)**2 #index in shell
if i <= n:
return (n,-i)
elif i <= 2*n-1:
return (2*n - i, -n)
elif i <= 3*n - 1:
return (2*n - 1 - i, -n)
elif i <= 4*n - 2:
return (-n, -4*n + 1 + i)
elif i <= 5*n - 2:
return (-n, -4*n + 2 + i)
elif i <= 6*n - 3:
return (-6*n + 2 + i, n)
elif i <= 7*n - 3:
return (-6*n + 3 + i,n)
else:
return (n, 8*n -3 - i)
要从 (i,j) 网格坐标获取左上角的 (x,y) 笛卡尔坐标,您可以使用以下函数,它有一个可选的宽度参数以允许非单位正方形:
def cartesianFromGrid(i,j,w = 1):
x = w * (i if i < 0 else i - 1)
y = w * (j if j > 0 else j + 1)
return (x,y)
可以不用先通过网格坐标直接得到左上角的直角坐标。生成的公式涉及的情况更少(因为我不需要从 1 直接跳到 -1 或反之亦然),尽管我将这两个公式都保留在答案中,因为在许多方面网格透视更自然:
def cartCoordinates(m):
n = ceil(sqrt(m)/2) #shell number
i = m - 4*(n-1)**2 #index in shell
if i <= n:
return (n-1,-i+1)
elif i <= 3*n - 1:
return (2*n - 1 - i, -n + 1)
elif i <= (5*n - 2):
return (-n, -4*n + 2 + i)
elif i <= 7*n - 3:
return (-6*n + 2 + i, n)
else:
return (n-1, 8 * n - 3 - i)
1-16 的输出:
>>> for n in range(1,17):
print(n, ': grid coords =', coordinates(n),
'Cartesian =',cartesianFromGrid(*coordinates(n)))
1 : grid coords = (1, -1) Cartesian = (0, 0)
2 : grid coords = (-1, -1) Cartesian = (-1, 0)
3 : grid coords = (-1, 1) Cartesian = (-1, 1)
4 : grid coords = (1, 1) Cartesian = (0, 1)
5 : grid coords = (2, -1) Cartesian = (1, 0)
6 : grid coords = (2, -2) Cartesian = (1, -1)
7 : grid coords = (1, -2) Cartesian = (0, -1)
8 : grid coords = (-1, -2) Cartesian = (-1, -1)
9 : grid coords = (-2, -2) Cartesian = (-2, -1)
10 : grid coords = (-2, -1) Cartesian = (-2, 0)
11 : grid coords = (-2, 1) Cartesian = (-2, 1)
12 : grid coords = (-2, 2) Cartesian = (-2, 2)
13 : grid coords = (-1, 2) Cartesian = (-1, 2)
14 : grid coords = (1, 2) Cartesian = (0, 2)
15 : grid coords = (2, 2) Cartesian = (1, 2)
16 : grid coords = (2, 1) Cartesian = (1, 1)
如果您想知道:
>>> coordinates(1000000)
(500, 1)
最后一个答案是有道理的,因为百万分之一的正方形是 1000x1000 正方形网格的顶点。
我使用上面的方法在 tkinter canvas 小部件上放置彩色方块:
工作中的一群人和我自己都在努力解决这个问题,但找不到一种干净有效的数学方法来解决这个问题。这是:
给定一个标准的笛卡尔平面和一系列图像(都是正方形且尺寸相同,例如我们只说 1 个单位)我们如何以这样的方式组织它们,使它们围绕原点旋转( 0,0) 平面的左上角。但更具体地说,如果给定数字 25(序列中的第 25 个方块),它的 X、Y 坐标是什么?
希望这张粗略的图像有助于解释序列。放置在网格上的第一个方块将是红色,然后是蓝色、黄色、紫色、绿色、黑色、棕色,然后依此类推,如圆点所示。
我们希望有一个 "relatively" 简单的数学公式来解决这个问题,但这也许只是一厢情愿。
假设您使用的是基于 1 的索引,您可以使用以下算法找到您的答案(不确定是否存在封闭形式的解决方案)。
这背后的想法是找出你在哪一层(你的 x 或 y 离 0 有多远)。然后找到你在哪个象限。然后找出你在哪一侧然后找出坐标。
get_coords(n)
layer = 1
while (n > 3 * layer * layer + (layer+1)*(layer+1))
++layer
layer_ind = n - 3 * (layer - 1) * (layer - 1) - layer * layer
if (layer_ind < 2 * layer + 1) # in bottom right
if (layer_ind <= layer + 1)
return (layer, - layer_ind + 1)
else
return (2 * layer + 1 - layer_ind, -layer)
else if ((layer_ind - 2 * layer - 1)/(2 * layer - 1) < 1) # bottom left
layer_ind = layer_ind - 2 * layer - 1
if (layer_ind <= layer)
return (-layer_ind+1, -layer+1)
else
return (-layer+1, -2*layer + 1 + layer_ind)
else if ((layer_ind - 2 * layer - 1)/(2 * layer - 1) < 2) # top left
layer_ind = layer_ind - 4 * layer
if (layer_ind <= layer)
return (-layer+1, layer_ind)
else
return (layer_ind - 2 * layer, layer)
else # top right
layer_ind = layer_ind - 6 * layer + 1
if (layer_ind <= layer)
return (layer_ind-1, layer)
else
return (layer-1, 2 * layer - layer_ind)
我之前的答案(现在归入修订历史)是一个递归解决方案。下面是我称之为 "cyclic" 的解决方案。
我们的想法是我们围绕一个正方形进行多次旋转。在任何给定索引处:
- 我们正在围绕一个较小的正方形外侧进行革命
- 我们在这个广场的一侧
- 我们部分位于这个广场的一侧
通过跟踪正方形、边和边的距离,我们可以索引每个点。
我们通过画 4 条相等的线绕一个正方形。 (称他们为"segments"。)我们在第一段的中间开始每一圈。
例如:
第 1 圈围绕一个大小为 0、长度为 1 的正方形:
O OO
O OO OO OO
第 2 圈围绕一个大小为 4、长度为 3 的正方形:
O OOOO OOOO
OO OO OOO OOO OOOO
OOO OOO OOOO OOOO OOOO
O OOOO OOOO OOOO OOOO
第 3 次旋转是围绕一个大小为 16、长度为 5 的正方形:
O OOOOOO OOOOOO
OOOO OOOO OOOOO OOOOO OOOOOO
OOOO OOOO OOOOO OOOOO OOOOOO
OOOOO OOOOO OOOOOO OOOOOO OOOOOO
OOOOO OOOOO OOOOOO OOOOOO OOOOOO
O OOOOOO OOOOOO OOOOOO OOOOOO
下面是 Python 中的一个实现。
import math
def makeSquare(main_index, offset_size):
step_index = int(math.sqrt(main_index))/2 # How many revolutions have we made?
last_square = int(math.pow(step_index*2,2)) # Whats the largest square less than main_index?
segment_length = (2 * step_index) + 1 # How long is the side of the square we've made so far? The segment length is 1 more than that.
main_index = main_index + step_index + 1 # Skip ahead so we start in the middle of the right side instead of the top. We do some modulo stuff below to wrap around.
segment_index = (main_index - last_square) / segment_length # Which segment are we on?
dot_index = main_index - segment_index*segment_length - last_square # How far along the segment are we?
draw_functions = [
lambda i, size: [size, size - i], # Draw the right side of a square
lambda i, size: [size - i, 0 ], # Draw the bottom of a square
lambda i, size: [0, i ], # Draw the left side of a square
lambda i, size: [i, size ], # Draw the top of a square
]
x, y = draw_functions[segment_index % 4](dot_index % (4 * segment_length), segment_length)
return [ x + offset_size - step_index - 1, y + offset_size - step_index - 1]
# Print the points to the console in sequence
import time, os
step = 4
points = [makeSquare(i, step) for i in range(int(math.pow(step*2,2)))]
board = [[" " for x in range(step*2)] for x in range(step*2)]
for p in range(len(points)):
print
print p, "------------------"
board[step*2-1 - points[p][1]][points[p][0]] = "O" # Do some coordinate wrangling so we have normal X and Y
print (os.linesep).join([''.join(row) for row in board])
time.sleep(.1)
(编辑时:我添加了第二个函数,可以直接获取笛卡尔坐标版本。)
在我的头爆炸之前我已经走了这么远。它是封闭形式,因为它给出了比方说百万分之一平方的坐标,而不需要将它们一个接一个地放在一个循环中。把它写下来不会给出一个很好的公式,但你可以写成一个由 8 个部分组成的分段定义的公式。答案以基于 1 的网格坐标给出。第一个坐标告诉您在 y 轴的右边或左边有多少个方块,第二个坐标告诉您向上或向下有多远。从这些数字中可以相对容易地得出例如得到他们左上角的笛卡尔坐标。我在 Python 中实现了它:
from math import sqrt, ceil
def coordinates(m):
n = ceil(sqrt(m)/2) #shell number
i = m - 4*(n-1)**2 #index in shell
if i <= n:
return (n,-i)
elif i <= 2*n-1:
return (2*n - i, -n)
elif i <= 3*n - 1:
return (2*n - 1 - i, -n)
elif i <= 4*n - 2:
return (-n, -4*n + 1 + i)
elif i <= 5*n - 2:
return (-n, -4*n + 2 + i)
elif i <= 6*n - 3:
return (-6*n + 2 + i, n)
elif i <= 7*n - 3:
return (-6*n + 3 + i,n)
else:
return (n, 8*n -3 - i)
要从 (i,j) 网格坐标获取左上角的 (x,y) 笛卡尔坐标,您可以使用以下函数,它有一个可选的宽度参数以允许非单位正方形:
def cartesianFromGrid(i,j,w = 1):
x = w * (i if i < 0 else i - 1)
y = w * (j if j > 0 else j + 1)
return (x,y)
可以不用先通过网格坐标直接得到左上角的直角坐标。生成的公式涉及的情况更少(因为我不需要从 1 直接跳到 -1 或反之亦然),尽管我将这两个公式都保留在答案中,因为在许多方面网格透视更自然:
def cartCoordinates(m):
n = ceil(sqrt(m)/2) #shell number
i = m - 4*(n-1)**2 #index in shell
if i <= n:
return (n-1,-i+1)
elif i <= 3*n - 1:
return (2*n - 1 - i, -n + 1)
elif i <= (5*n - 2):
return (-n, -4*n + 2 + i)
elif i <= 7*n - 3:
return (-6*n + 2 + i, n)
else:
return (n-1, 8 * n - 3 - i)
1-16 的输出:
>>> for n in range(1,17):
print(n, ': grid coords =', coordinates(n),
'Cartesian =',cartesianFromGrid(*coordinates(n)))
1 : grid coords = (1, -1) Cartesian = (0, 0)
2 : grid coords = (-1, -1) Cartesian = (-1, 0)
3 : grid coords = (-1, 1) Cartesian = (-1, 1)
4 : grid coords = (1, 1) Cartesian = (0, 1)
5 : grid coords = (2, -1) Cartesian = (1, 0)
6 : grid coords = (2, -2) Cartesian = (1, -1)
7 : grid coords = (1, -2) Cartesian = (0, -1)
8 : grid coords = (-1, -2) Cartesian = (-1, -1)
9 : grid coords = (-2, -2) Cartesian = (-2, -1)
10 : grid coords = (-2, -1) Cartesian = (-2, 0)
11 : grid coords = (-2, 1) Cartesian = (-2, 1)
12 : grid coords = (-2, 2) Cartesian = (-2, 2)
13 : grid coords = (-1, 2) Cartesian = (-1, 2)
14 : grid coords = (1, 2) Cartesian = (0, 2)
15 : grid coords = (2, 2) Cartesian = (1, 2)
16 : grid coords = (2, 1) Cartesian = (1, 1)
如果您想知道:
>>> coordinates(1000000)
(500, 1)
最后一个答案是有道理的,因为百万分之一的正方形是 1000x1000 正方形网格的顶点。
我使用上面的方法在 tkinter canvas 小部件上放置彩色方块: