大幂次运算
题目描述:
给你两个正整数a(0 < a < 100000)和n(0 <= n <=100000000000),计算(a^n) %
20132013并输出结果
思路:
第一个就是直接使用Python内置库里面的pow()函数,专门就是用来处理这个问题的。
第二个就是使用了一个公式:
将幂次分为两种情况,若为偶数则可以将幂次提取一个2出来,放入数a中,这样就可以减小幂次。如果是奇数的话,就先提一个a出来这样b就是偶数了,再执行同样的操作。
至于减小幂次是否能够简化运算,可以找一个例子:
2^31若正常运算需要执行30次2的相乘。
使用第二种方法:2^15*2^16
==> 2^7*2^8*2^8*2^8
==> 2^3*2^4*2^4*2^4*2^4*2^4*2^4*2^4*
==>…….
很明显,这样算的话,有很多运算是重复的,可以简化操作。
于是下面贴代码:
a=85265
n=10000256625323
from time import clock
def func(a,n):
return pow(a,n,20132013)
begin = clock()
print(func(a,n))
finish = clock()
print(finish-begin)
# 优化后的代码
def func2(a,n):
ans = 1
while n:
if n%2 ==1:
ans = (ans * a)%20132013
n //=2
a = a*a%20132013
return ans
begin = clock()
print(func2(a,n))
finish = clock()
print(finish-begin)
使用了python的time.clock()函数来计算时间:
6434294
2.8049393797291378e-05
6434294
1.9358032338975736e-05
计算结果都是一样的,可是时间相差了将近1/3。
============分割线============
密码生成
题目描述: 生活在当代社会,我们要记住很多密码,银行卡,qq,人人,微博,邮箱等等。小P经过一番思索之后,发明了下面这种生成密码方法:
给定两个正整数a和b, 利用a / b我们会到的一个长度无限的小数(若a / b不是无限小数,
比如1/2=0.5,我们认为0.5是0.5000000…,同样将其看做无限长的小数),小P将该小数点后第x位到第y位的数字
当做密码,这样,无论密码有多长,小P只要记住a,b,x,y四个数字就可以了,牢记密码再也不是那么困难的事情了。 现在告诉你a,b,x,y(0
< a,b <= 20132013, 0 < x <= y < 100000000000),请你输出密码。 例如:a = 1, b = 2,
x = 1, y = 4, 则 a / b = 0.5000000…, 输出小数点后第1到4位数字,即5000
思路:
这道题用了模拟除法运算,具体的运算在代码中有备注。
代码:
a,b,x,y = 1,3,1,4
def func(a,b,x,y):
'''模拟除法'''
# 使a<b
a = a%b
# 最后输出
result = ''
# 除的过程中若有重复的就可以直接取结果
history = {}
size = y
while len(result) < y:
# 若被除数为0,则所有的商都为0,所以要在后面补0
if a == 0:
result = result + '0'*(size-len(result))
break
# 若被除数已经被除过了,说明经过中间那么多次的运算又重复了,则可以直接取商
if a in history:
# 字典中的值是被除数取商的第一个位置
remain = result[history[a]:]
# 只要位数不够,都可以直接补
while len(result)<y:
result+=remain
# 去除位数多的
result=result[:y]
break
# 将当前被除数加入字典,值为商在result的索引
history[a]=len(result)
a*=10
while a<b:
#乘一次10,补一个0
a*=10
result +='0'
# 商
temp = a//b
result+=str(temp)
# 取余,循环
a %=b
return result[x-1:y]
print(func(a,b,x,y))
============分割线============
最大连续子序列
题目描述:
给你一个整数list L, 如 L=[2,-3,3,50], 求L的一个连续子序列,使其和最大,输出最大子序列的和。
思路:
方法1:最简单了,就是遍历所有序列中的所有可能,然后找到最大的一个。
代码也就一行就可以搞定。使用了三重循环,两个for循环在外层,然后里面又用了一个sum,最后使用max找出最大的。
代码1:
def func1(L):
return max([sum(L[i:j]) for i in range(len(L)) for j in range(len(L)+1) if i<j])
方法2: 这就比上面的方法要好一点了。只需要2层循环,第一层for循环遍历开始位置,第二层是用来求和,只要当前的和大于最大和时就进行替换。
代码2:
def func2(L):
max_ = 0
for i in range(len(L)):
sum_ = 0
for j in range(i,len(L)):
sum_+=L[j]
if max_<sum_:
max_ = sum_
return max_
方法3: 这个应该是最优的了。只用了一层循环,因为在序列中,若开始元素是负数,那么这个负数肯定不会是最大子序列的开始,和加上负数只会变小,那还不如不加这个负数大呢。所以只要拓展下,序列开始的一段和如果是负数,那么肯定不会是最大子序列。因此如果小于0就可以去掉,重新计数。
代码3:
def func3(L):
max_ = 0
sum_ = 0
for i in range(len(L)):
sum_+=L[i]
if sum_>max_:
max_ = sum_
elif sum_<0:
sum_ = 0
return max_
同样用time.clock()进行计时,序列使用L=[2,-3,3,50,23,-5,4]
76
func1: 4.622224048286044e-05
76
func2: 1.5407413494286808e-05
76
func3: 7.111113920440064e-06
很明显,func3是用时最短的,如果序列长一点的话应该差异会更明显。