Swift 进阶 | 看得见的算法

GitHub Repo:coderZsq.target.swift
Follow: coderZsq · GitHub
Resume: coderzsq.github.io/coderZsq.we…前端

平常扯淡

前段日子写了篇面经, 获得了掘金征文活动三等奖仍是很是开心, 可是被寒神说面试不过就泄题, 影响很差, 我想一想也是, 立刻就把大厂的名字给抹掉了, 但被转载的就无能为力了, 看到下面好多喷的, 真是背后一凉, 一首凉凉送给本身, 形成的伤害没法挽回, 再此郑重道歉, 不再写面经了. 但愿之后还能得到大厂面试的机会!python

大厂刷掉后, 我心情难以平复, 由于其实我是作了充足的准备来的, 可是仍是实力上有差距, 诶... 仍是想一想改如何提高本身的水平吧!webpack

其实对于本身, 我其实并不知道iOS该如何进行学习, 也不知道技术这条路我半道转的是否是正确, 更不知道在当今环境下我这种水平的iOS开发者是否还有存在的必要.c++

提及转行作iOS到如今, 从Objective-C基础语法学起, 认为OC是最好的语言, 学会作几个简单的UITableView页面, 能播放音频视频, 以为本身真是转了个高大上的职业, 如今想一想, 真是肤浅的让人忍不住发笑.git

我把作iOS这段学习经历分为五个阶段:程序员

第一个阶段: 是刚刚转行进入互联网企业, 那时候以为学好CALayer掌握了一些酷炫的动画(贝塞尔曲线), 感受就已经比大多数人都强了, 那时候还把经常使用的工具类封装起来, 就以为, 嗯, 本身还不错.github

第二个阶段: 就是瞎学些有的没的, 如Swift, JavaScript, Java什么的, 以为本身全栈了, 什么都会了, 很牛逼, 你看还本身可以写一个前端简历, 还作过公司的前端项目, 服务器开发也会了, 自信心爆棚啊, 以为本身真是无所不能.web

第三个阶段: 写了一个架构生成器, 以为本身所向披靡了, 公司项目都在用我写的架构, 用着我制定的规则, 尼玛不就是一个简单的字符串替换, 嘚瑟个啥... 并且对于架构的理解肤浅至极...面试

第四个阶段: 察觉到了本身的薄弱, 开始学习iOS的底层原理, 学习了C\C++的语法, 学习了Linux基础, 学习了8086, ARM64汇编, 了解了一些自认为仍是比较深的知识点. 以为本身前途仍是有但愿的.算法

第五个阶段: 也就是被刷掉以后的如今, 其实我如今也很迷茫, 也不知道如今学的东西到底有没有用, 也不知道大厂到底要什么样的人才(只知道牛逼就行...), 但我也经过把知识点进行分类进行进阶吧. 把最近的学习总结分享出来和你们一块儿讨论, 像我这种水平的玩家到底该怎么玩耍.

学习计划

我把最近学习的方面所有都整理在StudyNote这个里面了, 能够看得出, 这个阶段, 我明显的就是像要学习增强算法与数据结构方面的, 多是由于被大厂刷的有阴影了吧.

除了数据结构与算法, 最近刚看完的CS193p的教程, 白胡子老头的这个视频的质量很高, 发现了不少我平时忽略的东西, 并且以为不少我觉得的写法从本质上都是有问题的.

还有就是objc.io的这几本书了, 质量挺高的, 函数式Swift的学习对我颇有帮助. 感受就像是打开了新世界.

还有就是排在后面的学习计划: python, 数据分析, 机器学习, 深度学习.能够看得出, 其实这些都学完, 其实也不知道可以干什么, 无所事事诶... 但至少这些都是我可以找到的比较高质量的资料了, 若是有其余高质量的资料, 欢迎进行资料共享hhhh~

算法学习

果真, 算法是挡在(复制黏贴)程序员和(正常)程序员以前的一条很难跨越的鸿沟, 我这里不是说, 你会写快速排序,二分查找, 深度广度优先这类你们都可以背出来的东西就叫作掌握算法了, 我在Coursera上看了加州大学的算法课, 才知道该如何设计算法, 但因为是英语的, 并且没有代码, 纯数学的看不太懂, 因此转向了北京大学的算法课, 如下就是我最新学到的算法和你分享.

这个只是算法基础的第二节课, 你能想象这是算法基础么? 光是这个题目, 我就看了老半天, 大意是点击一个灯, 上下左右的灯会自动点亮(熄灭), 当随机给出点亮熄灭数的时候, 须要算出点击哪几个灯能够将全部的灯点亮或熄灭.

这种算法题, 真是闻所未闻见所未见吧, 并且这是算法基础的开头的课... 我在想难道那些大厂的人作这种题跟玩的同样么, 想起了面试官的微笑, 那可真是有力量的微笑呢.

#include <stdio.h>

int puzzle[6][8], press[6][8];
/* 推测验证过程: 根据第一行猜想 */
bool guess() {
    int c, r;
    //根据press第1行和puzzle数组,计算press其余行的值
    for(r=1; r<5; r++) {
        for(c=1; c<7; c++) {
            press[r+1][c]=(puzzle[r][c]+press[r][c]+press[r-1][c]+press[r][c-1]+press[r][c+1])%2;
        }
    }
    //判断所计算的press数组可否熄灭第5行的全部灯
    for(c=1; c<7; c++) {
        if ((press[5][c-1]+press[5][c]+press[5][c+1]+press[4][c])%2 != puzzle[5][c]) {
            return false;
        }
    }
    return true;
}

/* 枚举过程: 对press第1行的元素press[1][1]~press[1][6]的各类取值进行枚举 */
void enumerate() {
    int c;
    bool success; //这个变量时当时定义了没排上用场吧,NodYoung注
    for(c=1; c<7; c++) {
        press[1][c]=0;
    }
    while(guess()==false) {
        press[1][1]++;
        c=1;
        while(press[1][c]>1) {  //累加进位
            press[1][c]=0;
            c++;
            press[1][c]++;
        }
    }
    return ;
}

int main() {
    int cases, i, r, c;
    scanf("%d", &cases);
    for(r=0; r<6; r++) {
        press[r][0]=press[r][7]=0;
    }
    for(c=0; c<7; c++) {
        press[0][c]=0;
    }
    for(i=0; i<cases; i++) {
        for(r=1; r<6; r++) {
            for(c=1; c<7; c++) {
                scanf("%d", &puzzle[r][c]); //读入输入数据
            }
        }
        enumerate();
        printf("PUZZLE#%d\n", i+1);
        for (r=1; r<6; r++) {
            for (c=1; c<7; c++) {
                printf("%d ", press[r][c]);
            }
            printf("\n");
        }
    }
    return 0;
}
复制代码

这是北大老师视频里给出的算法的答案, 讲的很好, 可是说真的听的是只知其一;不知其二, 缘由在于不知道为何, 这些网课都不是在线编译的, 而是直接对着代码分析, 上面的代码摘抄自-> 能够搜索熄灯问题.

var puzzle = [[Int]](repeating: [Int](repeating: 0, count: 8), count: 6)
var press = [[Int]](repeating: [Int](repeating: 0, count: 8), count: 6)
复制代码
func guess() -> Bool {
    for r in 1..<5 {
        for c in 1..<7 {
            press[r + 1][c] = (puzzle[r][c] + press[r][c] + press[r - 1][c] + press[r][c - 1] + press[r][c + 1]) % 2
        }
    }
    for c in 1..<7 {
        if (press[5][c - 1] + press[5][c] + press[5][c + 1] + press[4][c]) % 2 != puzzle[5][c] {
            return false
        }
    }
    return true
}
复制代码
func enumerate() {
    var c = 1
    for _ in 1..<7 {
        press[1][c] = 0
        while (guess() == false) {
            press[1][1] += 1
            c = 1
            while press[1][c] > 1 {
                press[1][c] = 0
                c += 1
                press[1][c] += 1
            }
        }
        c += 1
    }
}
复制代码
class Enumerate {
    
    static func main() {
        let cases = 1
        for r in 0..<6 {
            press[r][0] = 0
            press[r][7] = 0
        }
        for c in 1..<7 {
            press[0][c] = 0
        }
        for i in 0..<cases {
            for r in 1..<6 {
                for c in 1..<7 {
                    puzzle[r][c] = 2.arc4random
                }
            }
            enumerate()
            print("PUZZLE #\(i + 1)")
            for r in 1..<6 {
                for c in 1..<7 {
                    print(puzzle[r][c], terminator: "")
                }
                print()
            }
            print("== press ==")
            for r in 1..<6 {
                for c in 1..<7 {
                    print(press[r][c], terminator: "")
                }
                print()
            }
            print()
        }
    }
}
复制代码

以上是我学习的时候转换成swift表达的, 不为何, 只是用来熟悉Swift语法罢了, 毕竟OC也不知道还能活个几年了.

PUZZLE #1
011010
001110
010011
000101
100000
== press ==
001001
000101
001010
001101
011110
复制代码

可是光看代码, 很难看懂这个结果究竟是正确仍是不正确的... 由于跑出来是这样的一个东西, 可是有些地方仍是能够讲一下, 好比是外面包了一圈0来避免冗余逻辑判断, 用2进制进位的方法进行运算, 仍是有学到一些皮毛的.

看得见的算法

固然, 这种文字上的描述, 很难有深入的印象的, 因此, 我就在想是否能够把这个熄灯游戏给作出来, 再本身测试一下呢? 想到就干吧!!

经过CS193p的学习, 对于画UI方面有了全新的认识, 该如何添加约束, MVC到底怎么写, 以致于我之前理解的感受彻底就是错的, 正好趁这个机会来练练手.

咱们经过StoryBoard先把View画好, 不得不说UIStackView真是好用到爆!!

import Foundation

struct Matrix {
    var rows: Int
    var columns: Int
}

struct LightSwitch {
    
    private var puzzle: [[Int]]
    private var matrix: Matrix
    var lights = [Int]()
    
    mutating func lightUp(index: Array<Any>.Index?) {
        guard let index = index else { return }
        var m = Matrix(rows: 0, columns: 0)
        if index <= matrix.rows {
            m.columns = index + 1
        } else {
            m.columns += index % matrix.columns + 1
        }
        for i in 0...index {
            if i % matrix.columns == 0 {
                m.rows += 1
            }
        }
        puzzle[m.rows][m.columns] = puzzle[m.rows][m.columns] == 0 ? 1 : 0
        puzzle[m.rows + 1][m.columns] = puzzle[m.rows + 1][m.columns] == 0 ? 1 : 0
        puzzle[m.rows][m.columns + 1] = puzzle[m.rows][m.columns + 1] == 0 ? 1 : 0
        puzzle[m.rows - 1][m.columns] = puzzle[m.rows - 1][m.columns] == 0 ? 1 : 0
        puzzle[m.rows][m.columns - 1] = puzzle[m.rows][m.columns - 1] == 0 ? 1 : 0
        lights.removeAll()
        for r in 1..<matrix.rows + 1 {
            for c in 1..<matrix.columns + 1 {
                lights.append(puzzle[r][c])
            }
        }
    }
    
    init(matrix: Matrix) {
        self.matrix = matrix
        puzzle = [[Int]](repeating: [Int](repeating: 0, count: matrix.columns + 2), count: matrix.rows + 2)
        for r in 1..<matrix.rows + 1 {
            for c in 1..<matrix.columns + 1 {
                puzzle[r][c] = 2.arc4random
                lights.append(puzzle[r][c])
            }
        }
        print("========")
        for r in 0..<matrix.rows + 2 {
            for c in 0..<matrix.columns + 2 {
                print(puzzle[r][c], terminator: "")
            }
            print()
        }
        print("========")
    }
}
复制代码

Model代码, 原来MVCM须要这样写的, 之前都只是认为是简单的数据结构来的真是肤浅, 这种直接业务逻辑写在M里面的的写法真是好用到爆啊!

import UIKit

extension UIColor {
    var toImage: UIImage {
        let bounds = CGRect(origin: .zero, size: CGSize(width: 1.0, height: 1.0))
        let renderer = UIGraphicsImageRenderer(bounds: bounds)
        return renderer.image { context in
            self.setFill()
            context.fill(CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0))
        }
    }
}

extension Int {
    var arc4random: Int {
        if self > 0 {
            return Int(arc4random_uniform(UInt32(self)))
        } else if self < 0 {
            return -Int(arc4random_uniform(UInt32(self)))
        } else {
            return 0
        }
    }
}

class ViewController: UIViewController {
    
    @IBOutlet var lights: [UIButton]! {
        didSet {
            for (index, light) in lights.enumerated() {
                light.setBackgroundImage(UIColor.yellow.toImage, for: .normal)
                light.setBackgroundImage(UIColor.darkGray.toImage, for: .selected)
                light.isSelected = switchs.lights[index] == 1 ? true : false
            }
        }
    }
    
    @IBAction func lightUp(_ sender: UIButton) {
        switchs.lightUp(index: lights.index(of: sender))
        for (index, light) in lights.enumerated() {
            light.isSelected = switchs.lights[index] == 1 ? true : false
        }
        if Set(switchs.lights).count == 1 {
            let alert = UIAlertController(title: "Congratulation", message: "You made all light up successfully", preferredStyle: .alert)
            alert.addAction(UIAlertAction(
                title: "again",
                style: .default,
                handler: { [weak self] _ in
                    self?.restart()
                }
            ))
            present(alert, animated: true)
        }
    }
    
    @IBAction func restart(_ sender: UIButton? = nil) {
        switchs = LightSwitch(matrix: Matrix(rows: 5, columns: 6))
        for (index, light) in (self.lights.enumerated()) {
            light.isSelected = self.switchs.lights[index] == 1 ? true : false
        }
    }
    
    var switchs: LightSwitch = LightSwitch(matrix: Matrix(rows: 5, columns: 6))
}
复制代码

Controller的代码, 这才可以真正理解什么叫作控制器用来协调ViewModel的交互, ModelView毫无关联, 这才是iOS的正确写法啊.

运行了一下, 果真白胡子大叔没有骗我, 跑的6到飞起~

能够看到的是, 终端打印的矩阵和界面上显示的是一一对应的, 从北大老师学到的外面包一圈的方法也是特别好用的.

算法测试

init(matrix: Matrix) {
        self.matrix = matrix
        puzzle = [[Int]](repeating: [Int](repeating: 0, count: matrix.columns + 2), count: matrix.rows + 2)
        for r in 1..<matrix.rows + 1 {
            for c in 1..<matrix.columns + 1 {
// puzzle[r][c] = 2.arc4random
                puzzle[1][1] = 1
                lights.append(puzzle[r][c])
            }
        }
        print("========")
        for r in 0..<matrix.rows + 2 {
            for c in 0..<matrix.columns + 2 {
                print(puzzle[r][c], terminator: "")
            }
            print()
        }
        print("========")
    }
复制代码

咱们将初始状态从随机数改为只暗一个灯, 位置是[1][1]

class Enumerate {
    
    static func main() {
        let cases = 1
        for r in 0..<6 {
            press[r][0] = 0
            press[r][7] = 0
        }
        for c in 1..<7 {
            press[0][c] = 0
        }
        for i in 0..<cases {
            for r in 1..<6 {
                for c in 1..<7 {
// puzzle[r][c] = 2.arc4random
                    puzzle[1][1] = 1
                }
            }
            enumerate()
            print("PUZZLE #\(i + 1)")
            for r in 1..<6 {
                for c in 1..<7 {
                    print(puzzle[r][c], terminator: "")
                }
                print()
            }
            print("== press ==")
            for r in 1..<6 {
                for c in 1..<7 {
                    print(press[r][c], terminator: "")
                }
                print()
            }
            print()
        }
    }
}

复制代码

咱们把北大算法也改为对应的[1][1]

PUZZLE #1
100000
000000
000000
000000
000000
== press ==
000111
101010
101100
001000
110000
复制代码

能够看懂只要按下所有位置对应为1的按钮就能够将全部灯都打开了, 咱们来试一下.

更新算法

为了避免用每次都跑到另外一个程序去运行算法, 我新增了Hint提示功能, 每盘游戏均可以点击提示, 看到提示显示的深色按钮点击对应位置的灯, 便可点亮全部的灯.

添加这个功能其实也很简单, 只须要新建一个Popver控制器便可, 使用对应算法, 映射到向量便可.

import UIKit

class HintViewController: UIViewController {

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        if let fittedSize = topLevelView?.sizeThatFits(UILayoutFittingCompressedSize) {
            preferredContentSize = CGSize(width: fittedSize.width + 30, height: fittedSize.height + 30)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        if presentationController is UIPopoverPresentationController {
            view.backgroundColor = .clear
        }        
    }
    @IBOutlet var hints: [UIButton]! {
        didSet {
            for (index, hint) in hints.enumerated() {
                hint.setBackgroundImage(UIColor.yellow.toImage, for: .normal)
                hint.setBackgroundImage(UIColor.darkGray.toImage, for: .selected)
                hint.isSelected = switchs?.hints[index] == 1 ? true : false
            }
        }
    }
    @IBOutlet weak var topLevelView: UIStackView!
    var switchs: LightSwitch?
}

复制代码

新增控制器, 就是显示提示的控制器

@IBOutlet weak var hintButton: UIButton!
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "Show Hint", let destination = segue.destination.contents as? HintViewController,
            let ppc = destination.popoverPresentationController {
            ppc.delegate = self
            ppc.sourceRect = hintButton.bounds
            destination.switchs = switchs
        }
    }
    
    func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
        return .none
    }

复制代码

原先控制器的Segue设置, 让iphone默认不适配

mutating func guess() -> Bool {
        for r in 1..<matrix.rows {
            for c in 1..<matrix.columns + 1 {
                press[r + 1][c] = (puzzle[r][c] + press[r][c] + press[r - 1][c] + press[r][c - 1] + press[r][c + 1]) % 2
            }
        }
        for c in 1..<matrix.columns + 1 {
            if (press[matrix.rows][c - 1] + press[matrix.rows][c] + press[matrix.rows][c + 1] + press[matrix.rows - 1][c]) % 2 != puzzle[matrix.rows][c] {
                return false
            }
        }
        return true
    }
    
    mutating func enumerate() {
        var c = 1
        for _ in 1..<matrix.columns + 1 {
            press[1][c] = 0
            while (guess() == false) {
                press[1][1] += 1
                c = 1
                while press[1][c] > 1 {
                    press[1][c] = 0
                    c += 1
                    press[1][c] += 1
                }
            }
            c += 1
        }
    }
复制代码

Model中加入核心算法便可

这个Demo多是全网惟一的熄灯问题UI版本吧, 给本身一个赞~

通过好几回测试, 能够看见, 算法是正确的, 我也学到了这个算法背后的思惟, 更经过了写了一个Demo来证实了算法的正确性. 这个Demo的难点在于向量矩阵之间的互相转换, 这里为何不说一维数组二维数组呢? , 缘由在于吴恩达的机器学习课程中也教会了我一些比较厉害的算法, 好比梯度降低之类的.

好了, 如今写文章没有以前频繁了, 缘由在于以前那些文章都太水, 太肤浅, 写了对本身也没有太大的意义, 被人看到也只会以为是垃圾而已... 因此在我第五阶段的学习后, 但愿可以有机会进入一家大厂继续深造吧!

最后 本文中全部的源码均可以在github上找到:

GitHub Repo:coderZsq.target.swift
Follow: coderZsq · GitHub
Resume: coderzsq.github.io/coderZsq.we…

相关文章
相关标签/搜索