leetcode(5):快速幂||大数幂||大数幂模运算

近期听到群友笔试题:

给定一个长度为n的int数组,请问可否随机组成2的n次方。如果可以输出组成的数,如果不可以输出-1

首先看到上面这个题,有4个想法:

  1. 反向思维:2的n次方的n=1,2,3…… n,循环暴力搜索
  2. 正向思维:按题目要求随机排列组合,检查是否是2的n次方,更暴力;
  3. 二进制:由于是2的n次方,可以考虑微操作,2的n次方的规律就是:二进制中只有一个1。具体想法还没有想通(后续待补充)

解法在第四部分

1.快速幂

不管是正向搜索还是反向搜索,很有可能涉及到的知识就是计算2的n次幂
将最近学习到的快速幂记下来: 快速幂的实现方法已经定型了,理解的话移步文章

int poww(int a, int b) {
    int ans = 1, base = a;
    while (b != 0) {
        if (b & 1 != 0)
            ans *= base;
            base *= base;
            b >>= 1;
    }
    return ans;
}

2.大数幂

学会了快速幂,就可以算基本的幂运算了;可是当a或者b很大时,上述快速幂算法就会越界,这就涉及到另一个问题:大数幂

大数幂的解决方法很多,但其根源只有一个:用数组/链表存储每位
链表存储是反向存储;数组正向就行,反向也可;

——————————————————————————————————————————————————————————

2.1 大数幂(基于乘法)

用的是vector数据结构;可以用数组,可以用链表都行
下面是vector版本:

//全局变量 ans
vector<int> ans; 
// 大数幂 的基础 大数乘法
void mul(int num){
    int temp_add = 0;
    int temp_mul = 0;
    for(int i = 0; i < ans.size(); i++){
        temp_mul = num * ans[i];
        ans[i] = (temp_mul + temp_add) % 10;
        temp_add = (temp_mul + temp_add) / 10;
    }
    while(temp_add){
        ans.push_back( temp_add % 10);
        temp_add /= 10;
    }
}

void init(int b) //初始化 如果b大于等于10,就需要分配一下数组
{
    while(b>0)
    {
        ans.push_back(b % 10); //倒序
        b /= 10;
    }
}

// 大数幂
vector<int> powd(int b, int e){
    init(b);
    for(int i = 1; i < e; i++) 
        mul(b);
    return ans;
}


int main()
{
	vector<int> a = powd(4444, 3);

    for (int i = a.size()-1; i >= 0 ;i--) //反向打印输出
        cout << a[i] << "";
    
    cout << endl;
    return 0;
}


2.2 大数幂(基于快速幂)

我初步想法是,快速幂可以和大数幂两者结合的。但是还没开始做。上面几个方法应付leetcode和找工作已经足够了。后面有需要再补充;

3.大数幂模运算

学会了大数幂运算,还可能会遇到大数幂的模运算(如:%65536)。此时按照大数幂的方法求解出幂结果,再进行取模运算也是可以的。
不过那样的话,就很麻烦了,需要数组每一位对65536取模;

———————————————————————————————
取模的性质
基本性质

(1)若p|(a-b),则a≡b (% p) 即 a%p = b % p。例如 11 ≡ 4 (% 7), 18 ≡ 4(% 7)   //  | 表示整除

(2)(a % p)=(b % p)意味a≡b (% p)

(3)对称性:a≡b (% p)等价于b≡a (% p)

(4)传递性:若a≡b (% p)且b≡c (% p) ,则a≡c (% p)   
运算规则

模运算与基本四则运算有些相似,但是除法例外。其规则如下:

(a + b) % p = (a % p + b % p) % p (1)

(a - b) % p = (a % p - b % p) % p (2)

(a * b) % p = (a % p * b % p) % p (3)

ab % p = ((a % p)b) % p (4)

结合率:

((a+b) % p + c) % p = (a + (b+c) % p) % p (5)

((ab) % p * c)% p = (a * (bc) % p) % p (6)

交换率: (a + b) % p = (b+a) % p (7)

(a * b) % p = (b * a) % p (8)

分配率: ((a +b)% p * c) % p = ((a * c) % p + (b * c) % p) % p (9)
  
重要定理

若a≡b (% p),则对于任意的c,都有(a + c) ≡ (b + c) (%p);(10)

若a≡b (% p),则对于任意的c,都有(a * c) ≡ (b * c) (%p);(11)

若a≡b (% p),c≡d (% p),则 (a + c) ≡ (b + d) (%p),(a - c) ≡ (b - d) (%p),   (a * c) ≡ (b * d) (%p),(a / c) ≡ (b / d) (%p); (12)

若a≡b (% p),则对于任意的c,都有ac≡ bc (%p); (13)

参考文章: https://blog.csdn.net/afvdxhq267883/article/details/101247388
———————————————————————————————

3.1 大数幂模(基础)

基础方法主要利用性质3
(a * b) % p = (a % p * b % p) % p

int Dsmod(int b,int num,int modnum){ //
    int tempb = b % modnum;
    for(int i = 1; i < num; i++)
        tempb = tempb * tempb % modnum;
    return tempb;
}

int main()
{
    int an = Dsmod(4, 3, 3);
    cout <<an<< endl;

   
    return 0;
}

3.2 大数幂模(高阶)

上面的情况在进行 取模相乘时,结果可能越界。
此时就需要额外的操作进行处理。一般情况沿用上述大数乘法的方略;
主要涉及到以下性质:

(a + b) % p = (a % p + b % p) % p (1)   
(a - b) % p = (a % p - b % p) % p (2)   
(a * b) % p = (a % p * b % p) % p (3)

4.可否组成2的n次幂

4.1 反向思维

就应用之前提到的反向思维。
大概实现思路:

  1. 从1次方到n次方循环,存储2的n次方长度与输入数组长度相同的n值序列
  2. 检查数组中的序列与2的n次方序列是否完全一样;
#include <iostream>
#include <vector>
#include <sstream>
#include <string>

using namespace std;

 
vector<int> ans; 
// 大数幂 的基础 大数乘法
void mul(int num){
    int temp_add = 0;
    int temp_mul = 0;
    for(int i = 0; i < ans.size(); i++){
        temp_mul = num * ans[i];
        ans[i] = (temp_mul + temp_add) % 10;
        temp_add = (temp_mul + temp_add) / 10;
    }
    while(temp_add){
        ans.push_back( temp_add % 10);
        temp_add /= 10;
    }
}

void init(int b)
{
    ans.clear();
    while(b>0)
    {
        ans.push_back(b % 10); //倒序
        b /= 10;
    }
}

vector<int> powd(int b, int e){
    init(b);
    for(int i = 1; i < e; i++) 
        mul(b);
    return ans;
}


vector<int> allo(int b){
    vector<int> res;
    while(b>0)
    {
        res.push_back(b % 10); //倒序
        b /= 10;
    }
    return res;
}
//检查是否是相等
bool check(vector<int> a,vector<int> b)
{
    //可以用堆栈来检查
    for (vector<int>::iterator iter = b.begin(); iter != b.end();) //itereate
        {
            for (vector<int>::iterator iter2 = a.begin(); iter2 != a.end();)
            {
            if (*iter == *iter2)
            {
                a.erase(iter2);
                b.erase(iter);
                iter = b.begin();
                iter2 = a.begin();
                break;
            }else
            {
                iter2++;
            }
            }
            
        }
    if(a.size()==0)
        return true;
    else
        return false;
}

void outpur(vector<int> inv)
{
    for (vector<int>::iterator iter = inv.begin(); iter != inv.end();iter++) //应该反向打印
    {
        cout << *iter << "-";
    }
    cout << endl;
}
int main()
{

    int inpu;//有范围
    //空格分隔的大数
    // string line;
    // vector<int> inpu;
    // getline(cin, line);
    // stringstream ss(line);
    // while(ss)
    // {
    //     int temp;
    //     ss >> temp;
    //     inpu.push_back(temp);
    // }
    cin >> inpu;
    vector<int> inve = allo(inpu);
    vector<int> a2n;
    vector<int> n;
    for (int i = 1;;)//1开始 
    {
        //可能会涉及到大数
        ans.clear();
        a2n = powd(2, i);
        if(a2n.size()<inve.size())
            i++;
        else if (a2n.size()==inve.size())
        {
            if(check(a2n,inve))
            {
                outpur(inve);
                n.push_back(i);
            }
            
            i++;
        }else
        {
            break;
        }
    }
    //
    if(n.size() == 0)
    {
        cout << -1 << endl; //没有复合个数的n
    }
    system("pause");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/CoomCon/article/details/107537054