美文网首页UNSW COMP9021 2019T2
COMP9021 Principles of Programmi

COMP9021 Principles of Programmi

作者: Sisyphus235 | 来源:发表于2017-08-06 14:54 被阅读0次

    1. Monty Hall Problem

    一个著名的博弈论游戏,起源大概是一个美国电视游戏节目叫做Let's make a deal,这个问题名字来源于节目主持人Monty Hall
    游戏简述:
    “参赛者会看见三扇关闭了的门,其中一扇的后面有一辆汽车或者是奖品,选中后面有车的那扇门就可以赢得该汽车或奖品,而另外两扇门后面则各藏有一只山羊或者是后面没有任何东西。当参赛者选定了一扇门,但未去开启它的时候,知道门后情形的节目主持人会开启剩下两扇门的其中一扇,露出其中一只山羊。主持人其后会问参赛者要不要换另一扇仍然关上的门。问题是:换另一扇门会否增加参赛者赢得汽车的机会率?如果严格按照上述的条件的话,答案是会。—换门的话,赢得汽车的概率是2/3。”
    用python仿写这个游戏

    大多程序都可以拆解为输入、处理和输出。本题就是按照这个思路写出相应的程序

    1.1 输入1--模拟次数

    While True:
    #注意boolean的书写是大写的T,True
        try:
            n = int(input('How nany times do you want to run the simulation? '))
            #如果出现异常,不会继续运行try后面的程序行,而是跳入exceept程序段执行程序
            break
            #跳出当前循环,注意,如果有多个循环的时候,一次break不能跳出所有循环。
        except ValueError:
            print('Incorrect input, try again')
    #这样实现了用户多次输入的功能,允许用户多次输入来调整想要的输入值。
    

    这段程序使用的是之前lab1用过的try ... except ...被动异常捕获方法,但是只能保证输入值是int,而我们的输入值不仅是int,还需要>0,这里新引入一种主动异常处理方式--raise

    While True:
        try:
            n = int(input('How nany times do you want to run the simulation? '))
            if n < = 0:
                raise ValueError
                #rasie命令使得程序进入except ValueError程序块,不执行后续的try程序块的命令。
            break
        except ValueError:
            print('Incorrect input, try again')
    

    1.2 输入2--更改答案

    和上文同理

    while True:
        try:
            n = int(input('How nany times do you want to run the simulation? '))
            if n <= 0:
                raise ValueError
            break
        except ValueError:
            print('Incorrect input, try again')
    print(n)
    
    while True:
        switch = input('Do you want to switch? ')
        if switch in {'yes', 'Yes', 'y', 'Y'}:
            switch = True
            #由于switch只有两种情况,赋予boolean值效率最高,不要赋予其他int值。
            break
        if switch in {'no', 'No', 'n', 'N'}:
            switch = False
            break
        else:
            print('Incorrect input, try again')
    

    1.3 游戏初始化

    在三扇门后面要随机选择一个放入大奖,需要用随机函数来写,之前讲过import random,接下来的问题是查询使用哪个函数更方便。查询random的函数方法是dir(random),但是其中会有很多initilize的函数,这些函数命名两端是_,把他们删除的显示方法如下

    import random
    [x for x in dir(random) if not x.startswith('_')]
    

    上文程序写法是简化的list生成方法,它等于下文程序

    import random
    result = []
    for x in dir(random):
        if not x.startswith('_'):
            result.append(x)
    result
    

    从中选取了choice函数,因为它的属性是Choose a random element from a non-empty sequence.

    from random import *
    doors = ['A', 'B', 'C']
    #用list记录三扇门
    nb_of_wins = 0
    #记录获奖次数,初始化为0
    winning_door = choice(doors)
    #随机从doors的list中选择一个放置大奖
    first_choice = doors.pop(randrange(3))
    #在index[0,2]中随机抽选一个int作为用户的初始选择index,并把doors中对应index的门assign给first_choice,再从doors中删除
    

    1.4 游戏过程

    游戏过程是主持人删除了一个错误答案,玩家决定是否更换选项。

    if not switch:
    #前文把switch赋予了boolean值,所以可以直接用not switch判断True or False
        second_choice = first_choice
    if first_choice = winning_door:
    #如果first_choice是winning_door,打开的门可以是doors剩下两扇门中的任何一个
        opened_door = doors.pop(randrange(2))
        #从剩下两扇门中随意找到一扇门打开,接下来是要看玩家选项是否switch
        if switch:
            second_choice = doors[0]
            #因为此时doors的list中只有一个元素,所以switch的话,就把doors中剩下的这个元素(index一定是0)assign给second_choice
        else:
            nb_of_wins += 1
            #因为最开始已经定义not switch时second_choice的赋值问题,所以可以直接确定中奖。
    else:
        doors.remove(winning_door)
        #因为first_choice不是大奖,所以要从doors的list中删除大奖的门,大奖的门不能被主持人打开
        opened_door = doors[0]
        #此时doors的list中只剩下一个非大奖选项
        if switch:
            second_choice = winning_door
            nb_of_wins += 1      
    

    1.5 模块组合和结果输出

    输入模块、游戏初始化和游戏过程都已经写好,接下来整合模块,并且按照要求输出结果

    from random import choice, randrange
    
    while True:
        try:
            n = int(input(input('How nany times do you want to run the simulation? '))
            if n <= 0:
                raise ValueError
            break
        except ValueError:
            print('Incorrect input, try again')
    
    while True:
        switch = input('Do you want to switch? ')
        if switch in {'y', 'Y', 'yes', "Yes'}:
            switch = True
            break
        if switch in {'n', 'N', 'no', ‘No'}:
            switch = False
            break
        print('Incorrect input, try again')
    
    for _ in range(n):
        doors = ['A', 'B', 'C']
        winning_door = choice(doors)
        nb_of_wins = 0
        first_choice = doors.pop(randrange(3))
        if not switch:
            second_choice = first_choice
    
        if first_choice == winning_door:
            opened_door = doors.pop(randrange(2))
            if switch:
                second_choice = doors[0]
            else:
                nb_of_wins += 1
        else:
            doors.remove(winning_door)
            opened_door = doors[0]
            if switch:
                nb_of_wins += 1
                second_choice = winning_door
        
        print('Winning door:', winning_door)
        print('First choice:', first_choice)
        print('Opened door:', opened_door)
        print('Seconc choice:', second_choice)
        print()
    print(nb_of_wins)
    

    运行结果会发现switch的获奖概率是2/3,not switch的获奖概率是1/3。

    2. PDF automated form filling

    这个部分的内容没有详细讲解,主要是展示有这样的可能性。运行相关程序的时候需要install两个module,一个是PyPDF2,另一个是pyautogui,方法如下:

    pip3 install PyPDF2
    pip3 install pyobjc-core
    pip3 install pybojc
    pip3 install pyautogui
    

    实际上打开这个程序的python源文档,并不难理解,大部分是字符串操作。

    3. Elementary cellular automata (元胞自动机)

    简单说:以1个符号作为种子,根据某种规则向下衍生。

    3.1 规则制定

    本问题的规则是根据一个输入的数转化为2进制后制定的,先要实现二进制显示8个数字的功能。以输入90为例:

    bin(90)
    >>> '0b1011010'
    

    转化后数字前面有二进制符号0b,需要把它们去掉

    bin(90)[2:]
    >>> '1011010'
    

    然后发现不同的数字二进制转化后并不能确定是8位数,所以要在前面补全数字0

    '0' * (8 - len(bin(90)[2:]) + bin(90)[2:]
    >>> '01011010'
    

    这样的程序虽然达到了目的,但是太过复杂,想到之前提到过的格式化输出,可以利用string来帮助简化程序

    f'{90:08b}'
    >>> '01011010'
    

    接下来完成输入值90的规则制定

    n = f'{90:08b}'
    rules = {}
    for i in range(8):
        rules[(i // 4, i // 2 % 2, i % 2)] = n[7 - i]
    

    使用python的简化写法

    n = f'{90:08b}'
    rules = {(i // 4, i // 2 % 2, i % 2): n[7 - i] for i in range(8)}
    

    3.2 程序实现

    def print_line(line):
    #根据list调整输出格式
        for e in line:
            if e:
            #如果某个位置是非0的
                print('*', end = '')
            else:
                print(' ', end = '')
        print()
        #换行输出
    
    n = f'{90:08b}'
    rules = {(i // 4, i // 2 % 2, i % 2): int(n[7 - i]) for i in range(8)}
    
    line = [0] * 20 + [1] + [0] * 20
    #初始化,左右两边各20个0,中间有个1
    print_line(line)
    #输出初始化的第一行,后续行根据规则产生,边界是不能超出41个数字(20个0,1个1,20个0是初始化的位置)
    for i in range(20):
    #每次根据规则产生新的一行时,1的左右两侧各使用了1个0,所以只能产生20行,否则超出临界值。
        new_line = [0] * 41
        for j in range(20 - i, 20 + i + 1):
        #第i行需要根据规则产生数字的区域是[20-i, 20+i]
            new_line[j] = rules[line[j - 1], line[j], line[j + 1]]
        line = new_line
        print_line(line)
    >>>
                        *                    
                       * *                   
                      *   *                  
                     * * * *                 
                    *       *                
                   * *     * *               
                  *   *   *   *              
                 * * * * * * * *             
                *               *            
               * *             * *           
              *   *           *   *          
             * * * *         * * * *         
            *       *       *       *        
           * *     * *     * *     * *       
          *   *   *   *   *   *   *   *      
         * * * * * * * * * * * * * * * *     
        *                               *    
       * *                             * *   
      *   *                           *   *  
     * * * *                         * * * * 
    

    4. Rational Number

    有理数用小数的形式表现的时候小数点后分为两个部分,有限的无规律数字组+无限的有规律的循环数字组。(Eric给的课上代码是有错误的,正确的如下)

    sigma = input('Input sigma: ')
    tau = input('Input tau:')
    p = int(sigma) * ((10 ** (len(tau))) - 1) + int(tau)
    q = ((10 ** len(tau)) -1) * 10 ** len(sigma)
    print(p/q)
    
    #[sigma * (10^{|tau|} - 1) + tau] /
    #            [(10^{|tau|} - 1) * 10^{|sigma|}]
    #该公式是从0.(sigma)(tau)(tau)(tau)...
              = sigma * 10^{-|sigma|} + tau(10^{-|sigma|-|tau|} +
                                            10^{-|sigma|-2|tau|} +
                                            ...)推导出来的
    >>>
    Input sigma: 27
    Input tau:343
    0.27343343343343346
    

    在这个问题的python源文档中,有一种需要注意的函数调用方法

    def f():
        return (2, 6)
    def g(m, n):
        return m * n
    print(g(*f()))
    #注意f()前边的*,因为函数f()返回的是一个tuple(2,6),这是一个元素,而g()需要两个数的输入,所以要用*f()把tuple(2, 6)这样一个元素转化为两个元素。
    

    再举一个例子

    def f(a, b):
        return a + b
    f(*(2, 6))
    >>> 8
    

    相关文章

      网友评论

        本文标题:COMP9021 Principles of Programmi

        本文链接:https://www.haomeiwen.com/subject/yjhblxtx.html