Julia 科学计算应用算例 (1)

导入模块

using Pkg

using BenchmarkTools

问题

Problem 1

不使用 Julia 自带的 factorial(n) 函数, 用 for 编写一个计算 n ! n! n! (n 阶乘, n ! = n × ( n − 1 ) × ⋯ × 2 × 1 n! = n \times (n−1)\times \cdots \times 2 \times 1 n!=n×(n1)××2×1) 的函数, 函数名称为 factorial2

示例

factorial(3) 的计算结果为 6

factorial(3)
6

参考解答

function factorial2(n::Int64)::Int64
  res = 1
  for i in 1:n
    res = res*i
  end 
  return res
end
factorial2 (generic function with 1 method)
# 不指定类型
function factorial3(n)
    k = 1
    for i in 1:n
        k *= i  # or k = k * i
    end
    return k
end
factorial3 (generic function with 1 method)

速度对比

n s < μ s < m s < s ns < \mu s < ms <s ns<μs<ms<s

纳秒 < 毫秒 < 微妙 < 秒

n = 10
@benchmark factorial(n)
BenchmarkTools.Trial: 
  memory estimate:  16 bytes
  allocs estimate:  1
  --------------
  minimum time:     19.658 ns (0.00% GC)
  median time:      26.680 ns (0.00% GC)
  mean time:        32.284 ns (7.24% GC)
  maximum time:     3.939 μs (99.08% GC)
  --------------
  samples:          10000
  evals/sample:     997
@benchmark factorial2(n)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     6.399 ns (0.00% GC)
  median time:      7.700 ns (0.00% GC)
  mean time:        9.307 ns (0.00% GC)
  maximum time:     577.694 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1000
@benchmark factorial3(n)
BenchmarkTools.Trial: 
  memory estimate:  16 bytes
  allocs estimate:  1
  --------------
  minimum time:     22.946 ns (0.00% GC)
  median time:      30.461 ns (0.00% GC)
  mean time:        36.722 ns (7.05% GC)
  maximum time:     4.007 μs (98.62% GC)
  --------------
  samples:          10000
  evals/sample:     998

从上面的速度测试来看, factorial2 和 factorial3 相比说明, 指定参数类型的函数比不指定参数类型的函数快; factorial 和 factorial2 相比说明规范的手写算法在某些情况下可能会比 Julia 自带的函数相比还要快, 这在 python 中是不常见的, 尤其是在使用 for 函数嵌套结构的情况下.

Problem2

问题2: 二项分布随机变量 Y 表示了 n 次二元实验中成功的次数, 而每次实验成功的概率为 p. 用数学形式描述为, Y ∼ B i n ( n , p ) Y \sim Bin(n, p) YBin(n,p).

请使用 base 库的 rand 函数编写产生二项分布随机变量.

Hint: 如果 U U U 服从 (0, 1) 的均匀分布, 且 p ∈ ( 0 , 1 ) p \in (0, 1) p(0,1), 那么 U < p U \lt p U<p 意味着概率 p 条件下为真 (成功) 的情况

描述: 可以参考 Distributions 库的 Binomial函数. Binomial 第一个参数为 n, 表示二元实验的次数; 第二个参数为 p, 表示实验成功的概率; 函数返回一个 0-n 的整数 k, 且可从 rand 函数中实现"抽样", "抽样次数"为 n_samples, 以下为二项分布公式:

P ( X = k ) = ( n k ) p k ( 1 − p ) n − k ,  for  k = 0 , 1 , 2 , … , n . P(X = k) = {n \choose k}p^k(1-p)^{n-k}, \quad \text{ for } k = 0,1,2, \ldots, n. P(X=k)=(kn)pk(1p)nk, for k=0,1,2,,n.

示例

#Pkg.add("Distributions")
using Distributions
Binomial()
Binomial{Float64}(n=1, p=0.5)
n_samples = 10
rand(Binomial(), n_samples)
10-element Array{Int64,1}:
 0
 0
 1
 0
 1
 1
 1
 0
 1
 0
n = 4
p = 0.6
n_samples = 10
rd = rand(Binomial(n, p), n_samples)
ave = mean(rd)/n # 期望值
0.625
mean(rd)/n ≈ sum(rd)/n_samples/n
true

参考解答

function binomial2(n::Int=1, p::Float64=.5)::Int
    probs = rand(n)
    count = 0.0
    for prob in probs
        count += prob

1
function binomial3(n::Int=1, p::Float64=.5)::Int
    probs = rand(n)
    res = sum(prob

1
function binomial1(n::Int=1, p::Float64=.5)::Int
    count = 0
    U = rand(n)
    for i in eachindex(U)
        if U[i] < p
            count += 1 # or count = count + 1
        end
    end
    return count
end
binomial1()
0
function binomial4(n::Int=1, p::Float64=.5)::Int
    count = 0
    U = rand(n)
    for i in eachindex(U)
        if U[i] < p
            count += 1 # or count = count + 1
        end
    end
    return count
end
binomial4()
1

速度对比

n = 1000
p = 0.3
0.3
@benchmark binomial1(n, p)
BenchmarkTools.Trial: 
  memory estimate:  7.94 KiB
  allocs estimate:  1
  --------------
  minimum time:     1.933 μs (0.00% GC)
  median time:      2.772 μs (0.00% GC)
  mean time:        4.237 μs (13.71% GC)
  maximum time:     247.409 μs (96.89% GC)
  --------------
  samples:          10000
  evals/sample:     9
@benchmark binomial2(n, p)
BenchmarkTools.Trial: 
  memory estimate:  7.94 KiB
  allocs estimate:  1
  --------------
  minimum time:     2.300 μs (0.00% GC)
  median time:      3.622 μs (0.00% GC)
  mean time:        5.550 μs (20.27% GC)
  maximum time:     539.228 μs (97.61% GC)
  --------------
  samples:          10000
  evals/sample:     9
@benchmark binomial3(n, p)
BenchmarkTools.Trial: 
  memory estimate:  7.97 KiB
  allocs estimate:  2
  --------------
  minimum time:     1.370 μs (0.00% GC)
  median time:      1.920 μs (0.00% GC)
  mean time:        2.990 μs (22.08% GC)
  maximum time:     344.017 μs (96.31% GC)
  --------------
  samples:          10000
  evals/sample:     10
@benchmark binomial4(n, p)
BenchmarkTools.Trial: 
  memory estimate:  7.94 KiB
  allocs estimate:  1
  --------------
  minimum time:     1.956 μs (0.00% GC)
  median time:      2.856 μs (0.00% GC)
  mean time:        4.441 μs (14.11% GC)
  maximum time:     292.842 μs (97.66% GC)
  --------------
  samples:          10000
  evals/sample:     9
@benchmark rand(Binomial(n, p), 1)
BenchmarkTools.Trial: 
  memory estimate:  163 bytes
  allocs estimate:  4
  --------------
  minimum time:     231.040 ns (0.00% GC)
  median time:      335.251 ns (0.00% GC)
  mean time:        379.145 ns (9.18% GC)
  maximum time:     14.801 μs (96.91% GC)
  --------------
  samples:          10000
  evals/sample:     451

Problem3

问题3: 用蒙特卡罗方法近似计算 π \pi π . 只使用 rand() 函数生产随机数, 按照以下的提示:

  1. 如果 U U U 是单位平方 ( 0 , 1 ) 2 (0, 1)^2 (0,1)2 区域的二元均匀随机变量, 那么 U U U 落于 ( 0 , 1 ) 2 (0, 1)^2 (0,1)2 区域子集 B B B 的概率等于 B B B 的面积.
  2. 如果 U _ 1 , . . . , U _ n U\_1, ..., U\_n U_1,...,U_n U U U 一样是独立同分布的, 那么当 n n n 逐渐增大时, 落入 B B B 的部分收敛于落于 B B B 的概率.
  3. 圆形的面积等于 π × r a d i u s 2 \pi \times radius^2 π×radius2.

hint: 由于 π \pi π 可以依据圆形公式计算得到( π = c i r c l e   a r e a r a d i u s 2 \pi = \frac{circle\ area}{radius^2} π=radius2circle area ), 而圆形面积近似可以看成均匀分布于子集 B = { x , y : x 2 + y 2 < 1 } B = \{x, y: x^2+y^2<1\} B={x,y:x2+y2<1} 的概率. 进一步考虑圆形大小与单位平方区域, 即可得到 π \pi π.

参考解答

n = 10E3
n = convert(Int, 10e3)

10000
function area(n::Int=1000)::Float64
    x = rand(n)
    y = rand(n)
    count = 0
    for (i, j) in zip(x, y)
        if i^2+j^2<1
        count += 1
    end 
  end
  s =  4count/n
  return s
end

area()
3.152
function area2(n::Int=1000)::Float64
    count = 0
    for i in 1:n
        i, j = rand(2)
        if i^2+j^2<1
            count += 1
    end  
  end
  s =  4count/n
  return s
end
area2(n)
3.1348

速度对比

n = convert(Int, 10e6)
10000000
@benchmark area(n)
BenchmarkTools.Trial: 
  memory estimate:  152.59 MiB
  allocs estimate:  4
  --------------
  minimum time:     246.195 ms (62.99% GC)
  median time:      252.388 ms (63.23% GC)
  mean time:        254.003 ms (63.54% GC)
  maximum time:     289.401 ms (67.12% GC)
  --------------
  samples:          20
  evals/sample:     1
@benchmark area2(n)
BenchmarkTools.Trial: 
  memory estimate:  915.53 MiB
  allocs estimate:  10000000
  --------------
  minimum time:     716.644 ms (16.27% GC)
  median time:      729.055 ms (16.73% GC)
  mean time:        729.189 ms (16.56% GC)
  maximum time:     746.121 ms (16.73% GC)
  --------------
  samples:          7
  evals/sample:     1

Problem 4

仅使用 rand() 函数产生随机数, 编写程序, 实现以下随机事件:

  1. 不偏不倚地投掷 10 次硬币

  2. 如果连续 3 次头朝上事件在此序列中出现一次或多次, 则付给 1 美元, 否则不付钱

参考解答

"""
- `n::Int=10`: 扔 n 次硬币
- `p::Int=0.5`: 不偏不倚地扔

"""
function toss(n::Int=10, p::Float64=0.5)::Int
    count = 0
    payoff = 0
    for i in 1:n
        count = rand()>p ? count+1 : 0
        if count==3
            payoff += 1
            count = 0
        end
    end
    return payoff
end
toss()
1
"""
记录投掷硬币的过程
"""
function tossProcess(n::Int=10, p::Float64=0.5)::Tuple{Int64, Array{Int64,1}}
    count = 0
    payoff = 0
    seq = Vector{Int}(undef, n)
    for i in eachindex(seq)
        count, seq[i] = rand()>p ? (count+1, 1) : (0, 0)
        if count==3
            payoff += 1
            count = 0
        end
    end
    return payoff, seq
end
toss3 (generic function with 3 methods)
tossProcess()
(1, [0, 0, 1, 1, 1, 0, 0, 1, 0, 0])
"""
重复试验函数
"""
function expriments(N::Int=100, n::Int=10)
    trials = zeros(Int, N)
    for i in eachindex(trials)
        trials[i] = toss(n)
    end
    return trials
end

N, n = 100000000, 10
mean(expriments(N, n))
expriments (generic function with 3 methods)
N, n = 100000000, 10 # 进行 1亿次试验, 每次试验扔 10次硬币
@benchmark expriments(N, n)
BenchmarkTools.Trial: 
  memory estimate:  30.55 GiB
  allocs estimate:  200000002
  --------------
  minimum time:     21.970 s (6.41% GC)
  median time:      21.970 s (6.41% GC)
  mean time:        21.970 s (6.41% GC)
  maximum time:     21.970 s (6.41% GC)
  --------------
  samples:          1
  evals/sample:     1

速度对比

@benchmark toss()
BenchmarkTools.Trial: 
  memory estimate:  320 bytes
  allocs estimate:  2
  --------------
  minimum time:     127.126 ns (0.00% GC)
  median time:      180.920 ns (0.00% GC)
  mean time:        218.137 ns (10.08% GC)
  maximum time:     82.621 μs (99.61% GC)
  --------------
  samples:          10000
  evals/sample:     870
@benchmark tossProcess()
BenchmarkTools.Trial: 
  memory estimate:  192 bytes
  allocs estimate:  2
  --------------
  minimum time:     110.834 ns (0.00% GC)
  median time:      152.979 ns (0.00% GC)
  mean time:        183.500 ns (10.42% GC)
  maximum time:     76.656 μs (99.72% GC)
  --------------
  samples:          10000
  evals/sample:     923

你可能感兴趣的:(Julia)