计算概论(C)作业 5

编程

两个随机整数的差的绝对值的数学期望值

题目

取 0 到 100(含)之间的两个随机整数,求出它们的差的绝对值,请问这个值的数学期望值是多少?(可以模拟1000次,得到其平均值,当成其数学期望的近似值)。

思路

题目要求我们模拟 1000 次。因此,我们需要进行 1000 次循环。

随机整数

在每一个循环里,我们需要生成两个随机整数。

生成随机整数的方法是 random.randint(x,y) ,其中 $x$ 和 $y$ 表示随机整数的范围 $[x,y]$ ,要注意的是 $x$ 和 $y$ 是包括在取值范围内的。在这个题目里,我们会用 random.randint(0,100) 来取 0 到 100 之间的随机整数。

接着,我们对两个随机整数取差后,取绝对值。

绝对值的定义如下:
$$
\left | x \right | =
\begin{cases}
\hfill x & \text{if $x\geq 0$}\
-x & \text{if $x<0$}
\end{cases}
$$
当差大于等于零时,不需要理会。当差小于零时,需要将差乘以 $-1$ 。翻译为 Python 就是:

if difference < 0:
        difference = difference * (-1)

平均值

可以模拟 1000 次,得到其平均值,当成其数学期望的近似值

题目希望我们得到他的平均值。平均值是这样算的:
$$
平均值 = \frac {总和}{项数} = \frac {累计每一次差的绝对值}{ 1000次} = \frac {列表总和}{列表长度}
$$

方法 1:s+=

每一个循环里,我们都将这个差 difference 累计起来,存到总和 total 里。

total += difference

最后我们把 total 除以项数即可得到平均值。

print(total/sim_rounds)
方法 2:存到列表

我们将每次循环得到的 difference 放入列表 diff_list 中。

diff_list.append(difference)

最后求平均值 sum() / len() 。其中 sum() 会返回列表里每一个项相加的总和,len() 函数则会返回列表的长度,也就是我们的项数 1000 。

print(sum(diff_list)/len(diff_list))

答案

这是使用 s+= 求和,然后得到平均值的方法:

import random

total = 0
sim_rounds = 1000

for x in range(0,sim_rounds):
    num1 = random.randint(0,100)
    num2 = random.randint(0,100)

    difference = num1 - num2
    # get absolute value
    if difference < 0:
        difference = difference * (-1)
    
    # Add difference to total
    total += difference

# get average
print(total/sim_rounds)

我们也可以将得到的值放入列表中,最后求平均值 sum() / len()

import random

total = 0
sim_rounds = 1000
diff_list = []

for x in range(0,sim_rounds):
    num1 = random.randint(0,100)
    num2 = random.randint(0,100)

    difference = num1 - num2
    # get absolute value
    if difference < 0:
        difference = difference * (-1)
    
    # Add difference to total
    diff_list.append(difference)

# get average
print(sum(diff_list)/len(diff_list))

0 到 1 之间的随机实数的平均值与标准差

题目

用循环得到 1000 个数:每个数是由 6 个随机实数 (0到1之间的)的平均值而得到的,将这个数 append() 到列表中。计算这 1000 个数的平均值及标准差。

思路

生成随机实数

题目要求我们模拟 1000 次。

我们需要进行 1000 次大循环,得到 1000 个数,每一个数都依赖于 6 个随机实数的平均。

每一次大循环里,进行 6 次的小循环。每一个小循环里,生成 1 个随机实数。最后在小循环结束后,得到 6 个随机实数的平均值。

因此,这个题目里我们会用到双层嵌套循环(double nested loop),结构如下:

flowchart TD
    B{For 循环: 小于 1000 次吗 } -->|True| C{For 循环: 小于 6 次吗 }
    C -->|True| D[生成一次随机实数] --> I[加到 average]
    C -->|False| G([结束 For 循环]) --> H[average 除以 6 得到六个随机实数的平均] --> J[ append 到列表中] --> B
    B ---->|False| E([结束 For 循环])

要生成一个 0 到 1 之间的随机实数,我们要使用 random.random()

得到 6 个随机实数的平均值的方法,正是上一题里 s+= 的方法,只不过在这题里只需要把总和除以 6 就能得出平均值。

所以,这部分是这样写的:

for y in range(sim_rounds):
    average = 0
    for x in range(6):
        num = random.random()
        average += num
    average /= 6
    my_list.append(average)

平均函数

接着我们需要一个计算平均的函数。

sum() 一个列表会返回列表里每一个项相加的总和;len() 一个列表则会返回列表的长度,也就是列表里元素的数量。

由于 $平均 = \frac {总和}{项数} = \frac {列表总和}{列表长度}$ ,只要 sum(列表)/len(列表) 就会得出平均值。

定义一个计算平均的函数:输入参数为列表 lst ,返回平均值。

def Average(lst):
    return sum(lst) / len(lst)

标准差函数

接着,我们需要一个计算标准差的函数。

先看下平均、方差和标准差的定义。

概念 公式
平均(Mean) $\mu$ $\mu = \frac{1}{N}\sum_{i=1}^N x_i$
方差(Variance)$\sigma^2$ $\sigma^2 = \frac{1}{N} \sum_{i=1}^N (x_i - \mu)^2$
标准差(Standard Deviation)$\sigma$ $\sigma = \sqrt{\frac{1}{N} \sum_{i=1}^N (x_i - \mu)^2}$

输入一个列表,我们要得到标准差了。定义一个标准差函数,平均函数刚才我们已经写过了,可以调用之前的平均函数。只需要把方差和标准差的数学语言翻译成 Python 编程语言:

def Standard_deviation(lst):
    mean = Average(lst)
    variance = sum([((x - mean) ** 2) for x in lst]) / len(lst)
    res = variance ** 0.5
    return res

我们先计算平均,接着是方差,接着将方差取根号得到标准差。

值得留意的是 Python 中的幂函数表示法。在 Python 里,** 代表数学的幂函数。a ** b 会返回 $a$ 的 $b$ 次幂,即 $a^b$ 。

举些例子,当前 a 值为 4。

试下 a ** 2 ,也就是 $a^2$ ,会得到结果 16。

>>> print(a ** 2)
16

试下 a ** 0.5 ,也就是 $a^{0.5} = \sqrt{a}$ ,会得到结果 2。

>>> print(a ** 0.5)
2.0

答案

把思路里的三个部分结合起来,就能得出最终答案。

import random

my_list = []
sim_rounds = 1000

def Average(lst):
    return sum(lst) / len(lst)

def Standard_deviation(lst):
    mean = Average(lst)
    variance = sum([((x - mean) ** 2) for x in lst]) / len(lst)
    res = variance ** 0.5
    return res

for y in range(sim_rounds):
    average = 0
    for x in range(6):
        num = random.random()
        average += num
    average /= 6
    my_list.append(average)

print("Average is: ", Average(my_list))
print("Standard Deviation is: ", Standard_deviation(my_list))

阶乘与组合数

题目

組合数 $C(m,n)=\frac{m!}{n!(m-n)!}$。试编写阶乘的函数及组合数的函数,并调用求出 $C(6,4)$。

思路

组合数 $C(m,n) = {m \choose n}$ 的定义为:从 $m$ 个不同元素中取出 $n$ 个元素的所有不同组合的个数。

计算组合数,可以使用公式:
$$
C(m,n)=\frac{m!}{n!(m-n)!}
$$
由此公式可以得知,只要计算出阶乘,就能得到组合数。

阶乘函数

一个阶乘(Factorial)是所有小于及等于该数的正整数的积,计作 $n!$。特别地 $0!=1$。
$$
n! = (n-1)! \times n \ 1! = 0! \times 1 = 1
\
2! = 1! \times 2
\
3! = 2! \times 3
\
4! = 3! \times 4
$$
从这个规律我们能看出:只要从 $1!=1$ 开始,自增 1,然后再乘以自己,就能得到下一个阶乘,如此类推。

因此,我们只要写一个循环,做这个步骤就能算出阶乘。

def factorial(x):
    fact = 1
    for i in range(1,x+1):
        fact = fact * i
    return fact

组合数函数

我们利用公式 $C(m,n)=\frac{m!}{n!(m-n)!}$ ,阶乘部分调用刚才写的阶乘函数即可。

def combination(m,n):
    return factorial(m)/factorial(n)/factorial(m-n)

答案

import random

m = int(input("Input your m: "))
n = int(input("Input your n: "))

# Calculate factorial
def factorial(x):
    fact = 1
    for i in range(1,x+1):
        fact = fact * i
    return fact

# Calculate combination
def combination(m,n):
    return factorial(m)/factorial(n)/factorial(m-n)

print("{:.0f}".format(combination(m,n)))

在代码的最后一行里,由于组合数函数里使用了除法,Python 默认结果为浮点数,e.g. 307.0 ,带有小数部分。

一个组合数应该是整数。因此,我们需要对输出结果进行格式控制。

print("{:.0f}".format(combination(m,n)))

浮点数格式有很多用法:

数字 格式 输出 描述
3.1415926 {:.2f} 3.14 前两个小数位
3.1415926 {:+.2f} +3.14 前两个小数位(带正负)
2.71828 {:.0f} 3 无小数位

质数判断

题目

先定义一个函数判断一个数是否是质数,主程序将 2 到 100 以内的质数 append() 到一个列表中,并看看总共有多少个质数。

思路

质数(素数)的定义是:大于 1 的自然数中,除了 1 和该数自身外,无法被其他自然数整除的数。

判断是否整除的方法可以利用取模运算符 % 返回除法的余数。当 a % b 余数为 0 时,代表 $a$ 能够被 $b$ 整除,即 $b$ 是 $a$ 的因数。在这个情况中,如果 $b$ 不是 1 和 $a$ 自身,则 $a$ 不是质数。

所以,我们做一个 for 循环,从 2 开始。在每一次循环里,我们都通过取模来验证是否质数。

if (x % i) == 0:
    return False

循环的范围只需要从 $2$ 到 $n/2$ ,而不需要到 $n$ 。如果一个数能被 $[2,\frac{n}{2}]$ 内的数字整除,这个数就不是质数。

for i in range(2, int(x/2)+1):
    # If num is divisible by any number between 2 and n / 2, it is not prime
    if (x % i) == 0:
        return False

循环体本身的范围,是 int(n/2) + 1

int(n/2) 防止 $n$ 不被 2 整除而出现小数位的情况。

+1 加一保证第 int(n/2) 个数也会被测试到。一个 range() 函数是不包含最后一个数的。

for i in range(2, 5): 
    print(i) # 2, 3, 4

比如,上面这段代码的输出是 2,3,4 ,不包含 5 。要遍历到最后一个数,需要加一 +1

for i in range(2, 5+1): 
    print(i) # 2, 3, 4, 5

答案

# Calculate factorial
def isPrime(x):
    # Iterate from 2 to n / 2
    for i in range(2, int(x/2)+1):
        # If num is divisible by any number between 2 and n / 2, it is not prime
        if (x % i) == 0:
            return False
    else:
        return True

stop = 100
primeNumbers = []

for x in range(2,stop+1): # [2,100]
    if isPrime(x):
        primeNumbers.append(x)

print("List of prime numbers: ",primeNumbers)
print("Number of prime numbers: ",len(primeNumbers))