導入模塊
using Pkg
using BenchmarkTools
問題
Problem 1
不使用 Julia 自帶的 factorial(n)
函數, 用 for
編寫一個計算 (n 階乘, ) 的函數, 函數名稱爲 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 = 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. 用數學形式描述爲, .
請使用 base 庫的 rand
函數編寫產生二項分佈隨機變量.
Hint: 如果 服從 (0, 1) 的均勻分佈, 且 , 那麼 意味着概率 p 條件下爲真 (成功) 的情況
描述: 可以參考 Distributions 庫的 Binomial
函數. Binomial
第一個參數爲 n, 表示二元實驗的次數; 第二個參數爲 p, 表示實驗成功的概率; 函數返回一個 0-n 的整數 k, 且可從 rand
函數中實現"抽樣", "抽樣次數"爲 n_samples, 以下爲二項分佈公式:
示例
#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<p ? 1.0 : 0.0
end
return count
end
binomial2()
1
function binomial3(n::Int=1, p::Float64=.5)::Int
probs = rand(n)
res = sum(prob<p ? 1 : 0 for prob in probs)
return res
end
binomial3()
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: 用蒙特卡羅方法近似計算 . 只使用 rand()
函數生產隨機數, 按照以下的提示:
- 如果 是單位平方 區域的二元均勻隨機變量, 那麼 落於 區域子集 的概率等於 的面積.
- 如果 和 一樣是獨立同分布的, 那麼當 逐漸增大時, 落入 的部分收斂於落於 的概率.
- 圓形的面積等於 .
hint: 由於 可以依據圓形公式計算得到( ), 而圓形面積近似可以看成均勻分佈於子集 的概率. 進一步考慮圓形大小與單位平方區域, 即可得到 .
參考解答
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()
函數產生隨機數, 編寫程序, 實現以下隨機事件:
-
不偏不倚地投擲 10 次硬幣
-
如果連續 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