1.判断闰年
/*** 判断闰年,闰年的基本规则“四年一闰,百年不闰,四百年再闰”,也就是能被4整除并且不能被100整除,或者可以被400整除*/public static boolean leapYear(int year) {return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;}
2.多项式计算
2.1.一维多项式求值
/*** 多项式计算* 一维多项式* P(X)=aₙ₋₁Xⁿ⁻¹ + aₙ₋₂Xⁿ⁻² + … +a₁X+a₀* 例如:* P(X)=3X⁶+7X⁵-3X⁴+2X³+7X²-7X-15* 解法,将上面的公式按照x的幂从小到大进行,也就是倒序,结果如下* P(X)=-15-7X+7X²+2X³-3X⁴+7X⁵+3X⁶*/public static double polynomial(int a[], double x) {double result = 0;for (int i = 0; i < a.length; i++) {//数组a的长度,就是多项式的项数,这个例子为7项double temp = 1;//temp作为x的n次幂的结果for (int j = 0; j < i; j++) {temp *= x;}System.out.print(x + " 的 " + i + " 次方 = " + temp + " \t常量为 " + a[i]);result += (temp * a[i]);//数组a为每一项前面的那个常量,System.out.print("\t当前项 = " + temp * a[i]);System.out.println("\t本次result = " + result);}return result;}
调用
int b[] = {-15, -7, 7, 2, -3, 7, 3};double x = 2.0;double result = polynomial(b, x);System.out.println("多项式的结果:" + result);
输出:
2.0 的 0 次方 = 1.0 常量为 -15 当前项 = -15.0 本次result = -15.02.0 的 1 次方 = 2.0 常量为 -7 当前项 = -14.0 本次result = -29.02.0 的 2 次方 = 4.0 常量为 7 当前项 = 28.0 本次result = -1.02.0 的 3 次方 = 8.0 常量为 2 当前项 = 16.0 本次result = 15.02.0 的 4 次方 = 16.0 常量为 -3 当前项 = -48.0 本次result = -33.02.0 的 5 次方 = 32.0 常量为 7 当前项 = 224.0 本次result = 191.02.0 的 6 次方 = 64.0 常量为 3 当前项 = 192.0 本次result = 383.0多项式的结果:383.0
2.2.多项式乘法
/*** 多项式乘法* 多项式乘法就是将两个多项式进行相乘,最后得到一个新的多项式* A(x)=aₘ₋₁xᵐ⁻¹+aₘ₋₂xᵐ⁻²+…+a₁x+a₀* B(x)=bₙ₋₁xⁿ⁻¹+bₙ₋₂xⁿ⁻²+…+b₁x+b₀* 这两个多项式相乘的结果如下:* R(x) = A(x)B(x) = (aₘ₋₁xᵐ⁻¹+aₘ₋₂xᵐ⁻²+…+a₁x+a₀)*(bₙ₋₁xⁿ⁻¹+bₙ₋₂xⁿ⁻²+…+b₁x+b₀)* 求下面这2个多项式的乘积多项式* A(x)=2x⁵+3x⁴-x³+2x²+5x-4* B(x)=3x³+x²-2x-3* 解法将上面的两个倒序如下* A(x)=-4+5x+2x²-x³+3x⁴+2x⁵* B(x)=-3-2x+x²+3x³。* A(x)B(x) 也就是要让 A(x)的每一项和 B(x)的每一项相乘,然后相加*/public static double polynomialMultiplication(double a[],int m,double b[], int n,int x){//参数中m和n分别为多项式A(x)和多项式B(x)的项数,此题中,m=6,n=4, a[]、b[]分别为A(x)和B(x)每一项的系数double result = 0;double resultA = 0;//A的每一项的值for(int i = 0;i <m;i++){//代表从A的最后一项开始,也就是x⁰,一直到x⁵,//求A(x)中每一项的x的次幂double tempA=1;for(int j=0;j<i;j++){tempA *= x;}resultA = a[i] * tempA;//求B(x)的所有项double resultB = 0;//B的所有项的值for(int k=0;k<n;k++){//求B(x)中每一项的x的次幂double tempB=1;for(int j=0;j<k;j++){tempB *= x;}resultB += (b[k] * tempB);}result += resultA * resultB;System.out.print(x + " 的 " + i + " 次方 = " + tempA + " \t常量为 " + a[i]);System.out.print(" \t当前项 resultA= " + resultA);System.out.print(" \t当前项 resultB= " + resultB);System.out.println("\t本次result = " + result);}return result;}
调用:
double a[] = {-4, 5, 2, -1, 3, 2};double b[] = {-3, -2, 1, 3};int x=2;double resultPolynomialMultiplication = polynomialMultiplication(a,a.length,b,b.length,x);System.out.println("多项式的结果:" + resultPolynomialMultiplication);
输出:
2 的 0 次方 = 1.0 常量为 -4.0 当前项 resultA= -4.0 当前项 resultB= 21.0 本次result = -84.02 的 1 次方 = 2.0 常量为 5.0 当前项 resultA= 10.0 当前项 resultB= 21.0 本次result = 126.02 的 2 次方 = 4.0 常量为 2.0 当前项 resultA= 8.0 当前项 resultB= 21.0 本次result = 294.02 的 3 次方 = 8.0 常量为 -1.0 当前项 resultA= -8.0 当前项 resultB= 21.0 本次result = 126.02 的 4 次方 = 16.0 常量为 3.0 当前项 resultA= 48.0 当前项 resultB= 21.0 本次result = 1134.02 的 5 次方 = 32.0 常量为 2.0 当前项 resultA= 64.0 当前项 resultB= 21.0 本次result = 2478.0多项式的结果:2478.0
3.随机数生成算法
Java中三种随机数生成方法:
- 1 Math.random()方法,产生随机数是0~1之间的一个double,我们可以把它乘以一定的数,比如100,它就是100以内的随机;
- 2 java.util包里面提供的Random类。我们可以新建一个Random对象来产生随机数,它可以产生随机整数、float、double、long。
- 3.System类中有一个currentTimeMillis()方法,这个方法返回一个从1970年1月1日0点0分0秒到目前的一个毫秒数,返回类型是long,我们可以拿它作为一个随机数,拿它对一些数取模,就可以把它限制在一个范围之内。
3.1.均匀分布的随机数生成算法
/*** [0,1]之间均匀分布的随机数算法* 首先设定一个基数 base=256.0,以及两个常数 a=17.0和b=139.0* 这里base一般取值2的整数倍,常数a和b可以根据自己的经验来随意取。* 公式 rᵢ=mod(a*rᵢ₋₁+b,base),pᵢ=rᵢ/base;其中i=1,2,…而pᵢ便是得到的第i个随机数,rᵢ是随着递推而每次更新的。*/static double r = 5.0;public static double mod(){double result = 0;double base = 256.0,a = 17.0,b = 139.0;//定义基数和常数double temp1 = a*r+b;//计算总值double temp2 = (int)(temp1/base);//计算商double temp3 = temp1-temp2*base;//计算余数r=temp3;//更新随机种子,为下一次使用result = r/base;//随机数return result;}
调用:
for(int i=0;i<10;i++){System.out.print("r:" + r );double result= mod();System.out.println(" 第" + i + "个随机数是:" + result);}
输出:
r:5.0 第0个随机数是:0.875r:224.0 第1个随机数是:0.41796875r:107.0 第2个随机数是:0.6484375r:166.0 第3个随机数是:0.56640625r:145.0 第4个随机数是:0.171875r:44.0 第5个随机数是:0.46484375r:119.0 第6个随机数是:0.4453125r:114.0 第7个随机数是:0.11328125r:29.0 第8个随机数是:0.46875r:120.0 第9个随机数是:0.51171875
要获取0-100之间的正整数,只需要将上面的结果*100,然后取整即可。取100-200之间的正整数,再+100即可
3.2.正太分布的随机数生成算法
/*** 正太分布(Normal distribution)的随机数生成算法* 对于一个正太分布,描述该正太分布的参数包括均值μ和方差σ²。在数学上,一种近似的产生正太分布的算法如下:* ₙ₋₁* (∑ Rᵢ)-n/2* ⁱ⁼⁰* RZT = μ+σ ————————————* √(n/12)** 其中Rᵢ为[0,1]之间均匀分布的随机数。当n趋向无穷大的时候,得到的随机分布为正太分布* 在实际应用中,我们不可能取n为无穷大。一般来说,n只要足够大就可以了,* 为了计算方便,我们可以取n=12,这样,上公式中的分母中的根号便可以忽略。* @param u 均值μ* @param t 方差σ* @param r 随机种子,和上面的函数mod()中的r一样* @return*/public static double normalDistribution(double u, double t, double r){double total=0.0;double result;for(int i = 0; i < 12; i++){total += mod();// 调用了上面的函数获取[0,1]之间均匀分布的随机数,然后再根据求和公式求和,}result = u + t * (total-12/2);// 因为分母为1,直接忽略,计算分子部分的值,即为最终结果return result;}
调用:
static double r = 5.0;// 假设 均值μ=2.0 方差σ²=3.5²,求正太分布随机数double u = 2.0;double t = 3.5;for(int i=0;i<10;i++){double result = normalDistribution(u, t, r );// %10.5f,是将结果保留5位小数输出,规定输出的值占10个字符,如果不够10个字符,则将输出的值往右移,前面用空格补齐System.out.printf("正太分布随机数%d :%10.5f\n",i, result);}
输出:
正太分布随机数0 : 0.55078正太分布随机数1 : 1.20703正太分布随机数2 : 5.36328正太分布随机数3 : 6.01953正太分布随机数4 : -3.82422正太分布随机数5 : 0.33203正太分布随机数6 : 4.48828正太分布随机数7 : -1.85547正太分布随机数8 : 2.30078正太分布随机数9 : 6.45703
4.阶乘
代码参考 常用算法指南(一)基本算法思想 3.3 递归算法思想
5.计算π的近似值
5.1.割圆术求π
/*** 用割圆术求π* π=圆的周长/圆的直径* 假设圆的半径为1,在圆内作一个内接正多边形,正多边形的边越多,就越接近圆。(也就是将圆经过多次切割,切割为正多边形后进行计算)* 以正6边形为例,假设边长为y₁,圆周率π≈(6*y₁)/2=3*y₁,而y₁=1;* 将边增加到12,也就是正12边形,假设边长为y₂,圆周率π≈(12*y₂)/2=6*y₂,而y₂²=2-2*√(1-(y₁/2)²);(这个结果可以参考《Java常用算法手册》197页的图6-21得出)* 以此类推,当边为n时,正多边形的边长为yₙ²=2-2*√(1-(yₙ₋₁/2)²);* 假设n是6的倍数,我们可以得出下面的函数。* @param x 在员内画一个各个定点在圆边的正多边形,这个多边形的边的数量除以6=x,* @return PI 圆周率π*/public static double cyclotomic(int x){int i = 1;double a = 1;//边长,正6边形的边长为1int n = 6;//正多边形的边数,初始边数为6,随着x的递增不断翻倍//先求出正多边形的边长yₙwhile(i <= x){n *= 2;a = Math.sqrt(2 - 2 * Math.sqrt(1 - (a/2) * (a/2)) );//套用上面注释中y₂²的公式,求出y₂i++;}double PI;//πPI = n * a / 2 ;//%12.10f,是将结果保留10位小数输出,规定输出的值占12个字符,如果不够12个字符,则将输出的值往右移,前面用空格补齐System.out.printf("正 %d 边形的边长为 %12.10f ,圆周率π ≈ %12.10f\n", n, a, PI);return PI;}
调用:
for(int i = 0; i < 13; i++){cyclotomic(i);}
输出:
正 6 边形的边长为 1.0000000000 ,圆周率π ≈ 3.0000000000正 12 边形的边长为 0.5176380902 ,圆周率π ≈ 3.1058285412正 24 边形的边长为 0.2610523844 ,圆周率π ≈ 3.1326286133正 48 边形的边长为 0.1308062585 ,圆周率π ≈ 3.1393502030正 96 边形的边长为 0.0654381656 ,圆周率π ≈ 3.1410319509正 192 边形的边长为 0.0327234633 ,圆周率π ≈ 3.1414524723正 384 边形的边长为 0.0163622792 ,圆周率π ≈ 3.1415576079正 768 边形的边长为 0.0081812081 ,圆周率π ≈ 3.1415838921正 1536 边形的边长为 0.0040906126 ,圆周率π ≈ 3.1415904632正 3072 边形的边长为 0.0020453074 ,圆周率π ≈ 3.1415921060正 6144 边形的边长为 0.0010226538 ,圆周率π ≈ 3.1415925166正 12288 边形的边长为 0.0005113269 ,圆周率π ≈ 3.1415926186正 24576 边形的边长为 0.0002556635 ,圆周率π ≈ 3.1415926453
5.2.蒙特卡罗算法求π
代码参考 常用算法指南(一)基本算法思想 3.5 概率算法思想
5.3.级数公式算法求π
/*** 级数公式算法求π* 在微积分中,对一个表达式进行级数展开并取极便可以得到一些了的迭代计算公式。* 对于圆周率π也可以采用相同的方法得到级数公式。这样的级数公式很多,例如:* π 1 1 2 1 2 3 1 2 3 4* — = 1 + — + — * — + — * — * — + — * — * — * — + …* 2 3 3 5 3 5 7 3 5 7 9* 因此* 1 1 1 2 1 2 3 1 2 3 4* π = 2 *(— + — + — * — + — * — * — + — * — * — * — + …)* 1 3 3 5 3 5 7 3 5 7 9* 上式中,从第二项开始,每一项都是前面的基础上多乘了一个分数,该分数的分母增加2,分子增加1,可以采用递推算法来实现*/public static void seriesFormulaPI(){double PI = 2.0, temp = 2.0;//初始化PI(π) 和 第一项的值int n = 1, m = 3;//初始化分子n和分母m//指定精度1e-15是一个科学记数法的数字(小数的指数表示法),相当于1x10的-15次方(也就是1除以10的15次方)。while(temp > 1e-15){//每一项大于指定精度才累加到PI,只要有一项小于这个精度,就直接退出得到πtemp = temp * n / m;//从第二项开始,每一项等于前一项 * 一个新的分数(分子加1,分母加2)PI+=temp;n++;//分子+1m+=2;//分母+2}System.out.printf("圆周率π ≈ %17.15f\n", PI);}
调用:
seriesFormulaPI();
输出:
圆周率π ≈ 3.141592653589792
6.哥德巴赫猜想
/*** 哥德巴赫(Goldbach)猜想(conjecture)* 任何一个大于2的偶数都能表示为两个素数之和* 以200以内的整数为例*/public static void goldbachConjecture() {int newLine = 0;//换行标记for (int i = 4; i <= 200; i += 2) {//大于2的偶数,从4开始,每次递加2boolean hanSolution = false;for (int j = 2; j <= i / 2; j++) {int k = i - j;//i=j+kif (isPrimeNumber(j) && isPrimeNumber(k)) {//j和k都是素数System.out.printf("%3d = %2d + %3d \t", i, j, k);newLine++;if (newLine % 3 == 0) { //每3个换一行输出System.out.printf("\n");}hanSolution = true;break;//得到一组解后就直接跳出,看下一个偶数的和是不是两个素数}}if (!hanSolution) {//有一个偶数找不到两个素数之和的解System.out.printf("偶数%3d 找不到两个素数之和的解!哥德巴赫猜想是错误的! \n", i);break;}}}/*** 判断一个数是不是素数* 素数:又称为质数,在大于1的自然数中,除了1和它本身以外不再有其他因数。** @param number int* @return true是素数,false不是*/public static boolean isPrimeNumber(int number) {if (number <= 1) {//质数要大于1return false;}for (int i = 2; i < number; i++) {if (number % i == 0) {//能被1和它本身以外的其他数整除则不是素数return false;}}return true;}
调用:
goldbachConjecture();
输出:
4 = 2 + 2 6 = 3 + 3 8 = 3 + 510 = 3 + 7 12 = 5 + 7 14 = 3 + 1116 = 3 + 13 18 = 5 + 13 20 = 3 + 1722 = 3 + 19 24 = 5 + 19 26 = 3 + 2328 = 5 + 23 30 = 7 + 23 32 = 3 + 2934 = 3 + 31 36 = 5 + 31 38 = 7 + 3140 = 3 + 37 42 = 5 + 37 44 = 3 + 4146 = 3 + 43 48 = 5 + 43 50 = 3 + 4752 = 5 + 47 54 = 7 + 47 56 = 3 + 5358 = 5 + 53 60 = 7 + 53 62 = 3 + 5964 = 3 + 61 66 = 5 + 61 68 = 7 + 6170 = 3 + 67 72 = 5 + 67 74 = 3 + 7176 = 3 + 73 78 = 5 + 73 80 = 7 + 7382 = 3 + 79 84 = 5 + 79 86 = 3 + 8388 = 5 + 83 90 = 7 + 83 92 = 3 + 8994 = 5 + 89 96 = 7 + 89 98 = 19 + 79100 = 3 + 97 102 = 5 + 97 104 = 3 + 101106 = 3 + 103 108 = 5 + 103 110 = 3 + 107112 = 3 + 109 114 = 5 + 109 116 = 3 + 113118 = 5 + 113 120 = 7 + 113 122 = 13 + 109124 = 11 + 113 126 = 13 + 113 128 = 19 + 109130 = 3 + 127 132 = 5 + 127 134 = 3 + 131136 = 5 + 131 138 = 7 + 131 140 = 3 + 137142 = 3 + 139 144 = 5 + 139 146 = 7 + 139148 = 11 + 137 150 = 11 + 139 152 = 3 + 149154 = 3 + 151 156 = 5 + 151 158 = 7 + 151160 = 3 + 157 162 = 5 + 157 164 = 7 + 157166 = 3 + 163 168 = 5 + 163 170 = 3 + 167172 = 5 + 167 174 = 7 + 167 176 = 3 + 173178 = 5 + 173 180 = 7 + 173 182 = 3 + 179184 = 3 + 181 186 = 5 + 181 188 = 7 + 181190 = 11 + 179 192 = 11 + 181 194 = 3 + 191196 = 3 + 193 198 = 5 + 193 200 = 3 + 197
7. 分解质因数
/*** 分解质因数* 每个合数都可以写成几个质数相乘的形式。其中每个质数都是这个合数的因数,叫做这个合数的分解质因数。* 要分解质因数,首先合数不可以是质数,因为质数不能分解** @param number 合数*/public static void decompositionFactor(int number) {if (number <= 1 || isPrimeNumber(number)) {factor.add(number);//质数的质因数是它本身,不能分解return;}for (int i = 2; i <= number; i++) {if (number % i == 0 && isPrimeNumber(i)) {// System.out.printf("%d,", i);factor.add(i);//找到一个质因数number = number / i;//将商作为新的正整数寻找质因数decompositionFactor(number);return;}}}static List<Integer> factor = new ArrayList<>();
调用:
int number = 90;System.out.printf("%d分解质因数:%d = ", number, number);decompositionFactor(number);int j = 0;for (int i : factor) {j++;if (j == factor.size()) {System.out.printf("%d", i);} else {System.out.printf("%d * ", i);}}
输出:
90分解质因数:90 = 2 * 3 * 3 * 5
8. 蛇形打印
/*** 蛇形打印* 打印出蛇形图案* 例如:最外层为蛇尾,最中间为蛇头* →→→→→→→↓* ↑→→→→→↓↓* ↑↑→→→↓↓↓* ↑↑↑→→↓↓↓* ↑↑↑←←←↓↓* ↑↑←←←←←↓* ↑←←←←←←←*/public static void snakePrint() {int row = 0, col = 0;int length = 7;int value = 1;int[][] snake = new int[length][length];//初始化snake数组Direction lastDirection = Direction.Right;for (int c = 0; c < length * length; c++) {snake[row][col] = value;lastDirection = findDirection(lastDirection, row, col, snake);switch (lastDirection) {case Right:col++;break;case Down:row++;break;case Left:col--;break;case Up:row--;break;}value++;}//输出for (int i = 0; i < length; i++) {for (int j = 0; j < length; j++) {System.out.printf("%3d", snake[i][j]);}System.out.println();}}private static Direction findDirection(Direction lastDirection, int row, int col, int[][] snake) {int length = 7;Direction direction = lastDirection;switch (direction) {case Right:if (col == length - 1 || snake[row][col + 1] != 0) {//如果到最后边,开始往下direction = Direction.Down;}break;case Down:if (row == length - 1 || snake[row + 1][col] != 0) {//到最下边,开始往左direction = Direction.Left;}break;case Left:if (col == 0 || snake[row][col - 1] != 0) {//到了最左边,开始往上direction = Direction.Up;}break;case Up:if (snake[row - 1][col] != 0) {//到了最上边,开始往右direction = Direction.Right;}break;}return direction;}static enum Direction {Right, Down, Left, Up;}
调用:
snakePrint();
输出:
1 2 3 4 5 6 724 25 26 27 28 29 823 40 41 42 43 30 922 39 48 49 44 31 1021 38 47 46 45 32 1120 37 36 35 34 33 1219 18 17 16 15 14 13
数学问题的算法就这些
