构造程序逻辑
文章目录
前言
学完了前面的几个章节后,我觉得有必要做一些练习来巩固之前所学的知识,虽然迄今为止我们学习的内容只是Python的冰山一角,但是这些内容已经足够我们来构建程序中的逻辑。对于编程语言的初学者的我来说,在学习了Python的核心语言元素(变量、类型、运算符、表达式、分支结构、循环结构等)之后,必须做的一件事情就是尝试用所学知识去解决现实中的问题,换句话说就是锻炼自己把用人类自然语言描述的算法(解决问题的方法和步骤)翻译成Python代码的能力,而这件事情必须通过大量的练习才能达成。
下面我想通过一些简单的例子,来巩固之前所学的Python知识,另一方面帮助我了解如何建立程序中的逻辑以及如何运用一些简单的算法解决现实中的问题。
(丑话说在前头)做完练习之后发现自己的水平的确很烂,然后大家的话还是看大佬版的吧。
一.例1:寻找水仙花
说明: 水仙花数也被称为超完全数字不变数、自恋数、自幂数、阿姆斯特朗数,它是一个3位数,该数字每个位上数字的立方之和正好等于它本身,例如: 1 3 + 5 3 + 3 3 = 153 1^3 + 5^3+ 3^3=153 13+53+33=153。
# 如寻找1000以内的水仙花数(小白版)
"""因为10的三次方就是1000了,所以在1000以内,
三个数应该都小于10,且和为三位数,故首位从1开始"""
for i in range(1,10):
for j in range(10):
for k in range(10):
if i**3 + j**3 + k**3 == 100*i + 10*j + k:
print("%d,%d,%d 三个数构成水仙花数为%d"%(i,j,k,100*i + 10*j + k))
还是看下面这个吧。
# 大佬版
for num in range(100, 1000):
low = num % 10
mid = num // 10 % 10
high = num // 100
if num == low ** 3 + mid ** 3 + high ** 3:
print(num)
对比上面的两种方法,小白版的话,肯定是以实现为第一,骚操作是看不到的(没技术咋骚嘛),就是直线思维嘛;大佬版的话,他好在结构扁平嘛,这在Python之禅上写过,就是能不嵌套尽量别嵌套,这样可读性也好点(我猜复杂度是不是也低点,现在还不知道这个怎么算的,个人感觉哈,还没学)。但是他这好像也是暴力检索吧?脑壳疼。
然后就是在上面的代码中,我们通过整除和求模运算分别找出了一个三位数的个位、十位和百位,这种小技巧在实际开发中还是常用的。用类似的方法,我们还可以实现将一个正整数反转,例如:将12345变成54321,代码如下所示。
num = int(input("请输入一个正整数:"))
reverse_num = 0
while num > 0:
reverse_num = 10 * reverse_num + num % 10
num //= 10
print("反转之后的数为:%d" % reverse_num)
所以他这种编程思想,泛化好像也好点。
二.例2:百钱白鸡问题
说明:百钱百鸡是我国古代数学家张丘建在《算经》一书中提出的数学问题:鸡翁一值钱五,鸡母一值钱三,鸡雏三值钱一。百钱买百鸡,问鸡翁、鸡母、鸡雏各几何?翻译成现代文是:公鸡5元一只,母鸡3元一只,小鸡1元三只,用100块钱买一百只鸡,问公鸡、母鸡、小鸡各有多少只?
"""
就是一共100元,那么公鸡不会超过20只,母鸡不会超过33只,
那小鸡不超过100只(因为一共才100只)。
"""
# 那最简单的应该是暴力检索吧(小白版)
for cock in range(0, 20):
for hen in range(0, 33):
for click in range(0, 100):
if cock + hen + click == 100 and 5*cock + 3*hen + (1/3)*click == 100:
print("公鸡有 %d 只,母鸡有 %d 只,小鸡有 %d 只!" % (cock,hen,click))
# 公鸡有 0 只,母鸡有 25 只,小鸡有 75 只!
# 公鸡有 4 只,母鸡有 18 只,小鸡有 78 只!
# 公鸡有 8 只,母鸡有 11 只,小鸡有 81 只!
# 公鸡有 12 只,母鸡有 4 只,小鸡有 84 只!
# 与上面类似(但这个少一层循环嘛,还是好点。)
for x in range(0, 20):
for y in range(0, 33):
z = 100 - x - y
if 5 * x + 3 * y + z / 3 == 100:
print('公鸡: %d只, 母鸡: %d只, 小鸡: %d只' % (x, y, z))
# 公鸡: 0只, 母鸡: 25只, 小鸡: 75只
# 公鸡: 4只, 母鸡: 18只, 小鸡: 78只
# 公鸡: 8只, 母鸡: 11只, 小鸡: 81只
# 公鸡: 12只, 母鸡: 4只, 小鸡: 84只
上面使用的方法叫做穷举法,也称为暴力搜索法,这种方法通过一项一项的列举备选解决方案中所有可能的候选项并检查每个候选项是否符合问题的描述,最终得到问题的解。这种方法看起来比较笨拙,但对于运算能力非常强大的计算机来说,通常都是一个可行的甚至是不错的选择,而且问题的解如果存在,这种方法一定能够找到它。
三.例3:CRAPS赌博游戏。
说明: CRAPS又称花旗骰,是美国拉斯维加斯非常受欢迎的一种的桌上赌博游戏。该游戏使用两粒骰子,玩家通过摇两粒骰子获得点数进行游戏。简单的规则是:玩家第一次摇骰子如果摇出了7点或11点,玩家胜;玩家第一次如果摇出2点、3点或12点,庄家胜;其他点数玩家继续摇骰子,如果玩家摇出了7点,庄家胜;如果玩家摇出了第一次摇的点数,玩家胜;其他点数,玩家继续要骰子,直到分出胜负。
我们设定玩家开始游戏时有1000元的赌注,游戏结束的条件是玩家输光所有的赌注。
# 小白版(感觉应该还是有BUG,或者还是没有完整完成要求,总感觉怪怪的。)
import random
init_wager = 1000
while init_wager > 0:
wager = int(input("请输入您的赌注(小于%d):" % init_wager))
guess_one = random.randint(1, 12)
if guess_one == 7|11:
init_wager += wager
print("您获胜! 当前还有赌注:%d" % init_wager)
elif guess_one == 2|3|12:
init_wager -= wager
print("庄家胜! 当前还有赌注:%d" % init_wager)
else:
print("游戏继续!当前还有赌注:%d" % init_wager)
while init_wager > 0:
if init_wager >= wager:
guess = random.randint(1, 12)
if guess == 7:
init_wager -= wager
print("庄家胜! 当前还有赌注:%d" % init_wager)
break
elif guess == guess_one:
init_wager += wager
print("您获胜! 当前还有赌注:%d" % init_wager)
break
else:
print("游戏继续!当前还有赌注:%d" % init_wager)
if init_wager == 0:
print("您破产了,游戏结束!")
break
if init_wager == 0:
print("您破产了,游戏结束!")
break
# 大佬版
from random import randint
money = 1000
while money > 0:
print('你的总资产为:', money)
needs_go_on = False
while True:
debt = int(input('请下注: '))
if 0 < debt <= money:
break
first = randint(1, 6) + randint(1, 6)
print('玩家摇出了%d点' % first)
if first == 7 or first == 11:
print('玩家胜!')
money += debt
elif first == 2 or first == 3 or first == 12:
print('庄家胜!')
money -= debt
else:
needs_go_on = True
while needs_go_on:
needs_go_on = False
current = randint(1, 6) + randint(1, 6)
print('玩家摇出了%d点' % current)
if current == 7:
print('庄家胜')
money -= debt
elif current == first:
print('玩家胜')
money += debt
else:
needs_go_on = True
print('你破产了, 游戏结束!')
感觉上面代码用到最好的一个地方就是让 “needs_go_on = False”,这样可以在下面一个while循环里面使用,验证此时的状态是否需要进行第二次判断以及后面的判断(多轮摇骰子)。第二就是“first = randint(1, 6) + randint(1, 6)”。因为按照概率来说,1-12和 两个1-6相加还是有些区别的,与实际有一定区别。第三就是它的显示比较多,就是摇骰子的点数都会实时公布,这样的话,游戏过程会更加清楚些。
四.练习
练习1:生成斐波那契数列的前20个数。
说明: 斐波那契数列(Fibonacci sequence),又称黄金分割数列,是意大利数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)在《计算之书》中提出一个在理想假设条件下兔子成长率的问题而引入的数列,所以这个数列也被戏称为"兔子数列"。斐波那契数列的特点是数列的前两个数都是1,从第三个数开始,每个数都是它前面两个数的和,形如:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …。斐波那契数列在现代物理、准晶体结构、化学等领域都有直接的应用。
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
print(a, end=' ')
fibonacci(20)
# 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765
练习2:找出10000以内的完美数。
说明: 完美数又称为完全数或完备数,它的所有的真因子(即除了自身以外的因子)的和(即因子函数)恰好等于它本身。例如:6( 6 = 1 + 2 + 3 6=1+2+3 6=1+2+3)和28( 28 = 1 + 2 + 4 + 7 + 14 28=1+2+4+7+14 28=1+2+4+7+14)就是完美数。完美数有很多神奇的特性,有兴趣的可以自行了解。
# 1 应该不算完美数吧?那我就从2开始好了。(暴力检索==小白?)
for i in range(2, 10000):
perfect = 0
for j in range(1, i):
if i % j == 0:
perfect += j
if i == perfect :
print(perfect)
# 大佬版
import math
for num in range(1, 10000):
result = 0
for factor in range(1, int(math.sqrt(num)) + 1):
if num % factor == 0:
result += factor
if factor > 1 and num // factor != factor:
result += num // factor
if result == num:
print(num)
好就好在了这个下面几点:
- 直接用"int(math.sqrt(num)) + 1",这样检索范围少了好多。
- “if factor > 1 and num // factor != factor: result += num // factor”,这样就是可以直接快速的得到大于"int(math.sqrt(num)) + 1"的因数。(这个说真的,真想不到。然后速度比上面的确快了很多。)
练习3:输出100以内所有的素数。
说明: 素数指的是只能被1和自身整除的正整数(不包括1)。
# 小白版(感觉没啥美感,一天天的暴力检索。。。。)
import math
for i in range(2, 100):
num = 0
for j in range(2, int(math.sqrt(i)) + 1):
if i % j == 0:
num += 1
else:
num += 0
if num == 0:
print(i, end=" ")
# 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 .
# 我傻了,还统计下来干嘛,出现因数直接下一个不好嘛。。。这样太浪费时间空间了。
# 大佬版
import math
for num in range(2, 100):
is_prime = True
for factor in range(2, int(math.sqrt(num)) + 1):
if num % factor == 0:
is_prime = False
break
if is_prime:
print(num, end=' ')
好就好在,这里出现了因数就break,比上面的快很多。
总结
还是真实的案例操作,血淋淋的教育了我一下,其实学的很烂的。这也告诫我后面的程序逻辑得多多磨炼。
溜了遛了,脑壳疼。Loading(05/100)。。。。。。