数学问题,围绕着数理逻辑的问题
- 进制转换
- GCD&LCM(最大公约束,最小公倍数)
- 质数&质数因子
- 快速幂
- 矩阵&矩阵快速幂
- 高精度整数
进制转换
- 10->N
- M->10
- M->N
例题6.1 二进制数
找到一个偷懒的办法。
10进制转化其他进制 | 对应的方法,参数:n(原10进制数据),r(进制), | 返回值 |
---|---|---|
10进制转2进制 | Integer.toBinaryString(n); | 一个二进制字符串. |
10进制转8进制 | Integer.toOctalString(n); | 一个八进制字符串 |
10进制转16进制 | Integer.toHexString(n); | 一个16进制字符串 |
10进制转 r 进制 | Integer.toString(100, 16); | 一个r进制字符串 |
radix进制的字符串s转10进制 | Integer.parseInt((String) s,(int) radix); | Int |
太偷懒了,不可以这样,万一是线上面试,抽题来问,那我不是凉凉?
所以说我们还是来简单计算一下。
static LinkedList Convert(int n){
LinkedList<Integer> LinkedList = new LinkedList<>();
while (n!=0){
LinkedList.addFirst(n%2);
n/=2;
}
return LinkedList;
}
如果这里采用的数字,就需要逆序输出。
10进制转换为2进制就需要,先%2取得余数,再/2。
比方说
这surface笔真的难用,复试完了就去找微软保修换了,不知道是机子得问题还是笔得问题。
注意得就是顺序问题啊,专门挑选了个不对称得二进制数来对比的。
如果是8进制,16进制只要把%2,/2换成%8,/8就好了,最后记得转换为String。
如果是转换为16进制,就记得把高于10的数字转换一下,10-16转换为A-F。
static char IntToChar(int target){
if(target<10){
return (char) ('0'+target);
}else {
return (char)(target-10+'A');
}
}
例题GCD & LCM
最大公约数,最小公倍数
上面就是暴力的线性查找。
辗转相除法(欧几里得算法)
原理就是:
a = g*l
b = g*m
a = b*k + r
g*l = g*m*k + r
r = g*(l-m*k)
这里可以得到a和b的最大公约数,同时也是r的最大公约数
这样可以从a,b的最大公约数问题,转换为b和r的最大公约数问题
依次递归就可以不断循环。
例题6.5
static int GCD(int a, int b){
if(a<b){
int temp = a;
a=b;
b=temp;
}
if(b==0){
return a;
}else {
return GCD(b,a%b);
}
}
记得a,b的位置哦。a一定大于b的,否则就要交换。
最小公倍数
可以通过上面的公式转换为求最大公倍数的
例题6.6 最小公倍数
emmm就上面的代码改一改就好啦。
例题6.7质数
对于穷举法来说,只需要遍历
n
\sqrt{n}
n即可。
下面的是约数。
所以这个方法的思路就是把数据量从O(n)的时间复杂度变成了O(
n
\sqrt{n}
n)的时间复杂度。
static boolean Judge(int n){
if(n<2){
return false;
}
int bound = (int)Math.sqrt(n)+1;
for (int i = 2; i <= bound; i++) {
if(n%i==0)return false;
}
return true;
}
那么这里我们将题目升级一下,
判断0-n内的所有质数,如果我们采用刚才的方法,那么一定会出问题的。时间复杂度会变成O(n* n \sqrt{n} n)的。
这里了解到了一个叫质数筛法的方法,我靠,和我以前自己想到的一个方法一样。每次遍历的时候,只需要遍历比当前数小的质数即可。
为什么?
假设我们求得数是91,那么如果遍历了2不是,就不用遍历4,6,8,等等2的倍数了,如果遍历了5不是,那么就不用遍历10,15,20等5的倍数了。依次推类,我们需要一个数据结构来存储这些质数元素。
也可以这样说,设计一个表,每次去除质数的倍数个的元素。即可。两种方法都可以。
我更喜欢我设计的那个方法。
static void JudgeArrays(int n){
ArrayList<Integer> arrayList= new ArrayList<Integer>();
arrayList.add(2);
for (int i = 3; i < n; i++) {
for (int j = 0; j < arrayList.size(); j++) {
if(i % arrayList.get(j)==0) break;
if(j==arrayList.size()-1) arrayList.add(i);
}
}
System.out.println(arrayList);
}
有兴趣的可以去了解一下
Miller Rabin算法
Miller Rabin算法的依据是费马小定理。这里就不作过多解释了。
该算法是基于概率来的,也就是俗称的猜。虽然不能100正确,但是如果能保证99%以上的正确率那也是不错的。
我设计那个数据量太大了,不好用。
更高效的质数查找方法
static boolean[] Judege(int num,ArrayList arrayList){
boolean isPrime[] = new boolean[num];
for (int i = 0; i < isPrime.length; i++) {
isPrime[i]=true;//将所有数预制为true
}
isPrime[0]=false;
isPrime[1]=false;
for (int i = 2; i < isPrime.length; i++) {
if(!isPrime[i]){//如果为假,就直接跳过本次及以后的数据
continue;
}
arrayList.add(i);
for (int j = i; j < isPrime.length; j+=i) {//遍历剩余的数据,将其置换为false
isPrime[j] = false;
}
}
return isPrime;
}
例题6.6
质因子分解
例题6.9
短除法
短除法一定是除的质数哦
由于这次数据是109的数据量,所以我们要考虑一下时间ok吗?
其实我们不需要这么多的数据,分析如下
第一种情况,所有质因素都小于
n
\sqrt{n}
n的,
第二种情况,有且仅有一个数大于等于
n
\sqrt{n}
n的,
但凡出现两个大于
n
\sqrt{n}
n的数,其乘积必定大于其n。
实际测试,用那n
n
\sqrt{n}
n 在数据量大于1000000的时候会很吃力,就算是1000000都等了很多秒。
刚才实验了一下,还是第一种更快,1000000级别基本上秒出,就算是再加2个00也是仅仅等了3秒多。效率更高。
static int NumberOfPrimeFactor(int num,ArrayList<Integer> arrayList){
int answer = 0;
for (int i = 0; i < arrayList.size(); i++) {
int factor = arrayList.get(i);
if(num<factor){
break;
}
int current = 0;//计数器
while (num % factor == 0){//满足目标条件
current++;
num /= factor;
}
answer+=current;
}
if(num>1){//如果最后一个数除不尽
answer++;
}
return answer;
}
记住我们这里是有重复值的。
约数的个数
例题6.7
这里思路都差不多,只需要增加或者修改属性参数标志符等等,就好了.
下面就是快速幂
这里需要额外拓展一个知识。
任何一个数n都可以被分为
n = 2i+2j+…+2k
因为任何一个数都可以用二进制的形式表达出来
然后我们就能在O(logn)内算出ab的内容
例题6.10
一般来说java有内置math.pow()函数,但是怕被问起来,所以说我们就写一个方法高效的实现
static int QuickPower(int x, int n) {
int answer = 1;
while (n != 0) {
if (n % 2 == 1) {
answer *= x;//计算前面的值
}
n /= 2;
x*=x;//升位
}
return answer;
}
这里其实是求n的二进制数,
所以说要注意的就是升位的x*=x;这里来说,我们是因为
右上角的次方数,是逐步递增2倍的,也就是a2倍的数据。所以每次位移之后都要把当前待乘的数升级。
矩阵&快速矩阵幂
矩阵这一部分来说,一般很简单
- 矩阵相加
- 矩阵相乘
- 矩阵转置
- 矩阵求幂
以上几个为常考
矩阵相加
蛮简单的
class Matrix{
int matrix[][];
int row;
int cow;
public Matrix(int row,int cow) {
this.row = row;
this.cow = cow;
matrix = new int[row][cow];
for (int i = 0; i < row; i++) {
Arrays.fill(matrix[i],3);
}
}
public Matrix ADD (Matrix M){
Matrix X = new Matrix(row,cow);
if(M.row!=this.row || M.cow!=this.cow)
X=null;
else {
for (int i = 0; i < this.row; i++) {
for (int j = 0; j < this.cow; j++) {
X.matrix[i][j]=M.matrix[i][j]+this.matrix[i][j];
}
}
}
return X;
}
}
也可以不用类,直接用数组进行,可以简化。
同理
除了加,还有乘。(减和除化为加和乘就好了)
乘法仅仅需要把x[i][j]*x[j][i]就好了。行×列
例题6.11
仅仅需要注意输入输出就好了。
转置也很简单。
求幂也不说了哈,理解为两个相同的矩阵进行乘即可,和上面原理一致。
高精度整数
int:2147483647
long long :9223372036854775807
如果更大会怎么样呢,我们建议在java用BigInteger和BigDecimal等等。
c和c++就需要更多的。