❤️学姐说她用 8 行代码写了 8 个算法(上)❤️(推荐收藏)
Posted 英雄哪里出来
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了❤️学姐说她用 8 行代码写了 8 个算法(上)❤️(推荐收藏)相关的知识,希望对你有一定的参考价值。
一、前言
本文适合对算法处于朦胧期的初学者,文字浅显易懂,并且配有生动有趣的动图,也是作者呕心沥血之作,希望对刚入大学,或者职场上想要涉足算法的青年同僚有所启示。
学习算法,任何时候都不嫌晚,大不了就是大器晚成而已,所以无论你是30岁,40岁,50岁,甚至60岁,只要下了决心,就已经成功了一半!本文的故事发生在 ❤️学姐教你 10 道题搞定 c 语言❤️ 的两年后,剧情扑朔迷离,作者至今回忆起来还历历在目。
二、朝思暮想
- 自从上次一别,不知何时才能相见,不免有些感伤。于是,那天晚上,我,辗转反侧,彻夜难眠,寝不安席,食无甘味。
不太聪明的亚子 - 从来没有一个女孩子可以让我如此朝思暮想,魂牵梦萦。
- 可能是因为她还没有把她的毕生算法教会给我,怎么一声不吭就人间蒸发了呢!我不甘心!
- 就算是天涯海角,我也要找到你!
- 终于,在一个夜黑风高的晚上,让我在睡梦中见到了她,梦里的她比现实中还要逗比。竟然给我写下了八行代码!
- 也就是因为那个晚上,成就了我后来的 ❤️《夜深人静写算法》❤️
- 她告诉我,一行代码代表一个算法!我觉得她在侮辱我的智商!
- 那天晚上大致是这样的 … …
三、南柯一梦
四、梦中的梦中
- 哎,越想越不对劲!
- 所以我打算继续睡,看看能不能继续梦到学姐。
- 果然……功夫不负有心人……
- 学姐出现了!!!
- 学姐还是像往常一样,心思缜密,替人着想,越来越崇拜她了。
五、梦中人的梦中
算法一
【例题1】给定 n ( n ≤ 65535 ) n(n \\le 65535) n(n≤65535),求 ∑ i = 1 n i = 1 + 2 + . . . + n \\sum_{i=1}^n i = 1 + 2 + ... + n ∑i=1ni=1+2+...+n。
- 这是一个等差数列!
- 我直接用等差数列的求和公式就行了。
int sum(int n) {
return n * (n + 1) / 2;
}
- 然后我调试了一下,发现:
- 当 n = 65535 n=65535 n=65535 时,输出的竟然是负数!
原因是因为 n ∗ ( n + 1 ) = 65535 ∗ 65536 = ( 2 16 − 1 ) 2 16 = 2 32 − 2 16 n * (n + 1) = 65535 * 65536 = (2^{16}-1)2^{16} = 2^{32} -2^{16} n∗(n+1)=65535∗65536=(216−1)216=232−216,而 i n t int int 能够表示的最大值为 2 31 − 1 2^{31}-1 231−1,所以产生了溢出。就变成了负数。至于为什么溢出会变成负数,可以了解补码相关的知识:c++ 补码详解。
- 这里只需要对 n n n 进行奇偶性判定,将除法放在乘法之前,就可以防止溢出了。即:
- s u m ( n ) = { ( n + 1 ) / 2 × n n 为 奇 数 n / 2 × ( n + 1 ) n 为 偶 数 sum(n) = \\begin{cases} (n+1)/2 \\times n & n 为奇数 \\\\ n/2 \\times (n+1) & n 为偶数 \\end{cases} sum(n)={(n+1)/2×nn/2×(n+1)n为奇数n为偶数
- c++ 实现如下:
int sum(int n) {
if(n % 2 == 1) {
return (n + 1) / 2 * n;
}else {
return n / 2 * (n + 1);
}
}
- 然后我们再通过三目运算符写成一行代码,如下:
int sum(int n) {
return (n%2) ? (n+1)/2*n : n/2*(n+1);
}
这里的
condition ? a : b
是 c/c++ 中的三目运算符,含义是根据表达式condition
的值的真或假,选择返回a
还是b
。由于对于一个数 x x x, x x x 非0就是真,为0就是假,所以可以直接省略x == 0
的判断。
- 通过这段代码,我了解了 32位整数的溢出、补码表示、三目运算符、表达式真值的省略写法。
算法二
【例题2】给定 n ( n < 16 ) n(n \\lt 16) n(n<16),求 ∏ i = 1 n i = 1 × 2 × . . . × n \\prod_{i=1}^n i = 1 \\times 2 \\times ... \\times n ∏i=1ni=1×2×...×n。
- 由于 n n n 比较小,所以我打算直接暴力枚举,大概可以写成这样:
int sum(int n) {
int s = 1;
for(int i = 1; i <= n; ++i) {
s *= i;
}
return s;
}
- 但是学姐说的一行代码好像比较难办到,我继续压缩,把
s
这个变量放到循环体内和i
一起初始化,并且把乘法和循环放到同一行,变成了下面这副样子。
int sum(int n) {
for(int s = 1, i = 1; i <= n; ++i) s *= i;
return s;
}
- 这时候发现编译不过!!!
- 原因是:
s
的作用域在循环体内,所以无法在循环体外部进行使用,但是我们这个函数有需要有一个返回值,总不能把函数体给返回吧?这可如何是好!
- 由于那时候,我对递归还没有什么概念,所以一脸懵逼。
- 我还是听不懂……
- 这下我就懂了!
- 我们可以定义这么一个函数 f ( x ) = 1 × 2 × 3 × . . . × x f(x) = 1 \\times 2 \\times 3 \\times ... \\times x f(x)=1×2×3×...×x,其中 x ≥ 0 x \\ge 0 x≥0。
- 当 x > 0 x > 0 x>0 时,代入 ( x − 1 ) (x-1) (x−1),显然有 f ( x − 1 ) = 1 × 2 × 3 × . . . × ( x − 1 ) f(x-1) = 1 \\times 2 \\times 3 \\times ... \\times (x-1) f(x−1)=1×2×3×...×(x−1);
- 于是,可以得到:
- f ( x ) f ( x − 1 ) = x \\frac {f(x)} {f(x-1)} = x f(x−1)f(x)=x
- 由于 f ( x − 1 ) > 0 f(x-1) > 0 f(x−1)>0, 等式两边可以同时乘上 f ( x − 1 ) f(x-1) f(x−1),很容易得出递推公式如下:
- f ( x ) = { 1 ( x = 0 ) f ( x − 1 ) × x ( x > 0 ) f(x) = \\begin{cases} 1 & (x = 0) \\\\ f(x-1) \\times x & (x > 0)\\end{cases} f(x)={1f(x−1)×x(x=0)(x>0)
- 所以,翻译成 c/c++ 的语言,就可以写成这样:
int f(int x) {
if(x == 0) {
return 1;
}else {
return f(x-1) * x;
}
}
- 然后利用三目运算符,改成一行代码,得到:
int f(int x) {
return x ? f(x-1) * x : 1;
}
- 通过这段代码,我了解了 递推公式 和 递归调用。
算法三
【例题3】现在有一个 n ( n ≤ 10000 ) n(n \\le 10000) n(n≤10000) 个元素的数组 a [ i ] a[i] a[i],但是我们已知的是前 i i i 个元素的和 f [ i ] ( 1 ≤ i ≤ n , 1 ≤ f [ i ] ≤ 100000 ) f_[i](1 \\le i \\le n, 1 \\le f[i] \\le 100000) f[i](1≤i≤n,1≤f[i]≤100000),然后给出 Q ( Q ≤ 1000000 ) Q(Q \\le 1000000) 以上是关于❤️学姐说她用 8 行代码写了 8 个算法(上)❤️(推荐收藏)的主要内容,如果未能解决你的问题,请参考以下文章
熬夜帮学姐用Python完成词云图,没想到我好兄弟竟然...