|
楼主 |
发表于 2009-10-21 18:38
|
显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用 · 内置多项VBA编程加强工具 ★ 免费下载 ★ ★ 使用手册★
结贴
EXCEL26进制来回转换 和 玩转N进制 两道VBA题目极其类似,放到一起做为结贴
EXCEL26进制来回转换 http://club.excelhome.net/thread-484032-1-1.html
玩转N进制 http://club.excelhome.net/thread-485781-1-1.html
----------------------------------------------------------------------------
EXCEL26进制来回转换 http://club.excelhome.net/thread-484032-1-1.html
先看下我的代码,
'ID :ldy
Function N2Char26(ByVal L As Double) As String
Do While L > 0
N2Char26 = Chr(L - Int(L / (26.0000000000001)) * 26 + 64) & N2Char26
L = Int(L / (26.0000000000001))
Loop
End Function
Function N2Char26(ByVal L As Double) As String '第二版本,稍微慢一点,便于和玩转N进制比较
Const Jzs As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Do
N2Char26 = Mid(Jzs, L - Int(L / 26.0000000000001) * 26, 1) & N2Char26
L = Int(L / (26.0000000000001))
Loop While L > 0
End Function
Function N2Char26R(ByVal L As String) As Double
L = UCase(L)
For i% = 1 To Len(L)
N2Char26R = N2Char26R + (Asc(Mid(L, i)) - 64) * 26 ^ (Len(L) - i)
Next
End Function
N2Char26R 所有参赛者的代码都大同小异,送分的机会,
重点在N2Char26, 把10进制数转换为A-Z 26个字母表示的字符串。为什么有两个版本,看下去就知道了
EXCEL26进制 与通常所提到的 2进制、8进制、10进制、16进制不同,区别在于这种进制是没有0位的,
这一点很容易被忽略,很多人只得了1分,就是因为这个原因,代码中没有处理26的整倍数的情况,
结果26、52 等26的倍数值转换错误,验证也很简单,只要是26的整倍数,转换后的最后一个字符一定是 Z
这个代码是合并语句后的结果,没有其他变量,虽然牺牲了一点效率的,看起来简洁一些,接着做玩转N进制的时候方便比较。
还原一下第一个版本:
Function N2Char26(ByVal L As Double) As String
Do
Y% = L - Int(L / 26) * 26' 余数
L = Int(L / 26)' 算出除以26的倍数, 商
If Y = 0 Then '余数为0 则倍数-1 余数+26 ,没有考虑到这一点,就少了2分
L = L - 1
Y = Y + 26
End If
N2Char26 = Chr(Y + 64) & N2Char26 ' 根据余数,按照字符集取得对应的字符
Loop While L > 0
End Functio
解题思路大致如下
1 算出余数
2 算出整倍数,商
3 如果余数为0 则倍数-1 余数+26 ,如果是常规有0位的进制则不用考虑
4 按照余数组织字符串
如此循环 直到倍数为 0
VBA 里常用的 两个运算符 Mod 和 整除 \ ,处理数据有限制,就是不能超出Long值的范围,不能直接使用。
算余数 用 L - Int(L / 26) * 26 来代替 L Mom 26
取整倍数用 Int(L / 26) 代替 L \ 26。
继续想一想,如何让余数算出来永远都不为 0,那就要除以一个仅仅比26大一点点的小数26.000000001,
也就是 26 + 极小数
还是这个代码变化一下
Function N2Char26(ByVal L As Double) As String
Do
Y% = L - Int(L / 26.00000001) * 26' 余数 这样一来26/26.0000001 的余数 Y 就是 26,不会是0 了
L = Int(L / 26.00000001) ' 算出除以26+极小数的倍数 26 就是26.0000001的 0.几倍 整倍数是0 不用-1了
N2Char26 = Chr(Y + 64) & N2Char26 ' 根据余数,按照字符集取得对应的字符
Loop While L > 0
End Functio
还有一个问题,26+极小数 中间到底多少个 0 合适?
Double 的处理精度是15位数字,不含小数点,所以中间 12个0 刚好刚好15位。把变量代入合并后就成了那个4行的代码。
玩转N进制 http://club.excelhome.net/thread-485781-1-1.html
,看起来比 26进制要难,其实要容易一些,网上有现成的代码,但不能转化负数。
需要了解的就是 Long值负数的问题。
很多人26 进制做错的代码拿过来,修改下取字符的方法,就是 玩转N进制的正确答案。纯粹的送分题,似乎鲜少有人轻松得分。
我的代码:
Function N2ChrX(ByVal L As Long, N As Integer) As String
Const Jzs As String = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Dim v As Double, x As Double, y As Long
If N = 10 Then N2ChrX = L: Exit Function' 10进制转10进制没必要处理,直接赋值
v = L - (L < 0) * 4294967296# '把负数转为 正数 赋值给 Double
Do
N2ChrX = Mid(Jzs, v - Int(v / N) * N + 1, 1) & N2ChrX
v = Int(v / N)
Loop While v > 0
End Function
Function ChrX2N(ByVal S As String, N As Integer) As Long
Const Jzs As String = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Dim i%, v As Double
S = UCase(S)
If N = 10 Then ChrX2N = S: Exit Function
For i = 1 To Len(S)
v = v + (InStr(Jzs, Mid$(S, i, 1)) - 1) * N ^ (Len(S) - i)
Next
ChrX2N = v + (v > 2147483647) * 4294967296#
End Function
ChrX2N 和 N2Char26R 几乎一样,都是字符转换为数字 ,只是多了个参数表示进制数
这两个函数除了名称变量不同,一个是从常量中取顺序值,一个是字符表中取Asc码,其他都一样。
26进制那题能拿到分的,这两分几乎就是白给的。唯一要注意的就是借助Double来过渡。
比较有趣的是Long值转换为字符串:N2ChrX
Long值有负数,但其实也是一种序列,有4294967296(2^32) 个成员,0是第 1个,-1 是最后一个。-1的序列数就是4294967296
如果把0 当作是第 “0”个, 那么 -1的序列数就是4294967295
解题思路:
先判断是否负数,把负数转为 正数 赋值给 Double
后面的就直接套用 N2Char26 函数的思路:
Do
Y% = L - Int(L / 26) * 26' 余数
L = Int(L / 26)' 算出除以26的倍数
If Y = 0 Then '余数为0 则倍数-1 余数+26 ,没有考虑到这一点,就少了2分
L = L - 1
Y = Y + 26
End If
N2Char26 = Chr(Y + 64) & N2Char26 ' 根据余数,按照字符集取得对应的字符
Loop While L > 0
因为有0位,余数为0的情况不用管了,仅仅把变量名称改一改
Do
Y% = V - Int(V / N) * N' 余数
V = Int(L / N)' 算出除以N的倍数
N2ChrX = Mid(Jzs, Y - 1, 1) & N2ChrX
Loop While v > 0
再把变量、代入合并,就是我的代码了。
把这两个代码中的循环中的部分放在一起比较一下,(第二版本的)
N2Char26 = Mid(Jzs, L - Int(L / 26.0000000000001) * 26 , 1) & N2Char26
L = Int(L / 26.0000000000001)
N2ChrX = Mid(Jzs, V - Int(V / N ) * N + 1, 1) & N2ChrX
V = Int(V / N)
这两函数的核心部分其实就差那么一点点----- 0.0000000000001,也就是说是否需要处理余数为 0 的情况。
N2ChrX 和 ChrX2N 只要修改其中的Jzs常量,你就有了自己独门的进制转换标准,把这对函数应用到程序的加密,注册码计算当中,可以很大的提高破解难度。 |
|