您当前的位置: 首页 >  Java

暂无认证

  • 8浏览

    0关注

    97562博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

【Java -- 算法】数字算法

发布时间:2020-04-06 20:28:31 ,浏览量:8

一、前言

本文介绍了有关 数字 的算法第一部分的 Java 代码实现,算法实例:

  • 斐波那契数列(循环算法、矩阵算法)
  • 跳台阶问题
  • 数值的整数次方
  • 打印1到最大的n位数
  • 计算从1到n中1出现的个数
  • 求两个数的二进制表示中有多少个是不同的
  • 给定一个整数N,求N!的末尾有多少个0
  • 给定一个整数N,求N!的二进制表示中最低位1的位置
  • 最大公约数
  • 精确地表达有限/无限循环小数
二、代码 1. 斐波那契数列(循环算法、矩阵算法)

问题描述

斐波那契数列 满足下面的通项公式,要求给出N,输出第N项的F(N)

F(0)=0, F(1)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)

解决思路

这里介绍两种解决办法,循环算法 和 矩阵算法。循环算法比较容易理解,就是从F(0)开始,根据通项公式,得到下一个斐波那契数列中的数字即可。

循环算法实现代码

class Untitled { static double fibonacci(int n) { if (n < 2) { return n; } //f(n-2) double fnMinus2 = 0; //f(n-1) double fnMinus1 = 1; double result = 0; for (int i=2; i<=n; i++) { //根据通项公式计算。 result = fnMinus2 + fnMinus1; fnMinus2 = fnMinus1; fnMinus1 = result; } return result; } public static void main(String[] args) { double loopResult = fibonacci(10); System.out.println("loopResult=" + loopResult); } } 

循环算法运行结果

loopResult=55.0

矩阵算法代码实现

class Untitled { static class Matrix { double a; double b; double c; double d; public Matrix(double a, double b, double c, double d) { this.a = a; this.b = b; this.c = c; this.d = d; } } //矩阵乘法函数。 static Matrix multiMatrix(Matrix a, Matrix b) { Matrix result = new Matrix(0, 0, 0, 0); result.a = a.a*b.a + a.b*b.c; result.b = a.a*b.b + a.b*b.d; result.c = a.c*b.a + a.d*b.c; result.d = a.c*b.b + a.d*b.d; return result; } //核心函数。 static double fibonacciMatrix(int n) { if (n < 2) { return n; } Matrix r = new Matrix(1, 0, 0, 1); Matrix tmp = new Matrix(1, 1, 1, 0); n--; while (n > 0) { if ((n&1) > 0) { r = multiMatrix(r, tmp); } tmp = multiMatrix(tmp, tmp); n >>= 1; } return r.a; } public static void main(String[] args) { double matrixResult = fibonacciMatrix(10); System.out.println("matrixResult=" + matrixResult); } } 

矩阵算法运行结果

matrixResult=55.0

2. 跳台阶问题

问题描述

一个台阶总共有n级,如果一次可以跳1级,也可以跳2级,求总共有多少总跳法。

解决思路 由于有两种跳台阶方式,因此跳n级台阶可以转换为下面两个问题之和:

  • 第一次跳1级台阶,之后的解法数为f(n-1)
  • 第一次跳2级台阶,之后的解法数为f(n-2)

这就和之前的斐波那契数列的通项公式相同。

实现代码

class Untitled { static class Matrix { double a; double b; double c; double d; public Matrix(double a, double b, double c, double d) { this.a = a; this.b = b; this.c = c; this.d = d; } } //矩阵乘法函数。 static Matrix multiMatrix(Matrix a, Matrix b) { Matrix result = new Matrix(0, 0, 0, 0); result.a = a.a*b.a + a.b*b.c; result.b = a.a*b.b + a.b*b.d; result.c = a.c*b.a + a.d*b.c; result.d = a.c*b.b + a.d*b.d; return result; } //核心函数。 static double fibonacciMatrix(int n) { if (n < 2) { return n; } Matrix r = new Matrix(1, 0, 0, 1); Matrix tmp = new Matrix(1, 1, 1, 0); n--; while (n > 0) { if ((n&1) > 0) { r = multiMatrix(r, tmp); } tmp = multiMatrix(tmp, tmp); n >>= 1; } return r.a; } static double stepCounter(int step) { if (step <= 2) { return step; } return fibonacciMatrix(step); } public static void main(String[] args) { double count = stepCounter(50); System.out.println("counter=" + count); } } 
3. 数值整数次方

实现代码

class Untitled { static double powerOfNum(int num, int power) { int tmp = num; int r = 1; while (power > 0) { //如果在该位上为1,那么就乘上对应的n次方。 if ((power&1) > 0) { r = r*tmp; } tmp = tmp*tmp; power >>= 1; } return r; } public static void main(String[] args) { System.out.println("powerOfNum=" + powerOfNum(2, 10)); } } 

运行结果

powerOfNum=1024.0

4. 打印 1 到最大的 n 位数

实现代码

class Untitled { static void printNumberCore(int p[], int depth, int len) { //到达末尾,打印当前数组中的元素。 if (depth == len+1) { StringBuilder builder = new StringBuilder(); for (int i=1; i<=len; i++) { builder.append(String.valueOf(p[i])); } System.out.println(builder.toString()); return; } //如果是首位,那么从1开始,否则从0开始。 int pStart = 0; if (depth == 1) { pStart = 1; } for (int i=pStart; i<=9; i++) { //替换该位,并进行递归。 p[depth] = i; printNumberCore(p, depth+1, len); } } static void printNumber(int n) { //首先建立数组。 int p[] = new int[n+1]; //len表示有几位数。 for (int len=1; len<=n; len++) { //对应于len位数的全排列。 printNumberCore(p, 1, len); } } public static void main(String[] args) { printNumber(3); } } 
5. 计算从 1 到 n 中 1 出现的个数

解决思路 这个问题,需要先总结一下规律,我们根据数字N的 位数 来进行分析:

一位数 那么N>=1时才会出现1,并且出现1的次数为1次

两位数 在这种情况下,出现1的次数等于个位上出现1的次数加上十位上出现1的个数。

  • 个位上出现1的次数不仅和个位的数字有关,还和十位的数字有关:

    • 如果个位为0,那么个位上出现1的次数等于十位的数字。
    • 如果个位大于0,那么个位上出现1的次数等于十位的数字加1。
  • 十位上出现1的次数不仅和十位的数字有关,还和个位的数字有关:

    • 如果十位为1,那么十位上出现1的次数等于个位的数字加1。
    • 如果十位大于1,那么十位上出现1的次数等于10。

N 位数 例如,如果要计算百位上1出现的次数,它要受到三方面的影响:百位上的数字,百位以下的数字,百位以上的数字。

  • 如果百位上数字为0,百位上可能出现1的次数仅由更高位决定,等于更高位数字乘以当前位数。

  • 如果百位上数字为1,百位上可能出现1的次数不仅受更高位影响还受低位影响:

    • 受高位影响的部分等于更高位数字乘以当前位数
    • 受低位影响等于低位数字加上1。
  • 如果百位上数字大于1,则百位上出现1的情况仅由更高位决定,等于更高位数字加上1乘以当前位数。

实现代码

class Untitled { static int countOneNum(int data) { int iFac = 1; int countNum = 0; int lowNum = 0; int curNum = data % 10; int highNum = data / 10; //从个位数开始遍历,每次while循环向前移动一位,计算每一位出现1的次数,总和就是问题的解。 while (curNum > 0 || highNum > 0) { if (curNum == 0) { countNum += highNum * iFac; } else if (curNum == 1) { countNum += highNum * iFac + (lowNum + 1); } else { countNum += (highNum+1)*iFac; } lowNum = lowNum + curNum*iFac; curNum = highNum % 10; highNum = highNum / 10; iFac *= 10; } return countNum; } public static void main(String[] args) { System.out.println("n=" + 123 + ",result=" + countOneNum(123)); } } 

运行结果

n=123,result=57

6. 求两个数的二进制表示中有多少个是不同的

解决思路 对于一个二进制数,例如1010,将其减1后得到的结果是1001,也就是将最后一个1(倒数第二位)及其之后的0变成1,1变成0,再将该结果与原二进制数相与,也就是1010 & 1001 = 1000,那么就可以去掉最后一个1。

因此,如果需要计算两个数的二进制表示中有多少位是不同的,可以 先将这两个数异或,那么不相同的位数就会变成1,之后利用上面的技巧,通过每次去掉最后一个1,来 统计该结果中1的个数,就可以知道两个数的二进制表示中有多少是不同的了。

代码实现

class Untitled { static int getDiffBit(int a, int b) { int diff = a^b; int count = 0; while (diff > 0) { count++; diff = diff & (diff-1); } return count; } public static void main(String[] args) { System.out.println("result=" + getDiffBit(1999, 2299)); } } 

运行结果

result=7

7. 给定一个整数 N,求 N! 的末尾有多少个 0

问题描述 N!的含义为1*2*3*...*(N-1)*N,计算 N! 的十进制表示中,末尾有多少个0。

解决思路 N!中能产生末尾是0的质数组合是2*5,所以N!末尾的0的个数取决了2的个数和5的个数的最小值,有因为被2整除的数出现的概率大于5,因此5出现的次数就是N!末尾0的个数。因此,该问题就转换成为计算从1~N,每个数可以贡献5的个数,也就是每个数除以5的值。

上面的解法需要从1到N遍历每一个数,当然还有更加简便的方法。以26!为例,贡献5的数有5、10、15、20、25,一共贡献了6个5,可以理解为5的倍数5、10、15、20、25贡献了一个5,而25的倍数又贡献了一个5,得到下面的公式:

Z = [N/5] +[N/5^2] +[N/5^3] + …(总存在一个K,使得5^K > N,[N/5^K]=0)

代码实现

class Untitled { static int lowZeroN(int N) { int count = 0; while (N > 0) { N = N / 5; count = count + N; } return count; } public static void main(String[] args) { System.out.println("26!的十进制表示中末尾0的个数=" + lowZeroN(26)); } } 

运行结果

26!的十进制表示中末尾0的个数=6

8. 给定一个整数 N,求 N! 的二进制表示中最低位 1 的位置

解决思路 首先,让我们换一个角度考虑,其实这个问题就是求解二进制表示中从最低位开始0的个数,因为二进制最低位为0代表的是偶数,能够被2整除,所以质因数2的个数就是二进制表示中最低位1后面的0的个数。

因此,我们的实现这就和上面2.7中求解质因数5的个数是一样的。

代码实现

class Untitled { static int lowOneN(int N) { int count = 0; while (N > 0) { N = N >> 1; count = count + N; } return count+1; } public static void main(String[] args) { System.out.println("3!的二进制表示中最低位1的位置=" + lowOneN(3)); } } 

运行结果

3!的二进制表示中最低位1的位置=2

9. 最大公约数

解决思路 最大公约数 的定义为 两个或多个整数的共有约数中最大的一个。这里采用的是 更相止损法,其操作步骤为:

  • 第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。
  • 第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。

则第一步中约掉的若干个2与第二步中等数的乘积就是所求的最大公约数。

代码实现

class Untitled { static int gcd(int big, int small) { int fac = 1; int temp; while (small > 0) { //保证数字的先后顺序。 if (small > big) { temp = big; big = small; small = temp; } if ((big & 1) > 0) { //奇奇。 if ((small & 1) > 0) { temp = big; big = small; small = temp - small; //奇偶。 } else { small >>= 1; } } else { //偶奇。 if ((small & 1) > 0) { big >>= 1; //偶偶。 } else { small >>= 1; big >>= 1; fac *= 2; } } } return big * fac; } public static void main(String[] args) { int result = gcd(319, 377); System.out.println("result=" + result); } } 

运行结果

result=29

10. 精确地表达有限/无限循环小数

问题描述 有限小数或者无限循环小数都可以转化为分数,例如:

有限小数:0.9 = 9/10
无限循环小数:0.333(3)= 1/3

解决思路 在这里插入图片描述

代码实现

class Untitled { public static class Fraction { //分子。 public double numerator; //分母。 public double denominator; } public static double powerOf(int num, int mi) { double temp = 10; double result = 1; while (mi > 0) { if ((mi & 1) > 0) { result = result * temp; } temp = temp * temp; mi >>= 1; } return result; } //分为有限循环和无限循环两个部分,按照公式计算。 public static Fraction getDescription(int limit, int limitLen, int unLimit, int unLimitLen) { //分别计算对应长度的10的n/m次幂。 double limitPower = powerOf(10, limitLen); double unLimitPower = powerOf(10, unLimitLen); Fraction fraction = new Fraction(); //根据公式计算分子和分母即可。 fraction.numerator = limit * (unLimitPower - 1) + unLimit; fraction.denominator = (unLimitPower - 1) * limitPower; return fraction; } public static void main(String[] args) { Fraction f = getDescription(285714, 6, 285714, 6); System.out.println("res= " + f.numerator + "/" + f.denominator); } } 

运行结果

res= 2.85714E11/9.99999E11

关注
打赏
1655516835
查看更多评论
立即登录/注册

微信扫码登录

0.0934s