五十六、质数
原文:http://inventwithpython.com/bigbookpython/project56.html
质数是只能被 1 和它自己整除的数。质数有各种各样的实际应用,但是没有算法可以预测它们;我们必须一次计算一个。然而,我们知道有无限多的质数有待发现。
这个程序通过强力计算找到质数。它的代码类似于项目 24,“因子寻找器。”(另一种描述质数的方式是,一和数本身是它唯一的因子。)你可以从en.wikipedia.org/wiki/Prime_number
那里找到更多关于质数的信息。
运行示例
当您运行primenumbers.py
时,输出将如下所示:
Prime Numbers, by Al Sweigart email@protected
`--snip--`
Enter a number to start searching for primes from:
(Try 0 or 1000000000000 (12 zeros) or another number.)
> 0
Press Ctrl-C at any time to quit. Press Enter to begin...
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, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, `--snip--`
工作原理
isPrime()
函数接受一个整数,如果它是质数,则返回True
。否则,它返回False
。如果你想了解这个项目,项目 24 是值得研究的。isPrime()
函数本质上是寻找给定数字中的任何因子,如果找到任何因子,就返回False
。
这个程序中的算法可以快速找到大质数。一万亿这个数字只有 10 位数。但是要找到像古戈尔一样大的质数(一个 1 后面跟着 100 个 0),你需要使用一种高级算法,比如 Rabin-Miller 素性测试。我的书《Python 密码破解指南》第 22 章有这个算法的 Python 实现。
"""Prime Numbers, by Al Sweigart email@protected
Calculates prime numbers, which are numbers that are only evenly
divisible by one and themselves. They are used in a variety of practical
applications.
More info at: https://en.wikipedia.org/wiki/Prime_number
This code is available at https://nostarch.com/big-book-small-python-programming
Tags: tiny, math, scrolling"""
import math, sys
def main():
print('Prime Numbers, by Al Sweigart email@protected')
print('Prime numbers are numbers that are only evenly divisible by')
print('one and themselves. They are used in a variety of practical')
print('applications, but cannot be predicted. They must be')
print('calculated one at a time.')
print()
while True:
print('Enter a number to start searching for primes from:')
print('(Try 0 or 1000000000000 (12 zeros) or another number.)')
response = input('> ')
if response.isdecimal():
num = int(response)
break
input('Press Ctrl-C at any time to quit. Press Enter to begin...')
while True:
# Print out any prime numbers:
if isPrime(num):
print(str(num) + ', ', end='', flush=True)
num = num + 1 # Go to the next number.
def isPrime(number):
"""Returns True if number is prime, otherwise returns False."""
# Handle special cases:
if number < 2:
return False
elif number == 2:
return True
# Try to evenly divide number by all numbers from 2 up to number's
# square root.
for i in range(2, int(math.sqrt(number)) + 1):
if number % i == 0:
return False
return True
# If this program was run (instead of imported), run the game:
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
sys.exit() # When Ctrl-C is pressed, end the program.
探索程序
试着找出下列问题的答案。尝试对代码进行一些修改,然后重新运行程序,看看这些修改有什么影响。
- 如果将第 22 行的
response.isdecimal()
改为response
,并输入一个非数字作为开始搜索质数的数字,会出现什么错误? - 如果把第 38 行的
number < 2
改成number > 2
会怎么样? - 如果把第 46 行的
number % 1 == 0
改成number % i != 0
会怎么样?
五十七、进度条
原文:http://inventwithpython.com/bigbookpython/project57.html
一个进度条是一个视觉元素,显示任务已经完成了多少。进度条通常与下载文件或软件安装一起使用。这个项目创建了一个getProgressBar()
函数,它根据传递给它的参数返回一个进度条字符串。它模拟了一个下载文件,但是你可以在你自己的项目中重复使用进度条。
运行示例
当您运行progressbar.py
时,输出将如下所示:
Progress Bar Simulation, by Al Sweigart
[█████████ ] 24.6% 1007/4098
工作原理
进度条依赖于在终端窗口中运行的程序可以执行的某种技巧。正如'\n'
和'\t'
分别是换行符和制表符的转义符一样,'\b'
是退格字符的转义符。如果您“打印”一个退格字符,文本光标将向左移动,并擦除先前打印的字符。这只适用于文本光标所在的当前行。如果运行代码print('Hello\b\b\b\b\bHowdy')
,Python 将打印文本"Hello"
,将文本光标向后移动五格,然后打印文本Howdy
。Howdy
文本将覆盖Hello
,让它看起来像是你直接打印了Hello
。
我们可以使用这种技术,通过打印一个版本的进度条,将文本光标移回起点,然后打印一个更新的进度条,在一行中创建一个动画进度条。这种效果可以生成任何文本动画,而不需要像bext
这样的模块,尽管它将被限制在终端窗口中占据一行。
一旦创建了这个程序,就可以通过运行import progressbar
并打印从progressbar.getProgressBar()
返回的字符串,在其他 Python 程序中显示进度条。
"""Progress Bar Simulation, by Al Sweigart email@protected
A sample progress bar animation that can be used in other programs.
This code is available at https://nostarch.com/big-book-small-python-programming
Tags: tiny, module"""
import random, time
BAR = chr(9608) # Character 9608 is '█'
def main():
# Simulate a download:
print('Progress Bar Simulation, by Al Sweigart')
bytesDownloaded = 0
downloadSize = 4096
while bytesDownloaded < downloadSize:
# "Download" a random amount of "bytes":
bytesDownloaded += random.randint(0, 100)
# Get the progress bar string for this amount of progress:
barStr = getProgressBar(bytesDownloaded, downloadSize)
# Don't print a newline at the end, and immediately flush the
# printed string to the screen:
print(barStr, end='', flush=True)
time.sleep(0.2) # Pause for a little bit:
# Print backspaces to move the text cursor to the line's start:
print('\b' * len(barStr), end='', flush=True)
def getProgressBar(progress, total, barWidth=40):
"""Returns a string that represents a progress bar that has barWidth
bars and has progressed progress amount out of a total amount."""
progressBar = '' # The progress bar will be a string value.
progressBar += '[' # Create the left end of the progress bar.
# Make sure that the amount of progress is between 0 and total:
if progress > total:
progress = total
if progress < 0:
progress = 0
# Calculate the number of "bars" to display:
numberOfBars = int((progress / total) * barWidth)
progressBar += BAR * numberOfBars # Add the progress bar.
progressBar += ' ' * (barWidth - numberOfBars) # Add empty space.
progressBar += ']' # Add the right end of the progress bar.
# Calculate the percentage complete:
percentComplete = round(progress / total * 100, 1)
progressBar += ' ' + str(percentComplete) + '%' # Add percentage.
# Add the numbers:
progressBar += ' ' + str(progress) + '/' + str(total)
return progressBar # Return the progress bar string.
# If the program is run (instead of imported), run the game:
if __name__ == '__main__':
main()
在输入源代码并运行几次之后,尝试对其进行实验性的修改。你也可以自己想办法做到以下几点:
- 创建一个旋转器的单行动画,它在角色
|
、/
、-
和\
之间交替,以产生旋转效果。 - 创建一个程序,可以显示从左向右移动的文本滚动字幕。
- 创建一个单行动画,显示一组四个等号作为一个单元来回移动,类似于电视节目霹雳游侠中机器人车上的红色扫描灯或电视节目太空堡垒卡拉狄加中的赛昂机器人脸。
探索程序
试着找出下列问题的答案。尝试对代码进行一些修改,然后重新运行程序,看看这些修改有什么影响。
- 如果删除或注释掉第 29 行的
print('\b' * len(barStr), end='', flush=True)
会发生什么? - 如果把第 48 行和第 49 行的顺序对调会怎么样?
- 如果把 53 行的
round(progress / total * 100, 1)
改成round(progress / total * 100)
会怎么样?
五十八、彩虹
原文:http://inventwithpython.com/bigbookpython/project58.html
彩虹是一个简单的程序,显示了一个彩色的彩虹在屏幕上来回移动。该程序利用了这样一个事实,即当新的文本行出现时,现有的文本会向上滚动,使其看起来像是在移动。这个项目对初学者来说很好,它类似于项目 15“深坑”
运行示例
图 58-1 显示了运行rainbow.py
时的输出。
:彩虹的锯齿形输出,在屏幕上是彩色的
工作原理
这个程序连续打印相同的彩虹图案。改变的是打印在它左边的空格字符的数量。增加这个数字会使彩虹向右移动,减少这个数字会使彩虹向左移动。indent
变量跟踪空格的数量。将indentIncreasing
变量设置为True
表示indent
应该增加,直到到达60
,此时变为False
。代码的其余部分减少了空格的数量。一旦到达0
,它又变回True
,重复彩虹的之字形。
"""Rainbow, by Al Sweigart email@protected
Shows a simple rainbow animation. Press Ctrl-C to stop.
This code is available at https://nostarch.com/big-book-small-python-programming
Tags: tiny, artistic, bext, beginner, scrolling"""
import time, sys
try:
import bext
except ImportError:
print('This program requires the bext module, which you')
print('can install by following the instructions at')
print('https://pypi.org/project/Bext/')
sys.exit()
print('Rainbow, by Al Sweigart email@protected')
print('Press Ctrl-C to stop.')
time.sleep(3)
indent = 0 # How many spaces to indent.
indentIncreasing = True # Whether the indentation is increasing or not.
try:
while True: # Main program loop.
print(' ' * indent, end='')
bext.fg('red')
print('##', end='')
bext.fg('yellow')
print('##', end='')
bext.fg('green')
print('##', end='')
bext.fg('blue')
print('##', end='')
bext.fg('cyan')
print('##', end='')
bext.fg('purple')
print('##')
if indentIncreasing:
# Increase the number of spaces:
indent = indent + 1
if indent == 60: # (!) Change this to 10 or 30.
# Change direction:
indentIncreasing = False
else:
# Decrease the number of spaces:
indent = indent - 1
if indent == 0:
# Change direction:
indentIncreasing = True
time.sleep(0.02) # Add a slight pause.
except KeyboardInterrupt:
sys.exit() # When Ctrl-C is pressed, end the program.
探索程序
试着找出下列问题的答案。尝试对代码进行一些修改,然后重新运行程序,看看这些修改有什么影响。
- 如果把第 44 行的
False
改成True
会怎么样? - 如果将所有
bext.fg()
调用的参数都改为'random'
,会发生什么?
五十九、石头剪刀布
原文:http://inventwithpython.com/bigbookpython/project59.html
在这个版本的双人手游中,玩家面对电脑。你可以选择石头、布或剪刀。石头打败剪刀,剪刀打败布,布打败石头。这个程序增加了一些短暂的停顿来制造悬念。
这个游戏的一个变种,见项目 60,“石头剪刀布(必胜版本)。”
运行示例
当您运行rockpaperscissors.py
时,输出将如下所示:
Rock, Paper, Scissors, by Al Sweigart email@protected
- Rock beats scissors.
- Paper beats rocks.
- Scissors beats paper.
0 Wins, 0 Losses, 0 Ties
Enter your move: (R)ock (P)aper (S)cissors or (Q)uit
> r
ROCK versus...
1...
2...
3...
SCISSORS
You win!
1 Wins, 0 Losses, 0 Ties
Enter your move: (R)ock (P)aper (S)cissors or (Q)uit
`--snip--`
工作原理
石头剪刀布的游戏逻辑相当简单,我们在这里用if
- elif
语句实现它。为了增加一点悬念,第 45 至 51 行在揭示对手的移动之前倒计时,在计数之间有短暂的停顿。这给了玩家一段时间,让他们对游戏的结果感到兴奋。如果没有这种停顿,结果会在玩家开始移动时立即出现——有点虎头蛇尾。为玩家改善用户体验并不需要很多代码。
"""Rock, Paper, Scissors, by Al Sweigart email@protected
The classic hand game of luck.
This code is available at https://nostarch.com/big-book-small-python-programming
Tags: short, game"""
import random, time, sys
print('''Rock, Paper, Scissors, by Al Sweigart email@protected
- Rock beats scissors.
- Paper beats rocks.
- Scissors beats paper.
''')
# These variables keep track of the number of wins, losses, and ties.
wins = 0
losses = 0
ties = 0
while True: # Main game loop.
while True: # Keep asking until player enters R, P, S, or Q.
print('{} Wins, {} Losses, {} Ties'.format(wins, losses, ties))
print('Enter your move: (R)ock (P)aper (S)cissors or (Q)uit')
playerMove = input('> ').upper()
if playerMove == 'Q':
print('Thanks for playing!')
sys.exit()
if playerMove == 'R' or playerMove == 'P' or playerMove == 'S':
break
else:
print('Type one of R, P, S, or Q.')
# Display what the player chose:
if playerMove == 'R':
print('ROCK versus...')
playerMove = 'ROCK'
elif playerMove == 'P':
print('PAPER versus...')
playerMove = 'PAPER'
elif playerMove == 'S':
print('SCISSORS versus...')
playerMove = 'SCISSORS'
# Count to three with dramatic pauses:
time.sleep(0.5)
print('1...')
time.sleep(0.25)
print('2...')
time.sleep(0.25)
print('3...')
time.sleep(0.25)
# Display what the computer chose:
randomNumber = random.randint(1, 3)
if randomNumber == 1:
computerMove = 'ROCK'
elif randomNumber == 2:
computerMove = 'PAPER'
elif randomNumber == 3:
computerMove = 'SCISSORS'
print(computerMove)
time.sleep(0.5)
# Display and record the win/loss/tie:
if playerMove == computerMove:
print('It\'s a tie!')
ties = ties + 1
elif playerMove == 'ROCK' and computerMove == 'SCISSORS':
print('You win!')
wins = wins + 1
elif playerMove == 'PAPER' and computerMove == 'ROCK':
print('You win!')
wins = wins + 1
elif playerMove == 'SCISSORS' and computerMove == 'PAPER':
print('You win!')
wins = wins + 1
elif playerMove == 'ROCK' and computerMove == 'PAPER':
print('You lose!')
losses = losses + 1
elif playerMove == 'PAPER' and computerMove == 'SCISSORS':
print('You lose!')
losses = losses + 1
elif playerMove == 'SCISSORS' and computerMove == 'ROCK':
print('You lose!')
losses = losses + 1
在输入源代码并运行几次之后,尝试对其进行实验性的修改。你也可以自己想办法做到以下几点:
- 在游戏中加入“蜥蜴”和“斯波克”的招式。蜥蜴毒死斯波克,吃纸,却被石头碾碎,被剪刀斩首。斯波克折断剪刀,蒸发岩石,但被蜥蜴毒死,被纸证明是错误的。
- 允许玩家每次胜利赢得一分,每次失败失去一分。一旦获胜,玩家还可以承担“双倍或零”的风险,以可能在随后的回合中赢得 2、4、8、16 以及更多的点数。
探索程序
试着找出下列问题的答案。尝试对代码进行一些修改,然后重新运行程序,看看这些修改有什么影响。
- 如果将第 54 行的
random.randint(1, 3)
改为random.randint(1, 300)
,会得到什么错误? - 如果把第 65 行的
playerMove == computerMove
改成True
会怎么样?
六十、石头剪刀布(必胜版本)
原文:http://inventwithpython.com/bigbookpython/project60.html
石头剪刀布的变体与项目 59“石头剪刀布”相同,只是玩家总是赢。选择计算机招式的代码被设置为总是选择失败的招式。你可以把这个游戏提供给你的朋友,他们赢的时候可能会很兴奋。。。一开始。看看他们需要多长时间才能意识到游戏被操纵对他们有利。
运行示例
当您运行rockppapersscissorsalwayswin.py
时,输出将如下所示:
Rock, Paper, Scissors, by Al Sweigart email@protected
- Rock beats scissors.
- Paper beats rocks.
- Scissors beats paper.
0 Wins, 0 Losses, 0 Ties
Enter your move: (R)ock (P)aper (S)cissors or (Q)uit
> p
PAPER versus...
1...
2...
3...
ROCK
You win!
1 Wins, 0 Losses, 0 Ties
Enter your move: (R)ock (P)aper (S)cissors or (Q)uit
> s
SCISSORS versus...
1...
2...
3...
PAPER
You win!
2 Wins, 0 Losses, 0 Ties
`--snip--`
SCISSORS versus...
1...
2...
3...
PAPER
You win!
413 Wins, 0 Losses, 0 Ties
Enter your move: (R)ock (P)aper (S)cissors or (Q)uit
`--snip--`
工作原理
你可能会注意到这个版本的程序比项目 59 要短。这是有意义的:当你不必为计算机随机生成一步棋并计算游戏的结果时,你可以从原始代码中删除相当多的代码。也没有变量来跟踪损失和平局的数量,因为无论如何这些都是零。
"""Rock,Paper, Scissors (Always Win version)
By Al Sweigart email@protected
The classic hand game of luck, except you always win.
This code is available at https://nostarch.com/big-book-small-python-programming
Tags: tiny, game, humor"""
import time, sys
print('''Rock, Paper, Scissors, by Al Sweigart email@protected
- Rock beats scissors.
- Paper beats rocks.
- Scissors beats paper.
''')
# These variables keep track of the number of wins.
wins = 0
while True: # Main game loop.
while True: # Keep asking until player enters R, P, S, or Q.
print('{} Wins, 0 Losses, 0 Ties'.format(wins))
print('Enter your move: (R)ock (P)aper (S)cissors or (Q)uit')
playerMove = input('> ').upper()
if playerMove == 'Q':
print('Thanks for playing!')
sys.exit()
if playerMove == 'R' or playerMove == 'P' or playerMove == 'S':
break
else:
print('Type one of R, P, S, or Q.')
# Display what the player chose:
if playerMove == 'R':
print('ROCK versus...')
elif playerMove == 'P':
print('PAPER versus...')
elif playerMove == 'S':
print('SCISSORS versus...')
# Count to three with dramatic pauses:
time.sleep(0.5)
print('1...')
time.sleep(0.25)
print('2...')
time.sleep(0.25)
print('3...')
time.sleep(0.25)
# Display what the computer chose:
if playerMove == 'R':
print('SCISSORS')
elif playerMove == 'P':
print('ROCK')
elif playerMove == 'S':
print('PAPER')
time.sleep(0.5)
print('You win!')
wins = wins + 1
在输入源代码并运行几次之后,尝试对其进行实验性的修改。你也可以自己想办法做到以下几点:
- 在游戏中加入“蜥蜴”和“斯波克”的招式。蜥蜴毒死斯波克,吃纸,却被石头碾碎,被剪刀斩首。斯波克折断剪刀,蒸发岩石,但被蜥蜴毒死,被纸证明是错误的。
- 允许玩家每赢一次就赢得一分。一旦获胜,玩家还可以承担“双倍或零”的风险,以可能赢得 2、4、8、16 以及更多的点数。
探索程序
试着找出下列问题的答案。尝试对代码进行一些修改,然后重新运行程序,看看这些修改有什么影响。
- 如果删除或注释掉第 33 到 57 行会发生什么?
- 如果把第 22 行的
input('> ').upper()
改成input('> ')
会怎么样?