作为「前端切图仔」的我为什么学习算法?

548 阅读6分钟

Tip:本文无需算法知识即可食用

再Tips:标题中的「前端切图仔」也可替换为「CURD工程师」、「找茬工程师」、「调参侠」等,欢迎对号入座~

让我们先看两个有趣的问题~

1.数学优化,降维打击:

实现一个方法,使其可以:接收一个正整数,把每一数位上的数字相加,直到和为个位数

f(12345) -> 1 + 2 + 3 + 4 + 5 = 15

f(15) -> 1 + 5 = 6

f(6) // return 6

常规思路:

将传入的数字视为一个字符串,循环相加求和,递归直到字符串的length等于1。

伪代码:

f(num) {
  if(num.length === 1) return num
    
  let both = 0;
  for(let i = 0; i < num.length; i++) {
    both += num[i]
  }
  
  return f(both)
}

(点击下方文字展开)

降维打击解法←
已知数字abc可以写作100a + 10b + c (123 = 100 x 1 + 10 x 2 + 1 x 3)

那么每次给出的数字经过f()处理后,变化如下:

处理算式 100a + 10b + c -> a + b + c

100a + 10b + c - (a + b + c) -> a + b + c - (a + b + c)

99a + 9b -> 0

9(11a + b) -> 0

即每次减少的都是9的倍数,当某次递归的参数数字小于9时,就是最终结果。

简单验证一下:

f(123) -> 1 + 2 + 3 -> 6 -> 117 -> 13 x 9

f(2345) -> 2 + 3 + 4 + 5 -> 14 -> 2331 -> 259 x 9

现在有一个问题,【如果一个数正好可以被9整除怎么办?】

可以使用先减后加的方法:取余之前减一,然后再给余数加一

81可以被9整除,那么我们取余的时候用80去取

80 % 9 = 8 -> 8 + 1 = 9

与原题目描述中f(81) -> 8 + 1 = 9 结果相符

【点击展开最终结果】
    f(n) {
      return (n - 1) % 9 + 1;
    }
    

摘自:大家都见过哪些让你虎躯一震的代码? - 王一的回答 - 知乎 www.zhihu.com/question/28…)

2.敢为人先

亚历克斯和李用几堆石子在做游戏。偶数堆石子排成一行,每堆都有正整数颗石子。

游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。

亚历克斯和李轮流进行,亚历克斯先开始。 每回合,玩家从行的开始或结束处取走整堆石头。 这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜。

假设亚历克斯和李都发挥出最佳水平,当亚历克斯赢得比赛时返回 true ,当李赢得比赛时返回 false 。

人话描述:

假设有一波石头子,被分成若干(偶数)堆,每堆数量「不一样」,类似下面这样横着一排放在你面前:

331619423664_.pic_hd.jpg

现在你和你的好朋友韩梅梅进行一场比赛:两人轮流从这一排石头子的左端或右端拿一「堆」石头子,到最后拿完为止,谁手里「所有」石头子的总数最多谁获胜。由你先开始拿,可以保证的是:你和韩梅梅都 智商正常 能发挥最佳水平;不会出现平局的情况;当你获胜的时候返回true,当韩梅梅获胜的时候,返回false

对于没接触过算法的人来说,看到这道题大概率是一头雾水。我是谁???我为什么要拿石子???我拿石子有什么用???

wenhao

对算法有一定经验的人看到这道题,可能会想到暴力递归 + 贪心算法,动态规划 + map储存所有子集避免指数级的复杂度,然后一顿操作。

caozuo

放心,这里我们不讨论具体如何实现,直接带你看结果:

(点击下边文字展开)

见证奇迹的时刻←
答案只有一行
return true
没错!真相就是:先手必胜

刚看到这个解的时候,我的理解是:先手的人,每次都可以从最左最右的两堆中选择最大的,那么周而复始,先手的石子总数肯定要比后手大。

不过很快我就想到一个极端反例来证伪:[3,10000000,2,1]

这时如果先手拿大的,那么你必输。

正确的理解应该是:把所有石子按照第奇数位和第偶数位分成两个组合(第1、3、5...为第奇数位,第2、4、6...为第偶数位),先手的人可以迫使后手的人选择总和较小的组合。


也许看到这里你还觉得没什么,那么我再来介绍一下这个题解的产生背景:

  • 这道题出自leetcode的第95场周赛
  • 这道题的原题目是纯英文
  • 给出这个题解的日本选手uwi从审题到给出答案,一共只用了1分34秒

感受一下原题目:



一分多钟我大概题目还没看完吧~

参考:原题链接

回归生活

也许你没有注意到,生活中也处处可见算法。大到机器学习、图像识别、自动驾驶,小到导航软件路线规划、网购优惠券的最大折扣。虽然这些东西并不需要你运用算法知识去自己解决

但你是否遇到过这个情景:实现一个模块/逻辑,内心的想法:

实际写出来的效果:

你是否曾感叹过:对于同一件事,别人看待的角度为什么如此巧妙,观点为什么会这么新颖?

那么,回到文章标题的问题中来,「我」为什么会看算法?

  • 因为在解算法题的过程当中,锻炼了我实现代码的能力
  • 因为在看别人的优秀解法时,我体会到了从不同角度切入问题的奇妙之处

最后,以某乎上一条回答中我非常喜欢的一段话作为结尾:

比如说,简单的冒泡算法,它是不是只是“多次扫描一个数组,交换遇到的每一对相邻的、顺序反了的数字;当不再发生交换时,数组已完成排序”甚至”好不容易才死记硬背下来的一段代码“?

如果你只学会了这个,那么,你真就完全白学了。

作为一个表现一般的排序算法,冒泡排序本身出场率就不高;何况还有各种提供了泛化的sort算法的库:如果仅仅记下了这个,那么你一辈子都不会遇到”必须重写冒泡算法“的场合。

但,如果你把冒泡算法记成: 就好象水中的气泡一样,每次只执行“相邻的元素比较密度(或其它特征),密度小的上浮,密度大的下沉”这个局部物理过程;多次进行后,局部有序就会变成(相关特征上的)整体有序。

甚至: 模仿各种会导致整体有序现象的局部过程去处理数据,可以使得数据整体上满足类似的排布。

甚至: 考察任何自然规律,看它会产生什么有趣的后果;那么当需要达到类似的效果时,不妨尝试用程序模拟出这个规律,很可能就已经得到了想要的效果。

那,你这一生,可就受用不尽了。

作者:invalid s

出处:www.zhihu.com/question/20…