iOS Swift 计算组合数 二项分布 二项分布求和

前言:

这次是写作业,质量管理课程里面的 OC 曲线, 我自己写了 分解质因数,计算组合数,计算二项分布,二项分布求和 这些函数。
然后描点画图。

效果图:

OC曲线.png
第一问.png
第二问.png

代码地址:

代码地址:https://github.com/gityuency/Autolayout
示例代码类名 【OCCurveViewController】【SecondQuestion】

关键代码

计算部分的代码: SecondQuestion.swift

import Foundation

/// 这个就是我用来做二项式计算的代码
class SecondQuestion {
    
    /// 二项分布求和
    /// - Parameters:
    ///   - m: 组合的上
    ///   - n: 组合的下
    ///   - p: 概率
    /// - Returns: 求和结果
    static func sumBinomialDistribution(m: Int, n:Int, p:Int) -> Double {
        var result: Double = 0
        for i in 0..<m {
            result = result + calculateBinomialDistribution(m: i, n: n, p: p)
        }
        result = result + calculateBinomialDistribution(m: m, n: n, p: p)
        //print("单次求和结果: \(result)")
        return result
    }
    
    
    /// 计算单个二项分布
    /// - Parameters:
    ///   - m: 上方
    ///   - n: 下方
    ///   - p: 发生的概率, 这个概率如果使用小数, 那么就会有精度丢失的问题, 那么, 在传入的时候,把这个精度使用 Int, 在计算的时候, 再转换成Double 取值范围 0-100
    ///   - d: 发生了多少次
    /// - Returns: 本次二项分布的概率
    static func calculateBinomialDistribution(m: Int, n:Int, p: Int) -> Double {
        let a = Double(xindejiechengfanan(m: m, n: n))
        let b = pow(Double(p) * 0.0001, Double(m))
        let c = pow((Double(10000 - p)) * 0.0001, Double(n - m))
        let result = a * b * c;
        return result
    }
    
    //计算组合数
    static func xindejiechengfanan(m: Int, n: Int) -> Int {
        
        var fenMuArray = [Int]()
        var fenZiArray = [Int]()
        
        //去掉最大的数
        let l = m
        let r = n - m
        var count: Int = 0
        if l > r {
            count = (n - l)
        } else {
            count = (n - r)
        }
        var k = n
        for _ in 0..<count {
            fenZiArray.append(k)
            k = k - 1
        }
        //print("fenZiArray  \(fenZiArray)")
        
        var B: Int = 0
        let zuixiaofenmuxunhuanshu = l > r ? r : l
        for _ in 0..<zuixiaofenmuxunhuanshu {
            B = B + 1
            fenMuArray.append(B)
        }
        if fenMuArray.count > 0 {
            fenMuArray.remove(at: fenMuArray.firstIndex(of: 1)!)
        }
        //print("fenMuArray  \(fenMuArray)")
        
        //把分子里面的每一个数,分解质因数,然后放到数组里面
        var fenziFenjiezhiyinshu = [Int]()
        for i in fenZiArray {
            fenziFenjiezhiyinshu += needZhiYinShuZu(n: i)
        }
        //print("fenziFenjiezhiyinshu \(fenziFenjiezhiyinshu)")
        
        var fenmufenjieshuzu = [Int]()
        for i in fenMuArray {
            fenmufenjieshuzu += needZhiYinShuZu(n: i)
        }
        //print("fenmufenjieshuzu \(fenmufenjieshuzu)")
        
        //遍历分子数组,然后把分母中相同元素移除掉
        for i in fenmufenjieshuzu {
            for k in fenziFenjiezhiyinshu.reversed() {
                if k == i {
                    fenziFenjiezhiyinshu.remove(at: fenziFenjiezhiyinshu.firstIndex(of: k)!)
                    break
                }
            }
        }
        
        //print("简化之后 fenziFenjiezhiyinshu \(fenziFenjiezhiyinshu)")
        let sum = fenziFenjiezhiyinshu.reduce(1,{$0 * $1})
        //print(sum)
        return sum;
    }
    
    
    /// 得到一个数分解质因数后的数组
    /// - Parameter n: 需要分解的数
    /// - Returns: 数组
    static func needZhiYinShuZu(n: Int) -> [Int] {
        var result = [Int]()
        @discardableResult
        func fenjizhiyinshu(n: Int) -> Int {
            
            for i in 2..<n {
                if (n % i == 0) {
                    let temp = n / i
                    result.append(i)
                    return fenjizhiyinshu(n: temp)
                }
            }
            result.append(n)
            return n;
        }
        fenjizhiyinshu(n: n)
        //print("分解得到质因数数组: \(result)")
        return result
    }
}



在ViewController里面画图:OCCurveViewController.swift

import UIKit


//!!!!!!!!   请调整为横屏!!!!!!!


/*
 
 第一问:
 α = 0.036564459244395664
 β = 0.07374950015762405

 第二问:
 P0 = 0.0043
 p1 = 0.0277
 
 第三问:
 在样本具有代表性前提下, 结合 OC 曲线图, 可以看到,这两种抽样方案, 对与生产方来说, 弃真风险较小, 对于接收方来说,取伪风险也较小.
 在第一种情况下, α = 0.036564, β = 0.07375 双方风险相差较小, 对于买卖双方来说, 相对公平.
 第二种情况下,  α = 0.05, β = 0.1 这是一种推荐 α,β 取值对, 结合 OC曲线图, 同样可以认为对买卖双方来说,具有操作空间,具有公平性.
        
 关于画图和求和计算: 使用电脑进行暴力计算, 合格品率我从 0.00% 取到了 100.00%, 一共是一万零一个数, 都把对应的接收概率P计算出来,
                 然后每个数值都在屏幕上描点, 因为点足够多, 就画出了 OC 曲线.
                 但是一万个点太多, 不适宜进行 作业中的 第二问, 第三问回答, 所以, 我取了曲线中变化最快的那一段进行放大,
                 于是就有了下方 从 0 - 0.05 描 501 个点的 OC 曲线, 这样图看起来就比较直观, 也易于求解.
 
 
 */
class OCCurveViewController: UIViewController {

    @IBOutlet weak var backShortK: UIView!
    
    ///宽1200 高 400
    @IBOutlet weak var backVVVV: UIView!
    /// 放曲线的背景
    lazy var backView: UIView = {
        let v = UIView()
        v.backgroundColor = UIColor.orange
        v.frame = CGRect(x: 100, y: 100, width: 400, height: 400)
        return v
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.navigationController?.navigationBar.isHidden = true
        
        
        for i in 0...10000 {
            let a = SecondQuestion.sumBinomialDistribution(m: 2, n: 190, p: i)
            let ki = (Double(i) * 0.01).roundTo(places: 2)
            print("不合格品率: \(ki)%    P: \(a)   α: \(1 - a)")
        }
        /*
         第一问:
         不合格品率: 0.38%    P: 0.9634355407556043   α: 0.036564459244395664
         α = 0.036564459244395664
         
         不合格品率: 3.0%    P: 0.07374950015762405   α: 0.926250499842376
         β = 0.07374950015762405
         
         */
                
        /*
         第二问:
         
         α 为 0.05, 那么 接受概率是 0.95
         在
         不合格品率: 0.43%    P: 0.9504782551776189   α: 0.04952174482238114
         P0 = 0.0043
          
         β 为 0.1
         不合格品率: 2.77%    P: 0.10092052472525978   α: 0.8990794752747402
         p1 = 0.0277
         
         */
        
    }
        
    ///这个函数是用来画曲线的
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        
        /// 放曲线的背景
        let bLayer = CALayer()
        //bLayer.backgroundColor = UIColor.blue.cgColor
        bLayer.frame = CGRect(x: 0, y: 0, width: backVVVV.frame.width, height: backVVVV.frame.height)
        backVVVV.layer.addSublayer(bLayer)
        
        var allPointArray = [Double]()
        
        for i in 0...10000 {
            let a = SecondQuestion.sumBinomialDistribution(m: 2, n: 190, p: i)
            let k = (1 - a) * 400.0
            allPointArray.append(k)
        }
        
        let pointWH: CGFloat = 1
        let rate: Double = 3 / 25.0
        
        // 添加分数点
        for (i, score) in allPointArray.enumerated() {
            let pLayer = CALayer()
            pLayer.frame = CGRect(x: 0, y: 0, width: pointWH, height: pointWH)
            //pLayer.cornerRadius = pointWH / 2
            pLayer.position = CGPoint(x: Double(i) * rate, y: (score))
            pLayer.backgroundColor = UIColor.red.cgColor
            bLayer.addSublayer(pLayer)
        }
        
        //开始二阶段的是算数
        startNew()
    }
    
    func startNew() {
        
        /// 放曲线的背景
        let bbbb = CALayer()
        //bLayer.backgroundColor = UIColor.blue.cgColor
        bbbb.frame = CGRect(x: 0, y: 0, width: backShortK.frame.width, height: backShortK.frame.height)
        backShortK.layer.addSublayer(bbbb)
        
        var allPointArray = [Double]()
        
        //不合格品率: 5.0%    P: 0.003555799076021292   α: 0.9964442009239787
        for i in 0...500 {
            let a = SecondQuestion.sumBinomialDistribution(m: 2, n: 190, p: i)
            let k = (1 - a) * 400.0
            allPointArray.append(k)
        }
        
        let pointWH: CGFloat = 1
        let rate: Double = 1200 / Double(allPointArray.count - 1)
        
        // 添加分数点
        for (i, score) in allPointArray.enumerated() {
            
            let pLayer = CALayer()
            
            if i == 43 || i == 277 {
            //if i == 38 || i == 300 {
                
                let heng = CALayer()
                heng.backgroundColor = UIColor.black.cgColor;
                heng.frame = CGRect(x: Double(i) * rate, y: score, width: 1, height: Double(bbbb.frame.height) - score)
                bbbb.addSublayer(heng)
                
                let shu = CALayer()
                shu.backgroundColor = UIColor.black.cgColor;
                shu.frame = CGRect(x: 0, y: score, width: Double(i) * rate, height: 1)
                bbbb.addSublayer(shu)
                
                let label = UILabel()
                let ki = (Double(i) * 0.01).roundTo(places: 2)
                let shuzhi = SecondQuestion.sumBinomialDistribution(m: 2, n: 190, p: i)
                label.text = "(\(ki)%, \(shuzhi.roundTo(places: 6)))"
                label.sizeToFit()
                
                label.frame = CGRect(x: CGFloat((Double(i) * rate) + 5), y: CGFloat(score - 10), width: label.frame.width, height: label.frame.height)
                backShortK.addSubview(label)
            }
            
            pLayer.backgroundColor = UIColor.red.cgColor
            pLayer.frame = CGRect(x: 0, y: 0, width: pointWH, height: pointWH)
            pLayer.position = CGPoint(x: Double(i) * rate, y: (score))
            bbbb.addSublayer(pLayer)
        }
    }
}

///保留两位小数 Rounds the double to decimal places value
extension Double {
    func roundTo(places:Int) -> Double {
        let divisor = pow(10.0, Double(places))
        return (self * divisor).rounded() / divisor
    }
}


结语

花了两天。

推荐阅读更多精彩内容