Excel 大家都用过,它的列名是用字母编号的,A 表示第一列,B 表示第二列,AA 表示第27列,AB 表示第28列等等。
现给定一个数字,如何得到列名称呢。比如输入28,输出 AB。
一开始以为就是一个简单的26进制转换,到底还是大意了,这里的每一位都是没有0的,而且能取到进制的最大值。正常26进制每一位都是0到25,但是这里却是1到26。
算法原理还是不停的取模和取余来得到每一位的值。只不过和正常的 n 进制转换不同,我们不能直接用 n 去取模和取余,而是应该用 n-1。
还是按26进制计算,假设要表示的数为 num
,余数为 r
,则:
n
u
m
=
n
×
26
+
r
num = n \times 26+r
num=n×26+r
其中
r
r
r 的范围是0到25,假设
R
R
R 的范围是1到26,我们将
r
r
r 线性映射到
R
R
R,并将
r
r
r 替换成
R
R
R,于是:
n
×
26
+
R
=
n
u
m
+
1
n \times 26+R=num+1
n×26+R=num+1
我们会发现在将
r
r
r 替换成
R
R
R 的过程中,
n
u
m
num
num 实际上每次都多加了1,所以在计算每一位的值时要将它减出来。
另外我们可以想一下,在取余的过程中,有两种情况:
n
u
m
=
n
×
26
+
r
n
u
m
=
n
×
26
\begin{align} num & = n \times 26 + r \\ num & = n \times 26 \end{align}
numnum=n×26+r=n×26
第一种情况其实可以正常计算,第二中情况不行,因为不会出现0,所以我们需要改写一下:
n
u
m
=
(
n
−
1
)
×
26
+
26
num=(n-1)\times 26 + 26
num=(n−1)×26+26
但是正常取余运算算不出这个余数,怎么办呢,给
n
u
m
num
num 减去1就可以了:
n
u
m
−
1
=
(
n
−
1
)
×
26
+
25
num-1=(n-1)\times 26 + 25
num−1=(n−1)×26+25
对于其他情况也一样,于是就统一变成了:
n
u
m
−
1
=
n
×
26
+
(
r
−
1
)
num-1=n \times 26 + (r-1)
num−1=n×26+(r−1)
至于多减的1,会自动补回来。
defmodule Solution do
@spec convert_to_title(column_number :: integer) :: String.t
def convert_to_title(column_number) do
{column_number, 26}
|> Stream.unfold(fn
{0, _} -> nil
{n, m} -> {rem(n - 1, m) + ?A, {div(n - 1, m), m}}
end)
|> Enum.reverse
|> List.to_string
end
end
在 Elixir 中,我们可以通过 Stream.unfold
来构造每一位的值,连递归都不用写了,可以说是十分优雅了。