作者:小傅哥
博客:https://bugstack.cn

积淀、分享、成长,让本人和别人都能有所播种!

一、前言

数学离程序员有多近?

ifelse也好、for循环也罢,代码能够说就是对数学逻辑的具体实现。所以敲代码的程序员简直就离不开数学,难易不同而已。

那数学不好就写不了代码吗?不,一样能够写代码,能够写出更多的CRUD进去。那你不要总感觉是产品需要简略所以你的实现过程才变成了增删改查,往往也是因为你还不具备可扩大、易保护、高性能的代码实现计划落地能力,才使得你小小年纪写出了更多的CRUD

与一锥子交易的小作坊相比,大厂和超级大厂更会重视数学能力。

2004年,在硅谷的交通动脉 101 公路上忽然呈现一块微小的广告牌,下面是一道数学题: {e 的间断数字中最先呈现的 10 位质数}.com。

广告:这里的 e 是数学常数,自然对数的底数,有限不循环小数。这道题的意思就是,找出 e 中最先呈现的 10 位质数,而后能够得出一个网址。进入这个网址会看到 Google 为你出的第二道数学题,胜利解锁这步 Google 会通知你,咱们或者是”气味相投“的人,你能够将简历发到这个邮箱,咱们一起做点扭转世界的事件。

计算 e 值能够通过泰勒公式推导进去:e^x≈1 + x + x^2/2! + x^3/3! +……+ x^n/n! (1) 推导计算过程还包含埃拉托色尼筛选法(the Sieve of Eratosthenes)线性筛选法的应用。感兴趣的小伙伴能够用代码实现下。

二、编程练习题

1. 斐波那契数列

@Testpublic void test_Fibonacci() {    int month = 15;  // 15个月    long f1 = 1L, f2 = 1L;    long f;    for (int i = 3; i < month; i++) {        f = f2;        f2 = f1 + f2;        f1 = f;        System.out.println("第" + i + "个月的兔子对数: " + f2);    }}
  • 难度:⭐⭐⭐⭐⭐
  • 题目:有一对兔子,从出世后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,如果兔子都不死,问每个月的兔子总数为多少?
  • 逻辑:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)
  • 扩大:斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子滋生为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的办法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)在古代物理、准晶体结构、化学等畛域,斐波纳契数列都有间接的利用,为此,美国数学会从 1963 年起出版了以《斐波纳契数列季刊》为名的一份数学杂志,用于专门刊载这方面的研究成果。

2. 判断素数

@Testpublic void test_Prime() {    int count = 0;    for (int i = 101; i < 200; i++) {        boolean b = true;// 默认此数就素数        for (int j = 2; j <= Math.sqrt(i); j++) {            if (i % j == 0) {                b = false; // 此数不是素数                break;            }        }        if (b) {            count++;            System.out.print(i + " ");        }    }    System.out.println("\n素数的个数:" + count);}
  • 难度:⭐⭐⭐
  • 题目:判断101-200之间有多少个素数,并输入所有素数。
  • 逻辑:判断素数的办法,用一个数别离去除2到sqrt(这个数),如果能被整除,则表明此数不是素数,反之是素数。
  • 扩大:素数又称质数,质数的个数是无穷的。欧几里得的《几何本来》中有一个经典的证实。它应用了证实罕用的办法:反证法。具体证实如下:假如质数只有无限的n个,从小到大顺次排列为p1,p2,……,pn,设N=p1×p2×……×pn,那么, 是素数或者不是素数。

3. 水仙花数

@Testpublic void test_narcissus() {    for (int num = 101; num < 1000; num++) {        int bbb = num / 100;        int bb = (num % 100) / 10;        int b = (num % 100) % 10;        if ((bbb * bbb * bbb + bb * bb * bb + b * b * b) == num) {            System.out.println(num);        }    }}
  • 难度:⭐⭐⭐⭐
  • 题目:打印出所有的"水仙花数(narcissus number)",所谓"水仙花数"是指一个三位数,其各位数字立方和等于该数自身。例如:153是一个"水仙花数",因为153=1的三次方+5的三次方+3的三次方。
  • 逻辑:利用for循环管制100-999个数,每个数合成出个位,十位,百位。
  • 扩大:水仙花数(Narcissistic number)也被称为超齐全数字不变数(pluperfect digital invariant, PPDI)、自恋数、自幂数、阿姆斯壮数或阿姆斯特朗数(Armstrong number),水仙花数是指一个 3 位数,它的每个位上的数字的 3次幂之和等于它自身(例如:1^3 + 5^3+ 3^3 = 153)。

4. 合成质因数

@Testpublic void test_ZhiYinShu() {    f(200);}int k = 2;public void f(int n) {    while (k <= n) {        if (k == n) {            System.out.println(n);            break;        } else if (n > k && n % k ==             System.out.print(k + "*")            n = n / k;            f(n);            break;        } else if (n > k && n % k !=             k++;            f(n);            break;        }    }}
  • 难度:⭐⭐⭐⭐
  • 题目:将一个正整数合成质因数。例如:输出90,打印出90=233*5。
  • 逻辑:对n进行合成质因数,应先找到一个最小的质数k,而后按此步骤实现(1)如果这个质数恰等于n,则阐明合成质因数的过程曾经完结,打印出即可。(2)如果n>k,但n能被k整除,则应打印出k的值,并用n除以k的商,作为新的正整数你n,反复执行第一步。(3)如果n不能被k整除,则用k+1作为k的值,反复执行第一步。
  • 扩大:每个合数都能够写成几个质数相乘的模式,其中每个质数都是这个合数的因数,把一个合数用质因数相乘的模式示意进去,叫做合成质因数。如30=2×3×5 。合成质因数只针对合数。

5. 杨辉三角

 @Test public void test_YangHuiSanJiao(){     int[][] a = new int[10][10];     for (int i = 0; i < 10; i++) {         a[i][i] = 1;         a[i][0] = 1;     }     for (int i = 2; i < 10; i++) {         for (int j = 1; j < i; j++) {             a[i][j] = a[i - 1][j - 1] + a[i - 1][j];         }     }     for (int i = 0; i < 10; i++) {         for (int k = 0; k < 2 * (10 - i) - 1; k++) {             System.out.print(" ");         }         for (int j = 0; j <= i; j++) {             System.out.print(a[i][j] + "   ");         }         System.out.println();     } }
  • 难度:⭐⭐⭐⭐
  • 题目:打印出杨辉三角形
  • 逻辑:杨辉三角形性质:每行数字左右对称,由1开始逐步变大,而后变小,回到1。第n行的数字个数为n个。第n行数字和为2^(n-1)。每个数字等于上一行的左右两个数字之和。可用此性质写出整个杨辉三角形。第n行的第1个数为1,第二个数为1×(n-1),第三个数为1×(n-1)×(n-2)/2,第四个数为1×(n-1)×(n-2)/2×(n-3)/3…依此类推。
  • 扩大:杨辉三角,是二项式系数在三角形中的一种几何排列,中国南宋数学家杨辉1261年所著的《详解九章算法》一书中呈现。在欧洲,帕斯卡(1623----1662)在1654年发现这一法则,所以这个表又叫做帕斯卡三角形。帕斯卡的发现比杨辉要迟393年,比贾宪迟600年。

6. 求最大公约数与最小公倍数

@Testpublic void test_Prime() {    int a = 10, b = 24;    int m = division(a, b);    int n = a * b / m;    System.out.println("最大公约数: " + m);    System.out.println("最小公倍数: " + n);}public int division(int x, int y) {    int t;    if (x < y) {        t = x;        x = y;        y = t;    }    while (y != 0) {        if (x == y)            return 1;        else {            int k = x % y;            x = y;            y = k;        }    }    return x;}
  • 难度:⭐⭐⭐
  • 题目:两个正整数m和n,求其最大公约数和最小公倍数。
  • 逻辑:在循环中,只有除数不等于0,用较大数除以较小的数,将小的一个数作为下一轮循环的大数,获得的余数作为下一轮循环的较小的数,如此循环直到较小的数的值为0,返回较大的数,此数即为最小公约数,最小公倍数为两数之积除以最小公倍数。
  • 扩大:最大公因数,也称最大公约数、最大公因子,指两个或多个整数共有约数中最大的一个。a,b的最大公约数记为(a,b),同样的,a,b,c的最大公约数记为(a,b,c),多个整数的最大公约数也有同样的记号。求最大公约数有多种办法,常见的有质因数分解法、短除法、辗转相除法、更相减损法。与最大公约数绝对应的概念是最小公倍数,a,b的最小公倍数记为[a,b]。两个或多个整数私有的倍数叫做它们的公倍数,其中除0以外最小的一个公倍数就叫做这几个整数的最小公倍数。整数a,b的最小公倍数记为[a,b],同样的,a,b,c的最小公倍数记为[a,b,c],多个整数的最小公倍数也有同样的记号。

7. 齐全平方数

@Testpublic void test_PerfectSquare() {    for (long l = 1L; l < 100000; l++) {        if (Math.sqrt((l + 100)) % 1 == 0) {            if (Math.sqrt((l + 268)) % 1 == 0) {                System.out.println(l + "加100是一个齐全平方数,再加168又是一个齐全平方数");            }        }    }}
  • 难度:⭐⭐⭐⭐
  • 题目:一个整数,它加上100后是一个齐全平方数,再加上168又是一个齐全平方数,请问该数是多少?
  • 逻辑:在10万以内判断,先将该数加上100后再开方,再将该数加上268后再开方,如果开方后的后果满足如下条件,即是后果。
  • 扩大:齐全平方指用一个整数乘以本人例如11,22,3*3等,依此类推。若一个数能示意成某个整数的平方的模式,则称这个数为齐全平方数。齐全平方数是非正数,而一个齐全平方数的项有两个。留神不要与齐全平形式所混同。

8. 求主对角线之和

@Testpublic void test_Sum() {    Scanner s = new Scanner(System.in);    int[][] a = new int[3][3];    for (int i = 0; i < 3; i++) {        for (int j = 0; j < 3; j++) {            a[i][j] = s.nextInt();        }    }    System.out.println("输出的3 * 3 矩阵是:");    for (int i = 0; i < 3; i++) {        for (int j = 0; j < 3; j++) {            System.out.print(a[i][j] + " ");        }        System.out.println();    }    int sum = 0;    for (int i = 0; i < 3; i++) {        for (int j = 0; j < 3; j++) {            if (i == j) {                sum += a[i][j];            }        }    }    System.out.println("对角线和是 " + sum);}
  • 难度:⭐⭐⭐
  • 题目:求一个3*3矩阵对角线元素之和
  • 逻辑:利用双重for循环管制输出二维数组,再将ai累加后输入。
  • 扩大:在一个n阶方阵(或是n阶行列式)中,从左上角到右下角这一斜线方向上的n 个元素所在的对角线,叫做n 阶方阵(或行列式)的主对角线。

9. 完数求解

@Testpublic void test_solution() {    System.out.println("1到1000的完数有: ");    for (int i = 1; i < 1000; i++) {        int t = 0;        for (int j = 1; j <= i / 2; j++) {            if (i % j == 0) {                t = t + j;            }        }        if (t == i) {            System.out.print(i + " ");        }    }}
  • 难度:⭐⭐⭐⭐
  • 题目:一个数如果恰好等于它的因子之和,这个数就称为 "完数 "。例如6=1+2+3.编程 找出1000以内的所有完数
  • 逻辑:如果p是质数,且2^p-1也是质数,那么(2^p-1)X2^(p-1)便是一个齐全数。
  • 扩大:齐全数(Perfect number),又称完满数或齐备数,是一些非凡的自然数。它所有的真因子(即除了本身以外的约数)的和(即因子函数),恰好等于它自身。

10. 求s=a+aa+aaa+aaaa+aa...a的值

@Testpublic void test_asum() {    long a = 2, b = 0;    Scanner s = new Scanner(System.in);    int n = s.nextInt();    int i = 0;    long sum = 0;    while (i < n) {        b = b + a;        sum = sum + b;        a = a * 10;        ++i;    }    System.out.println("input number: " + n);    System.out.println(sum);}
  • 难度:⭐⭐⭐
  • 题目:求s=a+aa+aaa+aaaa+aa...a的值,其中a是一个数字。例如2+22+222+2222+22222(此时共有5个数相加),几个数相加有键盘管制。
  • 逻辑:定义一个变量b, 赋初值为0;定义一变量sum, 赋初值为0,进入循环后,将a + b 的值赋给b,将sum + b 的值赋给sum;同时,将a 减少十倍, ++ i; 持续循环;循环完结后,输入sum 的值。

11. 无反复三位数

@Testpublic void test_AC() {    int count = 0;    for (int x = 1; x < 5; x++) {        for (int y = 1; y < 5; y++) {            for (int z = 1; z < 5; z++) {                if (x != y && y != z && x != z) {                    count++;                    System.out.print(x * 100 + y * 10 + z + "   ");                    if (count % 4 == 0) {                        System.out.println();                    }                }            }        }    }    System.out.println("共有" + count + "个三位数");}
  • 难度:⭐⭐⭐⭐
  • 题目:有1、2、3、4个数字,能组成多少个互不雷同且无反复数字的三位数?都是多少?
  • 逻辑:可填在百位、十位、个位的数字都是1、2、3、4。组成所有的排列后再去掉不满足条件的排列。

12. 从小到大输入数列

public class SmallToBig {    public static void main(String[] args) {        SmallToBig fnc = new SmallToBig();        int a, b, c;        System.out.println("Input 3 numbers:");        a = fnc.input();        b = fnc.input();        c = fnc.input();        if (a > b) {            int t = a;            a = b;            b = t;        }        if (a > c) {            int t = a;            a = c;            c = t;        }        if (b > c) {            int t = b;            b = c;            c = t;        }        System.out.println(a + " " + b + " " + c);    }    public int input() {        int value = 0;        Scanner s = new Scanner(System.in);        value = s.nextInt();        return value;    }    public void compare(int x, int y) {// 此办法没用        if (x > y) {            int t = x;            x = y;            y = t;        }    }}
  • 难度:⭐⭐
  • 题目:输出三个整数x,y,z,请把这三个数由小到大输入
  • 逻辑:方法把最小的数放到x上,先将x与y进行比拟,如果x> y则将x与y的值进行替换,而后再用x与z进行比拟,如果x> z则将x与z的值进行替换,这样能使x最小。

13. 猴子吃桃问题

public class Monkey {        public static void main(String[] args) {        int lastdayNum = 1;        for (int i = 2; i <= 10; i++) {            lastdayNum = (lastdayNum + 1) * 2;        }        System.out.println("猴子第一天摘了 " + lastdayNum + " 个桃子");    }}
  • 难度:⭐⭐⭐⭐
  • 题目:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个 第二天早上又将剩下的桃子吃掉一半,又多吃了一个。当前每天早上都吃了前一天剩下 的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。求第一天共摘了多少。
  • 逻辑:采取逆向思维的办法,从后往前推断。

14. 乒乓球较量

public class Compete {        static char[] m = { 'a', 'b', 'c' };    static char[] n = { 'x', 'y', 'z' };    public static void main(String[] args) {        for (int i = 0; i < m.length; i++) {            for (int j = 0; j < n.length; j++) {                if (m[i] == 'a' && n[j] == 'x') {                    continue;                } else if (m[i] == 'a' && n[j] == 'y') {                    continue;                } else if ((m[i] == 'c' && n[j] == 'x')                        || (m[i] == 'c' && n[j] == 'z')) {                    continue;                } else if ((m[i] == 'b' && n[j] == 'z')                        || (m[i] == 'b' && n[j] == 'y')) {                    continue;                } else                    System.out.println(m[i] + " vs " + n[j]);            }        }    }}
  • 难度:⭐⭐⭐⭐
  • 题目:两个乒乓球队进行较量,各出三人。甲队为a,b,c三人,乙队为x,y,z三人。已抽签决定较量名单。有人向队员打听较量的名单。a说他不和x比,c说他不和x,z比,请编程序找出三队赛手的名单。

15. 求分数之和

public class FenShu {    public static void main(String[] args) {        int x = 2, y = 1, t;        double sum = 0;        DecimalFormat df = new DecimalFormat("#0.0000");        for (int i = 1; i <= 20; i++) {            sum += (double) x / y;            t = y;            y = x;            x = y + t;            System.out.println("第 " + i + " 次相加,和是 " + df.format(sum));        }    }}
  • 难度:⭐⭐⭐⭐
  • 题目:有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13...求出这个数列的前20项之和。
  • 逻辑:抓住分子与分母的变化规律。

16. 求阶乘的和

public class JieCheng {    static long sum = 0;    static long fac = 0;    public static void main(String[] args) {        long sum = 0;        long fac = 1;        for (int i = 1; i <= 10; i++) {            fac = fac * i;            sum += fac;        }        System.out.println(sum);    }}
  • 难度:⭐⭐⭐
  • 题目:求1+2!+3!+...+20!的和
  • 逻辑:把累加变成了累乘
  • 扩大:阶乘是基斯顿·卡曼(Christian Kramp,1760~1826)于 1808 年创造的运算符号,是数学术语。一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且0的阶乘为1。自然数n的阶乘写作n!。1808年,基斯顿·卡曼引进这个表示法。

17. 回文判断

public class HuiWen {    public static void main(String[] args) {        Scanner s = new Scanner(System.in);        System.out.print("请输出一个正整数:");        long a = s.nextLong();        String ss = Long.toString(a);        char[] ch = ss.toCharArray();        boolean is = true;        int j = ch.length;        for (int i = 0; i < j / 2; i++) {            if (ch[i] != ch[j - i - 1]) {                is = false;            }        }        if (is == true) {            System.out.println("这是一个回文数");        } else {            System.out.println("这不是一个回文数");        }    }}
  • 难度:⭐⭐⭐
  • 题目:一个5位数,判断它是不是回文数。即12321是回文数,个位与万位雷同,十位与千位雷同。

18. 按程序输入数列

public class ShunXu {    public static void main(String[] args) {        Scanner s = new Scanner(System.in);        int a = s.nextInt();        int b = s.nextInt();        int c = s.nextInt();        if (a < b) {            int t = a;            a = b;            b = t;        }        if (a < c) {            int t = a;            a = c;            c = t;        }        if (b < c) {            int t = b;            b = c;            c = t;        }        System.out.println("从大到小的程序输入:");        System.out.println(a + " " + b + " " + c);    }}
  • 难度:⭐⭐⭐
  • 题目:输出3个数a,b,c,按大小程序输入

19. 地位替换

public class TiHuan {        static final int N = 8;    public static void main(String[] args) {        int[] a = new int[N];        Scanner s = new Scanner(System.in);        int index1 = 0, index2 = 0;        System.out.println("please input numbers");        for (int i = 0; i < N; i++) {            a[i] = s.nextInt();            System.out.print(a[i] + " ");        }        int max = a[0], min = a[0];        for (int i = 0; i < a.length; i++) {            if (a[i] > max) {                max = a[i];                index1 = i;            }            if (a[i] < min) {                min = a[i];                index2 = i;            }        }        if (index1 != 0) {            int temp = a[0];            a[0] = a[index1];            a[index1] = temp;        }        if (index2 != a.length - 1) {            int temp = a[a.length - 1];            a[a.length - 1] = a[index2];            a[index2] = temp;        }        System.out.println("after swop:");        for (int i = 0; i < a.length; i++) {            System.out.print(a[i] + " ");        }    }}
  • 难度:⭐⭐
  • 题目:输出数组,最大的与第一个元素替换,最小的与最初一个元素替换,输入数组。

20. 1的个数

long startTime = System.currentTimeMillis();int num = 10000000, saveNum = 1, countNum = 0, lastNum = 0;int copyNum = num;while (num != 0) {    lastNum = num % 10;    num /= 10;    if (lastNum == 0) {        // 如果是0那么正好是少了一次所以num不加1了        countNum += num * saveNum;    } else if (lastNum == 1) {        // 如果是1阐明以后数内少了一次所以num不加1,而且以后1所在位置        // 有1的个数,就是去除以后1最高位,剩下位数,的个数。        countNum += num * saveNum + copyNum % saveNum + 1;    } else {        // 如果非1非0.间接用公式计算        // abcd...=(abc+1)*1+(ab+1)*10+(a+1)*100+(1)*1000...        countNum += (num + 1) * saveNum;    }    saveNum *= 10;}System.out.println("1的个数:" + countNum);System.out.println("计算耗时:" + (System.currentTimeMillis() - startTime) + "毫秒");
  • 难度:⭐⭐⭐⭐
  • 题目:1~n中,1呈现的次数。比方:1~10,1呈现了两次。
  • 逻辑:咱们能发现这个1的个数在100、1000、10000中是有规定的循环呈现的。11、12、13、14或者21、31、41、51,以及单个的1呈现。最终能够得出通用公式:abcd...=(abc+1)1+(ab+1)10+(a+1)100+(1)1000...,abcd代表位数。另外在实现的过程还须要思考比方有余100等状况,例如98、1232等。

三、深度扩大

Java 代码自身就是基于数据结构和算法对数学逻辑的具体实现,而那些隐含在代码中的数学知识如果你不会,那么压根你就会疏忽掉它,也就因而看不懂源码了。

就像我问你:

  • HashCode为什么用31作为乘数,你证实过吗?
  • 扰动函数的函数作用是什么,它还有什么场景在用?
  • 拉链寻址和凋谢寻址具体是什么体现,怎么解决的碰撞问题?
  • ThreadLocal 的实现中还有黄金分割点的应用,你晓得吗?
  • CLH、MCS,都是怎么实现的偏心锁,代码是什么样?
  • jvmti 能够用于非入侵的监控线程池状态,你用过吗?

所以小傅哥整顿了一本,《Java 面经手册》 是一本以面试题为入口解说 Java 核心技术的 PDF 书籍,书中内容也竭力的向你证实代码是对数学逻辑的具体实现为什么这么说? 当你仔细阅读书籍时,会发现这里有很多数学知识,包含:扰动函数、负载因子、拉链寻址、凋谢寻址、斐波那契(Fibonacci)散列法还有黄金分割点的应用等等。

Java 面经手册,材料下载:https://codechina.csdn.net/MiddlewareDesign/doc/-/issues/8

四、总结

  • Programming is one of the most difficult branches of applied mathematics; the poorer mathematicians had better remain pure mathematicians. https://www.cs.utexas.edu/use...
  • 单纯的只会数学写不了代码,能写代码的不懂数学只能是CRUD码农。数学知识帮忙你设计数据结构和实现算法逻辑,代码能力帮你驾驭设计模式和架构模型。多方面的常识联合和应用才是码农和工程师的次要区别,也是是否领有外围竞争力的关键点。
  • 学习常识有时候看不到后面的路有多远,但哪怕是个泥坑,只有你不停的蠕动、折腾、翻滚,也能抓出一条泥鳅。常识的路上是发现常识的高兴,还学会常识的成就感,一直的促使你前行

五、系列举荐

  • 大学四年到毕业工作5年的学习路线资源汇总
  • 工作两年简历写成这样,谁要你呀!
  • 工作3年,看啥材料能月薪30K?
  • 程序员出一本技术书,是什么 体验?
  • 小伙伴美团一面的分享和剖析(含解答)