しめ鯖日記

swift, iPhoneアプリ開発, ruby on rails等のTipsや入門記事書いてます

モンテカルロ法で円周率を求める

囲碁AIで有名なモンテカルロ法で円周率を計算できるようなので試してみました。

モンテカルロ法とは、Wikipediaによるとシミュレーションや数値計算を乱数を用いて行う手法の総称とのことです。

モンテカルロ法 - Wikipedia

実装方法

計算の手順は下の通りです。

  1. 四角形とそれに内接する円を作成する
  2. 四角形の中に多数の点を配置する
  3. 円の内部にある点の数を円の面積だと仮定して円周率を計算する

まずは四角形と内接する円を作成します。

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let v = UIView(frame: CGRect(x: 100, y: 100, width: 200, height: 200))
        v.backgroundColor = UIColor.darkGray
        view.addSubview(v)
        let circle = UIView(frame: v.bounds)
        circle.backgroundColor = UIColor.lightGray
        circle.layer.cornerRadius = circle.frame.width / 2
        v.addSubview(circle)
    }
}

これを実行すると、四角形と円が描画されます。

f:id:llcc:20170508235355p:plain

次は四角形の上に点を配置していきます。

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let v = UIView(frame: CGRect(x: 100, y: 100, width: 200, height: 200))
        v.backgroundColor = UIColor.darkGray
        view.addSubview(v)
        let circle = UIView(frame: v.bounds)
        circle.backgroundColor = UIColor.lightGray
        circle.layer.cornerRadius = circle.frame.width / 2
        v.addSubview(circle)
        
        // ここから今回追加
        var dots: [UIView] = []
        (0...40000).forEach { _ in
            let x = CGFloat(arc4random_uniform(201))
            let y = CGFloat(arc4random_uniform(201))
            let dot = UIView(frame: CGRect(x: x, y: y, width: 1, height: 1))
            dot.backgroundColor = UIColor.white
            v.addSubview(dot)
            dots.append(dot)
        }
    }
}

実行すると、点が配置されているのが分かります。

f:id:llcc:20170509000427p:plain

次に円の中の点の数を計算します。

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let v = UIView(frame: CGRect(x: 100, y: 100, width: 200, height: 200))
        v.backgroundColor = UIColor.darkGray
        view.addSubview(v)
        let circle = UIView(frame: v.bounds)
        circle.backgroundColor = UIColor.lightGray
        circle.layer.cornerRadius = circle.frame.width / 2
        v.addSubview(circle)
        
        var dots: [UIView] = []
        (0...40000).forEach { _ in
            let x = CGFloat(arc4random_uniform(201))
            let y = CGFloat(arc4random_uniform(201))
            let dot = UIView(frame: CGRect(x: x, y: y, width: 1, height: 1))
            dot.backgroundColor = UIColor.white
            v.addSubview(dot)
            dots.append(dot)
        }
        
        // ここから今回追加
        let count = dots.filter {
            let dx = $0.frame.origin.x - circle.center.x
            let dy = $0.frame.origin.y - circle.center.y
            let radius = circle.frame.size.width / 2
            return dx*dx + dy*dy < radius*radius
        }.count
        print(count) // → 74
    }
}

最後に円の中の点(円の面積)を元に円周率を求めます。
結果は、3.1111と3.14に近い数字となりました。

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let v = UIView(frame: CGRect(x: 100, y: 100, width: 200, height: 200))
        v.backgroundColor = UIColor.darkGray
        view.addSubview(v)
        let circle = UIView(frame: v.bounds)
        circle.backgroundColor = UIColor.lightGray
        circle.layer.cornerRadius = circle.frame.width / 2
        v.addSubview(circle)
        
        var dots: [UIView] = []
        (0...40000).forEach { _ in
            let x = CGFloat(arc4random_uniform(201))
            let y = CGFloat(arc4random_uniform(201))
            let dot = UIView(frame: CGRect(x: x, y: y, width: 1, height: 1))
            dot.backgroundColor = UIColor.white
            v.addSubview(dot)
            dots.append(dot)
        }
        
        let count = dots.filter {
            let dx = $0.frame.origin.x - circle.center.x
            let dy = $0.frame.origin.y - circle.center.y
            let radius = circle.frame.size.width / 2
            return dx*dx + dy*dy < radius*radius
        }.count
        
        let radius = circle.frame.size.width / 2
        print(CGFloat(count) / radius / radius) // 3.1111
    }
}