Spiral Memory
You come across an experimental new kind of memory stored on an infinite two- dimensional grid.
Each square on the grid is allocated in a spiral pattern starting at a location marked 1 and then counting up while spiraling outward. For example, the first few squares are allocated like this:
While this is very space-efficient (no squares are skipped), requested data must be carried back to square 1 (the location of the only access port for this memory system) by programs that can only move up, down, left, or right. They always take the shortest path: the Manhattan Distance between the location of the data and square 1.
For example:
Data from square 1 is carried 0 steps, since it's at the access port. Data from square 12 is carried 3 steps, such as: down, left, left. Data from square 23 is carried only 2 steps: up twice.
Data from square 1024 must be carried 31 steps.
How many steps are required to carry the data from the square identified in your puzzle input all the way to the access port?
How to test your answer:
If you input: 100000 Your Answer should be: 173
If you input: 2345678 Your Answer should be: 1347
这道题目是参加培训的一道入门测试题, 同时也来源于2017 edition of Advent of Code的day 3.
大概说明下题目的意图,spiral memory 螺旋存储,以1开始,以逆时针螺旋式递增数字。输入一个数,算出这个数离1有多少步(只能上下左右移动步数),比如12离1有3步(下,左,左),以此类推。
先说结论:目标数字的坐标绝对值的和就是离中心1的步数
首先这道题可以以1为中心作直角坐标系,如下图
首先将每个正方形按照层级划分,1属于第零层,9属于第一层正方形,25属于第二层正方形。所以输入的数字只要知道在第几层就可以算出该数字的坐标(x, y)。
第一步确定目标数字属于第几层。
- 第一层的最后一位数是9=(2*1+1)^2
- 第二层的最后一位数是25=(2*2+1)^2
- 第i层的最后一位数是(2*i+1)^2
每一层的最后一个数字都是奇数的平方,所以对目标数字开方来计算判断属于第几层,具体算法看下面python代码:
def getI(number):
i = 0
num = int(math.sqrt(number))
print("num: " + str(num))
if num % 2 != 0:
# odd
i = int((num - 1) / 2)
else:
# even
i = int(num / 2)
min = int(math.pow(2 * (i - 1) + 1, 2))
max1 = int(math.pow(2 * i + 1, 2))
max2 = int(math.pow(2 * (i + 1) + 1, 2))
if min < number <= max1:
print("%d<%d<=%d" % (min, number, max1))
print("i: " + str(i))
i = i
elif max1 < number < max2:
print("%d<%d<=%d" % (max1, number, max2))
print("i+1: " + str(i + 1))
i = i + 1
return i
第二步就要算出y坐标
对于计算y坐标,需要对每一层的数字找到相对应的规律,因为都是正方形,就是每边的数字个数是相同的(8*i/4),每条边对应的坐标规律不一样,比如以x轴水平的边,y坐标不变,x坐标增减。
主要规律如下
- 每层数字个数(i表示层数):8*i
- 每边的数字个数:2*i
- 每层最后一个数字:(2*i+1)^2
以此类推可以算出每条边的数字范围以及坐标:
def getY(i, number):
n = 2 * i + 1
nsuq = int(math.pow(n, 2))
firstN = int(nsuq - 8 * i + 1)
secondN = int(nsuq - 6 * i)
thirdN = int(secondN + 1)
fourN = int(nsuq - 4 * i)
fiveN = int(fourN + 1)
sixN = int(nsuq - 2 * i)
seventN = int(sixN + 1)
eightN = int(nsuq)
x = 0
y = 0
if firstN <= number <= secondN:
x = i
y = i - secondN + number
elif thirdN <= number <= fourN:
x = -i + fourN - number
y = i
elif fiveN <= number <= sixN:
x = -i
y = -i + sixN - number
elif seventN <= number <= eightN:
x = i - eightN + number
y = -i
return x, y
第i层最后一个数是(2*i+1)^2, 坐标(i,-i)
最后测试结果:
input a number: 1
num: 1
1 needs 0 steps to the center
(x, y): (0, 0)
input a number: 12
num: 3
9<12<=25
i+1: 2
12 needs 3 steps to the center
(x, y): (2, 1)
input a number: 100000
num: 316
99225<100000<=100489
i: 158
100000 needs 173 steps to the center
(x, y): (-158, 15)
input a number: 2345678
num: 1531
2343961<2345678<=2350089
i+1: 766
2345678 needs 1347 steps to the center
(x, y): (581, 766)
耗时测试:
input a number: 1234567890
num: 35136
1234468225<1234567890<=1234608769
i: 17568
1234567890 needs 29393 steps to the center
(x, y): (-17568, -11825)
Consuming Time: 0:00:00.000089
input a number: 2345678
num: 1531
2343961<2345678<=2350089
i+1: 766
2345678 needs 1347 steps to the center
(x, y): (581, 766)
Consuming Time: 0:00:00.000055
代码传送门spiral memory