算法题:李嘉诚保险柜密码问题
据说,李嘉诚的保险柜密码是一个 8 位的数字。他经常更换密码,但换密码的规则很简单,每次都把密码的数字 * 3,如果有位数溢出,就把最前面的那个数字挪到整个数字末尾加起来 —— 比如 98765432 就会变成 98765432 * 3 = 296296296, 但是这有 9 位, 把最前面的 2 加到末尾, 变成 96296298, 就是新密码。现在我们偷到了李嘉诚的保险柜,而且我们知道他最初的密码是 00000001, 已经迭代了 n 次,求一个算法,可以在 O(log n) 的时间内算出密码。
迭代分析第n次的密码计算公式:
X0 = 0000 0001
X1 = (3 * X0) % 108 + (3 * X0) / 108,显然,X1< 108
X2 = (3 * X1) % 108 + (3 * X1) / 108
= {3 * [(3 * X0) % 108 + (3 * X0) / 108]} % 108 + {3 * [(3 * X0) % 108 + (3 * X0) / 108]} / 108
= {3 * [(3 * X0) % 108]} % 108 + {3 * [(3 * X0) / 108]}(显然其值<9) % 108
+ {3 * [(3 * X0) % 108 ]} / 108 + {3 * [(3 * X0) / 108]} / 108(显然其值趋于0)
= (32 * X0) % 10(由分配率得) + (32 * X0) / 108 + [(32 * X0) % 108 ] / 108(趋于0忽略) + 0
= (32 * X0) % 10 + (32 * X0) / 108
X3 = (33 * X0) % 10 + (33 * X0) / 108
以此类推可得:
Xn = (3n * X0) % 10 + (3n * X0) / 108
又因:X0 = 1,所以:Xn = 3n % 10 + 3n / 108
关于计算3n:
1、简单处理的话可以n个3联乘,时间复杂度O(n),当n较大时比较耗时,此题暂时不考虑大数问题;
2、当n为偶数时,3n=[3(n/2)]2,当n为奇数时,3n=3*{3[(n-1)/2]}2,时间复杂度O(log n),此为最优解法。
Python实现如下:
def g(x):
return x % 100000000 + x // 100000000
def f(x):
if x == 1:
return 3
elif x % 2 == 0:
return g(f(x / 2) * f(x / 2))
elif x % 2 == 1:
return g(f(1) * f(x - 1))
if __name__ == '__main__':
print(f(50))
参考资料: