精华内容
下载资源
问答
  • C语言数组专题知识点总结

    千次阅读 2018-12-22 16:48:20
    知识点总结 1.一维数组的定义和初始化. 注: ① C语言数组的下标都是从0开始的; ②在定义数组时不能使用变量定义数组的大小,如果前面没有对n进行宏定义 #define n 5 则a[n]则不对,因为n不是一个确定的数; ③在对数...
               C语言数组专题知识点易错点总结及做题感悟
    

    一.知识点总结
    1.一维数组的定义和初始化.
    注: ① C语言数组的下标都是从0开始的;
    ②在定义数组时不能使用变量定义数组的大小,如果前面没有对n进行宏定义 #define n 5
    则a[n]则不对,因为n不是一个确定的数;
    ③在对数组定义时,不要让数组下标越界访问
    2.二维数组的定义和初始化
    注:①定义: 类型 数组名[第一维度][第二维度]
    ②初始化: 例 int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}
    3.排序和查找
    例如:按交换法将数组a的元素值按从高到低排序(冒泡排序)
    for(int i=1;i<=n-1;i++)
    {
    for(int j=1;j<=n-i;j++)
    {
    swap(a[j],a[j+1])
    }
    }
    例如:按折半查找法查找值为x的数组元素,若找到则返回x在数组中的下标位置,否则返回-1(二分查找)
    int binsearch (long num[],long x,int n) (binsearch()为函数定义)
    {
    int low=0;high=n-1;mid;
    while(low<=high)
    {
    mid=(high+low)/2;
    if(x>num[mid])
    low=mid+1;
    else if(x<num[mid])
    high=mid-1;
    else return mid;
    }
    return -1;
    }
    二.数组方面题型总结及做题感悟
    1.一维数组
    ①例如:与指定数字m相同的数的个数,一共有n个数
    for(int i=1;i<=n;++i)
    scanf(“%d”,&a[i]); //数组的输入格式
    scanf(“%d”,&m);
    for(int i=1;i<=n;++i)
    if(a[i]m) ++k;
    printf(“%d”,k);
    ②利用标记法解决问题 例如:校门外的树对需要移走的树进行标记; 开关灯中对每次处理的灯进行标记; 查找特定的值并输出第一次出现的位置
    开关灯问题:有n盏灯,有m个人,第一个人(1号)将灯全部关闭,第二个人(2号)将编号为2的倍数的灯打开,第三个人(3号)将3的倍数的灯做相反处理(将打开的灯关闭,将关闭的灯打开)。以后的人和3号一样,将凡是自己编号倍数的灯做相反处理。问当第m个人操作之后,哪几盏灯是关闭的,按从小到大输出编号,其间用逗号间隔
    int main()
    {
    int n,m,z=1;
    cin>>n>>m;
    memset(a,0,sizeof(a)); //初始化全部关闭
    for(int i=1;i<=m;++i)
    for(j=1;j<=n;++j)
    if(j%i
    0) a[j]=!a[j]; //编号是i的倍数的灯做相反处理
    for(int i=1;i<=n;++i)
    {
    if(a[i]) //是关闭的灯a[i]==1
    {
    if(z) z=0;
    else printf(","); //第一次不用先输出逗号
    printf("%d",i); //顺次输出关闭的灯的编号
    }
    }
    2.二维数组
    ①有些问题需要找出规律再用代码表现出来,例如:矩阵转置,图像旋转,图像模糊处理
    3.字符类型和字符数组
    ①统计数字字符个数
    char ch[256];
    gets(ch); //用gets()读入字符数组
    l=strlen(ch); //计算字符串的长度,需调用cstring库
    4.字符串与string类
    ①输入字符串 getline(cin,字符串变量) getline函数默认是碰到换行符才结束,可以读进空格
    ②判断一个字母为大写(或小写)字母,可用逻辑表达式"s[i]>='A&&s[i]<=‘Z’" (像单词替换的问题)
    ③string类型的其他常用操作
    s.insert(pos,s2) 在s下标为pos的元素前插入string类型s2
    s.substr(pos,n) 返回从s下标pos起的n个字符,类型为string
    s.erase(pos,n) 删除s下标pos起的n个字符
    s.replace(pos,n,s2) 将s下标pos起的n个字符替换为s2的字符
    s.find(s2,pos) 在s下标pos起查找s2第一次出现的位置
    ④sscanf和sprintf语句(将字符串与数字巧妙转换)
    sscanf(s,"%d",&N); 将字符串s转换成数字N
    sprintf(s,"%d",N); 将整数N转换成字符串s
    例如:char s1[10]=“1,2,3”,s2=“4,5,6”;
    int a,b;
    sscanf(s1,"%d",&a);
    sscanf(s2,"%d",&b);
    cout<<a+b<<endl;
    return 0;
    ⑤string 中的字典序(应用于按字典序输出的问题)
    比较运算符 if s1>s2 cout<<s1;
    compare()函数 if(a.compare(b)<0) 即s1<s2
    if(a.compare(b)>0) 即s1>s2
    if(a.compare(b)=0) 即s1=s2
    sort函数从小到大排序
    #include
    for(int i=0;i<n;++i)
    cin>>a[i];
    sort a[a,a+n];
    总结:数组这一章的知识可以说为我们提供了一种解决问题的新思路,尤其是字符数组和对字符串的操作更要认真把握,为后面的函数及其他算法打下基础。

    展开全文
  • 对数时间复杂度 – 折半查找(二分查找) – 线性时间复杂度 – 顺序查找 / 桶排序 – 对数线性时间复杂度 – 高级排序算法(归并排序、快速排序) – 平方时间复杂度 – 简单排序算法(选择排序、插入排序、冒泡...
  • 机器学习知识点总结

    2018-10-12 17:58:21
    在一个n维的空间中, 最好的检测outlier(离群)的方法是: 马氏距离 对数几率回归(logistics regression)和一般回归分析有什么区别?: 对数几率回归是设计用来预测事件可能性的 对数几率回归可以用来度量...
    • 在一个n维的空间中, 最好的检测outlier(离群点)的方法是:
    • 马氏距离
    • 对数几率回归(logistics regression)和一般回归分析有什么区别?:
    • 对数几率回归是设计用来预测事件可能性的
    • 对数几率回归可以用来度量模型拟合程度
    • 对数几率回归可以用来估计回归系数
    • bootstrap数据是什么意思?(提示:考“bootstrap”和“boosting”区别):
    •  有放回地从总共N个样本中抽样n个样本
    • “过拟合”只在监督学习中出现,在非监督学习中,没有“过拟合”,这是:

    错的。我们可以评估无监督学习方法通过无监督学习的指标,如:我们可以评估聚类模型通过调整兰德系数(adjusted rand score

    • 对于k折交叉验证, 以下对k的说法正确的是 : 
    • k越大, 不一定越好, 选择大的k会加大评估时间
    • 选择更大的k, 就会有更小的bias (因为训练集更加接近总数据集)
    • 在选择k时, 要最小化数据集之间的方差 
    • 我们可以先去除一个共线性变量
    • 计算VIF(方差膨胀因子), 采取相应措施
    • 为了避免损失信息, 我们可以使用一些正则化方法, 比如, 岭回归和lasso回归. 
    • 解决多重公线性, 可以使用相关矩阵去去除相关性高于75%的变量 (有主观成分). 也可以VIF, 如果VIF值<=4说明相关性不是很高, VIF值>=10说明相关性较高.
    • 我们也可以用 岭回归和lasso回归的带有惩罚正则项的方法. 我们也可以在一些变量上加随机噪声, 使得变量之间变得不同, 但是这个方法要小心使用, 可能会影响预测效果.
    • 如果SVM模型欠拟合, 以下方法哪些可以改进模型 :  
    • 增大惩罚参数C的值 
    • 假如我们使用非线性可分的SVM目标函数作为最优化对象, 我们怎么保证模型线性可分 :  
    • 设C=无穷大,C无穷大保证了所有的线性不可分都是可以忍受的.
    • 我们想要减少数据集中的特征数, 即降维. 选择以下适合的方案 : 
    • 使用前向特征选择方法
    • 使用后向特征排除方法
    • 我们先把所有特征都使用, 去训练一个模型, 得到测试集上的表现. 然后我们去掉一个特征, 再去训练, 用交叉验证看看测试集上的表现. 如果表现比原来还要好, 我们可以去除这个特征.
    • 查看相关性表, 去除相关性最高的一些特征  
    • 如何进行特征选择?

    •  特征选择是一个重要的数据预处理过程,主要有两个原因,首先在现实任务中我们会遇到维数灾难的问题(样本密度非常稀疏),若能从中选择一部分特征,那么这个问题能大大缓解。另外就是去除相关特征会降低学习任务的难度,增加模型的泛化能力。冗余特征指该特征包含的信息可以从其他特征中推演出来,但是这并不代表该冗余特征一定没有作用,例如在欠拟合的情况下也可以用过加入冗余特征,增加简单模型的复杂度。
    • 在理论上如果没有任何领域知识作为先验假设那么只能遍历所有可能的子集。但是这显然是不可能的,因为需要遍历的数量是组合爆炸的。一般我们分为子集搜索和子集评价两个过程,子集搜索一般采用贪心算法,每一轮从候选特征中添加或者删除,分别成为前向和后先搜索。或者两者结合的双向搜索。子集评价一般采用信息增益,对于连续数据往往排序之后选择中点作为分割点。
    • 常见的特征选择方式有过滤式,包裹式和嵌入式,filter,wrapper和embedding。Filter类型先对数据集进行特征选择,再训练学习器。Wrapper直接把最终学习器的性能作为特征子集的评价准则,一般通过不断候选子集,然后利用cross-validation过程更新候选特征,通常计算量比较大。嵌入式特征选择将特征选择过程和训练过程融为了一体,在训练过程中自动进行了特征选择,例如L1正则化更易于获得稀疏解,而L2正则化更不容易过拟合。L1正则化可以通过PGD, 近端梯度下降进行求解。
    •  特征比数据还大 选择什么分类器

    • 如果训练集很小,那么高偏差/低方差分类器(如朴素贝叶斯分类器)要优于低偏差/高方差分类器(如k近邻分类器),因为后者容易过拟合。然而,随着训练集的增大,低偏差/高方差分类器将开始胜出(它们具有较低的渐近误差),因为高偏差分类器不足以提供准确的模型。你也可以认为这是生成模型与判别模型的区别。
    • 为什么一些机器学习模型需要对数据进行归一化? 

    • 归一化后加快了梯度下降求最优解的速度。等高线变得显得圆滑,在梯度下降进行求解时能较快的收敛。如果不做归一化,梯度下降过程容易走之字,很难收敛甚至不能收敛
    • 把有量纲表达式变为无量纲表达式, 有可能提高精度。一些分类器需要计算样本之间的距离(如欧氏距离),例如KNN。如果一个特征值域范围非常大,那么距离计算就主要取决于这个特征,从而与实际情况相悖(比如这时实际情况是值域范围小的特征更重要)
    • 逻辑回归等模型先验假设数据服从正态分布。
    • 归一化的类型有线性归一化、标准差归一化、非线性归一化 
    • 特征向量的缺失值处理 

    • 缺失值较多.直接将该特征舍弃掉,否则可能反倒会带入较大的noise,对结果造成不良影响。
    • 缺失值较少,其余的特征缺失值都在10%以内,我们可以采取很多的方式来处理: 
    1. 把NaN直接作为一个特征,假设用0表示;
    2. 用均值填充;
    3. 用随机森林等算法预测填充
    • 决策树的停止条件 

    • 直到每个叶子节点都只有一种类型的记录时停止,(这种方式很容易过拟合)
    • 另一种时当叶子节点的记录树小于一定的阈值或者节点的信息增益小于一定的阈值时停止 
    • 神经网络有哪些优化算法? 

    • 解决优化问题,有很多算法(最常见的就是梯度下降),这些算法也可以用于优化神经网络。每个深度学习库中,都包含了大量的优化算法,用于优化学习速率,让网络用最快的训练次数达到最优,还能防止过拟合。
    1. SGD:随机梯度下降
    2. SGD+Momentum: 基于动量的SGD(在SGD基础上做过优化)
    3. SGD+Nesterov+Momentum:基于动量,两步更新的SGD(在SGD+Momentum基础上做过优化)
    4. Adagrad:自适应地为各个参数分配不同学习速率
    5. Adadelta: 针对Adagrad问题,优化过的算法(在Adagrad基础上做过优化)
    6. RMSprop:对于循环神经网络(RNNs)是最好的优化器(在Adadelta基础上做过优化)
    7. Adam:对每个权值都计算自适应的学习速率(在RMSprop基础上做过优化)
    8. Adamax:针对Adam做过优化的算法(在Adam基础上做过优化)
    •  Dropout 怎么做,有什么用处,解释

    •  可以通过阻止某些特征的协同作用来缓解。在每次训练的时候,每个神经元有百分之50的几率被移除,这样可以让一个神经元的出现不应该依赖于另外一个神经元。另外,我们可以把dropout理解为 模型平均。假设我们要实现一个图片分类任务,我们设计出了100000个网络,这100000个网络,我们可以设计得各不相同,然后我们对这100000个网络进行训练,训练完后我们采用平均的方法,进行预测,这样肯定可以提高网络的泛化能力,或者说可以防止过拟合,因为这100000个网络,它们各不相同,可以提高网络的稳定性。而所谓的dropout我们可以这么理解,这n个网络,它们权值共享,并且具有相同的网络层数(这样可以大大减小计算量)。我们每次dropout后,网络模型都可以看成是整个网络的子网络。(需要注意的是如果采用dropout,训练时间大大延长,但是对测试阶段没影响)。
    • Dropout说的简单一点就是我们让在前向传导的时候,让某个神经元的激活值以一定的概率p,让其停止工作
    • CNN池化层的作用?

    • 不变性,旋转位移不变性,更关注是否存在某些特征而不是特征具体的位置。可以看作加了一个很强的先验,让学到的特征要能容忍一些的变化。
    • 减小下一层输入大小,减小计算量和参数个数。
    • 获得定长输出。(文本分类的时候输入是不定长的,可以通过池化获得定长输出)
    • 防止过拟合或有可能会带来欠拟合。保留主要的特征同时减少参数(降维,效果类似PCA)和计算量,防止过拟合,提高模型泛化能力 
    • (红黑树)红黑树是什么?有什么性质?红黑树与AVL树的区别?

    • 红黑树(Red Black Tree) 是一种自平衡二叉查找树。红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
    • 它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(logn)时间内做查找,插入和删除,这里的n 是树中元素的数目。红黑树的统计性能比较好,红黑树是牺牲了严格的高度平衡的优越条件为代价红黑树能够以O(log2 n)的时间复杂度进行搜索、插入、删除操作。此外,由于它的设计,任何不平衡都会在三次旋转之内解决。

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • MySQL 索引知识点总结

    千次阅读 2020-12-14 17:56:51
    作者:fanili,腾讯 WXG 后台开发工程师知其然知其所以然!本文介绍索引的数据结构、查找算法、常见的索引概念和索引失效场景。什么是索引?在关系数据库中,索引是一种单独的、物理的对数...

    作者:fanili,腾讯 WXG 后台开发工程师

    知其然知其所以然!本文介绍索引的数据结构、查找算法、常见的索引概念和索引失效场景。

    什么是索引?

    在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。(百度百科)

    索引的目的是提高查找效率,对数据表的值集合进行了排序,并按照一定数据结构进行了存储。

    本文将从一个案例开始,从索引的数据结构、分类、关键概念及如何使用索引提高查找效率等方面对索引知识进行总结。

    从一个案例开始

    现象

    业务中有个既存的历史 SQL 语句在运行时会导致 DB 服务器过载,进而导致相关服务阻塞无法及时完成。CPU 监控曲线如下:

    图1-优化前的CPU使用率

    从 DB 的 CPU 使用率曲线可以看到业务运行一直处于“亚健康”状态(1),随着业务的增长随时都可能出现问题。这种问题(2)在 11 月 11 日凌晨出现,当时 DB CPU 一直处于 100%高负荷状态,且存在大量的慢查询语句。最终以杀死进程降低 DB 负载、减少业务进程(3)的方式恢复业务。

    在 11 月 11 日下午,对该业务的 SQL 语句进行了优化,优化的效果如下。业务运行时的 CPU 使用率峰值有很大的降低(对比图 2 的 1,2,3 可见);慢查询语句几乎在监控曲线上也无法明显观察到(对比图 3 的 1,2,3 可见)。

    图2-优化前后的CPU使用率
    图3-优化前后的慢查询数量

    分析

    表结构

    CREATE TABLE T_Mch******Stat (`FStatDate` int unsigned NOT NULL DEFAULT 19700101 COMMENT '统计日期',
    `FMerchantId` bigint unsigned NOT NULL DEFAULT 0 COMMENT '商户ID',
    `FVersion` int unsigned NOT NULL DEFAULT 0 COMMENT '数据版本号',
    `FBatch` bigint unsigned NOT NULL DEFAULT 0 COMMENT '统计批次',
    `FTradeAmount` bigint NOT NULL DEFAULT 0 COMMENT '交易金额'
    PRIMARY KEY (`FStatDate`,`FMerchantId`,`FVersion`),
    INDEX i_FStatDate_FVersion (`FStatDate`,`FVersion`))
    DEFAULT CHARSET = utf8 ENGINE = InnoDB;
    

    从建表语句可以知道该表有两个索引:

    1. 主键索引,是一个组合索引,由字段 FStateDate、FMerchantId 和 FVersion 组成;

    2. 普通索引,是一个组合索引,由字段 FStateDate 和 FVersion 组成;

    优化前的 SQL 语句(做了部分裁剪)A:

    SELECT SQL_CALC_FOUND_ROWS FStatDate,
        FMerchantId,
        FVersion,
        FBatch,
        FTradeAmount,
        FTradeCount
    FROM T_Mch******Stat_1020
    WHERE FStatDate = 20201020
        AND FVersion = 0
        AND FMerchantId > 0
    ORDER BY FMerchantId ASC LIMIT 0, 8000
    

    对该 SQL 进行 explain 得到如下结果,Extra 字段的值为 using where,说明并没有使用到索引。

    优化后的 SQL 语句(做了部分裁剪)B:

    SELECT SQL_CALC_FOUND_ROWS a1.FStatDate,
        a1.FMerchantId,
        a1.FVersion,
        FBatch,
        FTradeAmount,
        FTradeCount
    FROM T_Mch******Stat_1020 a1, (
        SELECT FStatDate, FMerchantId, FVersion
        FROM T_Mch******Stat_1020
        WHERE FStatDate = 20201020
            AND FVersion = 0
            AND FMerchantId > 0
            ORDER BY FMerchantId ASC LIMIT 0, 8000 ) a2
    where a1.FStatDate = a2.FStatDate
        and a1.FVersion = a2.FVersion
        and a1.FMerchantId = a2.FMerchantId;
    

    优化关键步骤为:

    • 新增一个子查询,select 字段只有主键字段;

    该 SQL 的 explain 结果如下,子查询语句使用了索引,而最终在线上运行结果也证明了优化效果显著。

    疑问

    优化后的 SQL 语句 B 比原来的 SQL 语句 A 复杂的多(子查询,临时表关联等),怎么效率会提升,违反直觉?有三个疑问:

    1. SQL 语句 A 的查询条件字段都在主键中,主键索引用到了没?

    2. SQL 语句 B 的子查询为什么能够用到索引?

    3. 前后两条语句执行流程的差异是什么?

    索引的数据结构

    在 MySQL 中,索引是在存储引擎层实现的,而不同的存储引擎根据其业务场景特点会有不同的实现方式。这里会先介绍我们常见的有序数组、Hash 和搜索树,最后看下 Innodb 的引擎支持的 B+树。

    有序数组

    数组是在任何一本数据结构和算法的书籍都会介绍到的一种重要的数据结构。有序数组如其字面意思,以 Key 的递增顺序保存数据在数组中。非常适合等值查询和范围查询。

    ID:1ID:2......ID:N
    name2name2......nameN

    在 ID 值没有重复的情况下,上述数组按照 ID 的递增顺序进行保存。这个时候如果需要查询特定 ID 值的 name,用二分法就可以快速得到,时间复杂度是 O(logn)。

    // 二分查找递归实现方式
    int binary_search(const int arr[], int start, int end, int key)
    {
        if (start > end)
            return -1;
    
        int mid = start + (end - start) / 2;
        if (arr[mid] > key)
            return binary_search(arr, start, mid - 1, key);
        else if (arr[mid] < key)
            return binary_search(arr, mid + 1, end, key);
        else
            return mid;
    }
    

    有序数组的优点很明显,同样其缺点也很明显。其只适合静态数据,如遇到有数据新增插入,则就会需要数据移动(新申请空间、拷贝数据和释放空间等动作),这将非常消耗资源。

    Hash

    哈希表是一种以键-值(K-V)存储数据的结构,我们只需要输入键 K,就可以找到对应的值 V。哈希的思路是用特定的哈希函数将 K 换算到数组中的位置,然后将值 V 放到数组的这个位置。如果遇到不同的 K 计算出相同的位置,则在这个位置拉出一个链表依次存放。哈希表适用于等值查询的场景,对应范围查询则无能为力。

    二叉搜索树

    二叉搜索树,也称为二叉查找树、有序二叉树或排序二叉树,是指一颗空树或者具有以下性质的二叉树:

    1. 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;

    2. 若任意节点的右子树不空,则右子树上所有节点的值均大于或等于它的根节点的值;

    3. 任意节点的左、右子树也分别为二叉查找树;

    二叉搜索树相比于其它数据结构的优势在于查找、插入的时间复杂度较低,为 O(logn)。为了维持 O(logn)的查询复杂度,需要保持这棵树是平衡二叉树。

    二叉搜索树的查找算法:

    1. 若 b 是空树,则搜索失败,否则:

    2. 若 x 等于 b 的根节点的值,则查找成功;否则:

    3. 若 x 小于 b 的根节点的值,则搜索左子树;否则:

    4. 查找右子树。

    相对于有序数组和 Hash,二叉搜索树在查找和插入两端的表现都非常不错。后续基于此不断的优化,发展出 N 叉树等。

    B+树

    Innodb 存储引擎支持 B+树索引、全文索引和哈希索引。其中 Innodb 存储引擎支持的哈希索引是自适应的,Innodb 存储引擎会根据表的使用情况自动为表生成哈希索引,不能人为干预。B+树索引是关系型数据库中最常见的一种索引,也将是本文的主角。

    数据结构

    在前文简单介绍了有序数组和二叉搜索树,对二分查找法和二叉树有了基本了解。B+树的定义相对复杂,在理解索引工作机制上无须深入、只需理解数据组织形式和查找算法即可。我们可以简单的认为 B+树是一种 N 叉树和有序数组的结合体。

    例如:

    B+树的 3 个优点:

    1. 层级更低,IO 次数更少

    2. 每次都需要查询到叶子节点,查询性能稳定

    3. 叶子节点形成有序链表,范围查询方便

    操作算法

    • 查找

    由根节点自顶向下遍历树,根据分离值在要查找的一边的指针;在节点内使用二分查找来确定位置。

    • 插入

    • 删除

    注:插入和删除两个表格内容来自《MySQL 技术内幕-InnoDB 存储引擎》

    填充因子(innodb_fill_factor):索引构建期间填充的每个 B-tree 页面上的空间百分比,其余空间保留给未来索引增长。从插入和删除操作中可以看到填充因子的值会影响到数据页的 split 和 merge 的频率。将值设置小些,可以减少 split 和 merge 的频率,但是索引相对会占用更多的磁盘空间;反之,则会增加 split 和 merge 的频率,但是可以减少占用磁盘空间。Innodb 对于聚集索引默认会预留 1/16 的空间保证后续的插入和升级索引。

    Innodb B+树索引

    前文介绍了索引的基本数据结构,现在开始我们从 Innodb 的角度了解如何使用 B+树构建索引,索引如何工作和如何使用索引提升查找效率。

    聚集索引和非聚集索引

    数据库中的 B+树索引可以分为聚集索引和非聚集索引。聚集索引和非聚集索引的不同点在于叶子节点是否是完整行数据。

    Innodb 存储引擎表是索引组织表,即表中的数据按照主键顺序存放。聚集索引就是按照每张表的主键构造一棵 B+树,叶子节点存放的是表的完整行记录。非聚集索引的叶子节点不包含行记录的全部数据。Innodb 存储引擎的非聚集索引的叶子节点的内容为主键索引值。

    若数据表没有主键聚集索引是怎么建立的?在没有主键时 Innodb 会给数据表的每条记录生成一个 6 个字节长度的 RowId 字段,会以此建立聚集索引。

    Select 语句查找记录的过程

    下面例子将展示索引数据的组织形式及 Select 语句查询数据的过程。

    • 建表语句:

    create table T (
        ID int primary key,
        k int NOT NULL DEFAULT 0,
        s varchar(16) NOT NULL DEFAULT '',
        index k(k)
    ) engine=InnoDB DEFAULT CHARSET=utf8;
    
    insert into T values(100, 1, 'aa'),(200, 2, 'bb'),(300, 3, 'cc'),(500, 5, 'ee'),(600,6,'ff'),(700,7,'gg');
    
    • 索引结构示意

    左边是以主键 ID 建立起的聚集索引,其叶子节点存储了完整的表记录信息;右边是以普通字段 K 建立的普通索引,其叶子节点的值是主键 ID。

    • Select 语句执行过程

    select * from T where k between 3 and 5;
    

    执行流程如下:

    1. 在 K 索引树上找到 k=3 的记录,取得 ID=300;

    2. 再到 ID 索引树上查找 ID=300 对应的 R3;

    3. 在 k 索引树取下一个值 k=5,取得 ID=500;

    4. 再回到 ID 索引树查到 ID=500 对应的 R4;

    5. 在 k 索引树取下一个值 k=6,不满足条件,循环结束。

    上述查找记录的过程中引入了一个重要的概念:回表,即回到主键索引树搜索的过程。避免回表操作是提升 SQL 查询效率的常规思路及重要方法。那么如何避免回表?

    注:该例子来自《MySQL 实战 45 讲》

    覆盖索引

    MySQL 5.7,建表语句:

    CREATE TABLE `employees` (
      `emp_no` int(11) NOT NULL,
      `birth_date` date NOT NULL,
      `first_name` varchar(14) NOT NULL,
      `last_name` varchar(16) NOT NULL,
      `gender` enum('M','F') NOT NULL,
      `hire_date` date NOT NULL,
      PRIMARY KEY (`emp_no`),
      KEY `i_first_name` (`first_name`),
      KEY `i_hire_date` (`hire_date`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    • SQL 语句 A

    explain select * from employees where hire_date > '1990-01-14';
    

    explain 结果:

    • SQL 语句 B

    explain select emp_no from employees where hire_date > '1990-01-14';
    

    explain 结果:

    • 分析

    从前后两次 explain 的结果可以看到 SQL 语句 A 的 extra 为 using where,SQL 语句 B 的 extra 为 using where;using index。这说明 A 没有使用索引,而 B 使用了索引。

    索引 K 中包含了查询语句所需要的字段 ID 的值,无需再次回到主键索引树查找,也就是“覆盖”了我们的查询需求,我们称之为覆盖索引。覆盖索引可以减少树的搜索次数,显著提升查询性能。

    最左匹配

    • SQL 语句 A

    explain select * from employees where hire_date > '1990-01-14' and first_name like '%Hi%';
    
    
    • SQL 语句 B

    explain select * from employees where hire_date > '1990-01-14' and first_name like 'Hi%';
    
    • 分析

    在上述测试的 SQL 语句 A 使用了极端方式: first_name like '%Hi%',前后都增加模糊匹配使得 SQL 语句无法使用到索引;当去掉最左边的‘%’后,SQL 语句 B 就使用了索引。最左匹配可以是字符串索引的最左 N 个字符,也可以是联合索引的最左 M 的字段。合理规划、使用最左匹配可以减少索引,从而节约磁盘空间。

    索引下推

    何为索引下推?我们先从下面这组对比测试开始,将在 MySQL5.5 版本和 MySQL5.7 版本中执行同一条 SQL 语句:

    select * from employees where hire_date > '1990-01-14' and first_name like 'Hi%';
    
    • 在 MySQL 5.5 执行 explain,extra 字段的值显示没有使用索引

    执行查询花费时间为 0.12s

    • 在 MySQL 5.7 执行 explain,extra 字段的值显示使用了索引下推

    执行查询花费时间为 0.02s

    • 索引下推

    explain 结果中的 extra 字段值包含 using index condition,则说明使用了索引下推。索引下推功能是从 5.6 版本开始支持的。在 5.6 版本之前,i_first_name 索引是没有使用上的,需要每次去主键索引表取完整的记录值进行比较。从 5.6 版本开始,由于索引 i_first_name 的存在,可以直接取索引的 first_name 值进行过滤,这样不符合"first_name like 'Hi%'"条件的记录就不再需要回表操作。

    MRR 优化

    MySQL 5.6 版本开始支持 Multi-Range Read(MRR)优化,MRR 优化的目的是为减少磁盘的随机访问,并且将随机访问转化为较为顺序的数据访问,对于 IO-bound 类型的 SQL 查询语句可带来性能极大提升。我们先看下对比测试,以下测试语句在同一个 MySQL 实例下执行,执行前均进行 mysql 服务重启,以保证缓存此没被预热。

    • 关闭 MRR

    SET @@optimizer_switch='mrr=off';
    select * from employees where hire_date > '1990-01-14' and first_name like 'Hi%';
    

    执行耗时未 0.90s

    • 开启 MRR

     SET @@optimizer_switch='mrr=on,mrr_cost_based=off';
     select * from employees where hire_date > '1990-01-14' and first_name like 'Hi%';
    
    • 分析

    从测试结果可以发现在 mrr 从关闭到开启,耗时从 0.90s 减少到 0.03s,查询速率达到 30 倍的提升。

    常见的索引失效场景

    在 MySQL 表中建立了索引,SQL 查询语句就会一定使用到索引么?不一定,存在着索引失效的场景。我们给 employees 表增一个组合索引,后续例子均基于此表进行分析、测试。

    alter table employees add index i_b_f_l(birth_date, first_name, last_name)
    alter table employees add index i_h(hire_date);
    

    失效场景

    • 范围查询(>,<,<>)

    explain select * from employees where hire_date > '1989-06-02';
    
    • 查询条件类型不一致

    alter table employees add index i_first_name (first_name);
    explain select * from employees where first_name = 1;
    
    • 查询条件使用了函数

    explain select * from employees where CHAR_LENGTH(hire_date) = 10;
    
    • 模糊查询

    explain select * from employees where hire_date  like  '%1995';
    
    • 不使用组合索引的首个字段当条件

    explain select * from employees where last_name = 'Kalloufi' and first_name = 'Saniya';
    

    为什么会失效?

    • 顺序读比离散读性能要好

      范围查询一定会导致索引失效么?

      并不会!稍微更改下查询条件看下 explain 的对比结果,可以看到新语句用到索引下推,说明索引并未失效。为什么?

      在不使用覆盖索引的情况下,优化器只有在数据量小的时候才会选择使用非聚集索引。受制于传统的机械磁盘特性,通过聚集索引顺序读数据行的性能会比通过非聚集索引离散读数据行要好。所以,优化器在即使有非聚集索引、但是访问数据量可能达到送记录数的 20%时会选择聚集索引。当然也可以用 Force index 强制使用索引。

    explain select * from employees where hire_date > '1999-06-02';
    
    • 无法使用 B+索引快速查找

      B+树索引支持快速查询的基本要素是因为其索引键值是有序存储的,从左到右由小到大,这样就可以在每个层级的节点中快速查并进入下一层级,最终在叶子节点找到对应的值。

      使用函数会使得 MySQL 无法使用索引进行快速查询,因为对索引字段做函数操作会破坏索引值的有序性,所以优化器选择不使用索引。而查询条件类型不一致其实也是同样的情况,因为其使用了隐式类型转换*。

    模糊匹配和不使用组合索引的首字段作为查询条件均是无法快速定位索引位置从而导致无法使用索引。模糊匹配当查询条件是 lwhere A ike 'a%',a 是 A 的最左前缀时是可能用上索引的(最左匹配),是否用上最终还是依赖优化器对查询数据量的评估。

    回到初始的案例

    让我们回到文章初的案例,尝试回答下当时提出的 3 个问题。

    -- A语句
    SELECT FStatDate, FMerchantId, FVersion, FBatch, FTradeAmount, FTradeCount FROM T_Mch******Stat_1020 WHERE FStatDate = 20201020     AND FVersion = 0     AND FMerchantId > 0 ORDER BY FMerchantId ASC LIMIT 0, 8000;
    
    -- B语句
    SELECT SQL_CALC_FOUND_ROWS a1.FStatDate,
        a1.FMerchantId,
        a1.FVersion,
        FBatch,
        FTradeAmount,
        FTradeCount
    FROM T_Mch******Stat_1020 a1, (
        SELECT FStatDate, FMerchantId, FVersion
        FROM T_Mch******Stat_1020
        WHERE FStatDate = 20201020
            AND FVersion = 0
            AND FMerchantId > 0
            ORDER BY FMerchantId ASC LIMIT 0, 8000 ) a2
    where a1.FStatDate = a2.FStatDate
        and a1.FVersion = a2.FVersion
        and a1.FMerchantId = a2.FMerchantId;
    
    

    SQL 语句 A 的查询条件字段都在主键中,主键索引用到了没?

    主键索引其实是有被使用的:索引的范围查询,只是其需要逐条读取和解析所有记录才导致慢查询。

    SQL 语句 B 的子查询为什么能够用到索引?

    1. 前文中我们介绍了聚集索引,其索引键值就是主键。

    2. 两条 SQL 语句的不同之处在于 B 语句的子查询语句的 Select 字段都包含在主键字段中,而 A 语句还有其它字段(例如 FBatch 和 FTradeAmount 等)。这种情况下只凭主键索引的键值就能满足 B 语句的字段要求;A 语句则需要逐条取整行记录进行解析。

    前后两条语句执行流程的差异是什么?

    • SQL 语句 A 的执行过程:

    1. 逐条扫描索引表并比较查询条件

    2. 遇到符合查询条件的则读取整行数据返回

    3. 回到 a 步骤,直至完成所有索引记录的比较

    4. 对返回的所有符合条件的记录(完整的记录)进行排序

    5. 选取前 8000 条数据返回

    • SQL 语句 B 的执行过程:

    1. 逐条扫描索引表并比较查询条件

    2. 遇到符合查询条件的则从索引键中取相关字段值返回

    3. 回到 a 步骤,直至完成所有索引记录的比较

    4. 对返回的所有符合条件的记录(每条记录只有 3 个主键)进行排序

    5. 选取前 8000 条数据返回形成临时表

    6. 关联临时表与主表,使用主键相等比较查询 8000 条数据

    • 对比两个 SQL 语句的执行过程,可以发现差异点集中在步骤 2 和步骤 4。在步骤 2 中 SQL 语句 A 需要随机读取整行数据并解析非常耗资源;步骤 4 涉及 MySQL 的排序算法,这里也会对执行效率有影响,排序效果上看 SQL 语句 B 比 SQL 语句 A 好。

    名词解释

    • 主键索引

    顾名思义该类索引由表的主键组成,从左到右由小到大排序。一个 Innodb 存储表只有一张主键索引表(聚集索引)。

    • 普通索引

    最为平常的一种索引,没有特别限制。

    • 唯一索引

    该索引的字段不能有相同值,但允许有空值。

    • 组合索引

    由多列字段组合而成的索引,往往是为了提升查询效率而设置。

    总结

    在文章开始时介绍了常见的几种索引数据结构,适合静态数据的有序数组、适合 KV 结构的哈希索引及兼顾查询及插入性能的搜索二叉树;然后介绍了 Innodb 的常见索引实现方式 B+树及 Select 语句使用 B+树索引查找记录的执行过程,在这个部分我们了解了几个关键的概念,回表、覆盖索引、最左匹配、索引下推和 MMR;之后还总结了索引的失效场景及背后的原因。最后,我们回到最初的案例,分析出优化前后 SQL 语句在使用索引的差异,进而导致执行效率的差异。

    本文介绍了索引的一些粗浅知识,希望能够对读者有些许帮助。作为阶段性学习的一个总结,文章对 MySQL 索引的相关知识基本上是浅藏辄止,日后还需多多使用和深入学习。

    何以解忧?唯有学习。

    参考书目和资料

    展开全文
  • 心得 离散对数这里还是不怎么熟, 很早就学过,然而不怎么会套板子, 现在总算是把板子整理下来了 思路来源 ... ... 知识点整理 1.BSGS 求的最小x值,p仅为素数...

    心得

    离散对数这里还是不怎么熟,

    很早就学过,然而不怎么会套板子,

    现在总算是把板子整理下来了

    思路来源

    https://blog.csdn.net/qq_34921856/article/details/79794335

    https://blog.csdn.net/litble/article/details/73252928

    知识点整理

    1.BSGS

    a^{x}\equiv b(mod\ p)的最小x值,p仅为素数

    x=i*m+j,j<m,则需解a^{i*m}\equiv b*a^{-j}(mod\ p)

    j从0到m-1预处理等式右边的值v,令map[v]=j存下标,

    再左端枚举i从0到m-1,去查map表即可,找到最小的i满足j在表中,i*m+j即为答案

    复杂度最低时,令m=\sqrt pO(\sqrt p)解决一个x

    /*==================================================*\
    | Baby-Step-Giant-Step 大步小步算法
    | 求 a^x === b (mod p) 中的 最小 x值 -- 此处p仅为素数
    | 实际运用中用自己的hash表代替map可防TLE
    \*==================================================*/
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    ll a,b,p;
    ll extgcd(ll a,ll b,ll &x,ll &y)
    {
    	ll d=a;
    	if(b)d=extgcd(b,a%b,y,x),y-=(a/b)*x;
    	else x=1,y=0;
    	return d;
    }
    ll BSGS(ll a, ll b, ll p) 
    { 
        a %= p; b %= p;
        map<ll, ll> h;
        ll m = ceil(sqrt(p)), x, y, d, t = 1, v = 1;
        for(ll i = 0; i < m; ++i)
    	{
            if(h.count(t)) h[t] = min(h[t], i);
            else h[t] = i;
            t = (t*a) % p;
        }
        for(ll i = 0; i < m; ++i) 
    	{
            d = extgcd(v, p, x, y);//vx==1(mod p)即v在模p意义下逆元 
            x = (x* b/d % p + p) % (p);
            if(h.count(x)) return i*m + h[x];
            v = (v*t) % p;
        }
        return -1;
    }
    int main()
    {
    	scanf("%lld%lld%lld",&a,&b,&p);//a^x==b(mod p)
    	printf("%lld\n",BSGS(a,b,p));
    	return 0;
    }

    2.扩展BSGS

    a^{x}\equiv b(mod\ p)的最小x值,p不为素数

    a^{x-1}*a\equiv b(mod\ p)

    考虑扩展欧几里得,在求ax\equiv b(mod\ p)时,如果a、p不互质则a、b、p同除gcd(a,p)

    以上方法也同理,a^{x-1}*\frac{a}{d}{}\equiv \frac{b}{d}(mod\ \frac{p}{d})

    \frac{p}{d}与a互质,直接解x-1,所得加1即答案

    否则再提一个a出来,a^{x-2}*\frac{a}{d_{1}}*\frac{a}{d_{2}}\equiv \frac{b}{d_{1}*d_{2}}(mod\ \frac{p}{d_{1}*d_{2}})

    重复该过程,直至a与\frac{p}{\prod_{i=1}^{k}d_{i}}互质,

    去解a^{x-k}*\frac{a^{k}}{\prod_{i=1}^{k}d_{i}} \equiv \frac{b}{\prod_{i=1}^{k}d_{i}}(mod\ \frac{p}{\prod_{i=1}^{k}d_{i}})

    注意解出来的是x-k,再加k即答案,

    这里整理的板子,是手写哈希的,开大于预处理部分的空间的最小素数值

    #include<iostream>
    #include<cstdio>
    #include<climits>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define mod 99991
    int head[mod],tot;
    ll Next[mod<<1],id[mod<<1],has[mod<<1];
    ll gcd(ll x,ll y)
    {
    	ll r=x%y;
    	while(r){x=y;y=r;r=x%y;}
    	return y;
    }
    void add(ll x,ll num)
    {
    	ll k=num%mod;
    	id[++tot]=x;has[tot]=num;
    	Next[tot]=head[k];head[k]=tot;
    }
    ll find(ll num)
    {
    	ll k=num%mod;
    	for(ll i=head[k];i!=-1;i=Next[i])
    	if(has[i]==num)return id[i];
    	return -1;
    }
    ll exbsgs(ll a,ll b,ll p)
    {
    	a%=p;b%=p;
    	if(b==1)return 0;
    	ll tmp=1,d=1,cnt=0;
    	while((tmp=gcd(a,p))!=1)
    	{
    		if(b%tmp)return -1;
    		cnt++;b/=tmp;p/=tmp;d=d*(a/tmp)%p;
    		if(b==d)return cnt;//注意这里
    	}
    	ll m=ceil(sqrt(p)),q=1,j;
    	//一般m开为根号p 视具体询问而改变 
    	//看预处理和询问的次数 如只询问一次则应根号p 
    	for(ll i=0;i<m;i++)
    	{add(i,(q*b)%p);q=(q*a)%p;}
    	for(ll i=1;i<=m+1;i++)
    	{
    		d=(d*q)%p;j=find(d);
    		if(j!=-1)return i*m-j+cnt;
    	}
    	return -1;
    }
    void init()
    {
    	memset(head,-1,sizeof head);
    	tot=0;
    }
    int main()
    {
    	ll a,b,p,ans;
    	while(~scanf("%lld%lld%lld",&a,&p,&b))
    	{
    		if(!a&&!b&&!p)break;
    		init();
    		ans=exbsgs(a,b,p);
    		if(ans==-1)puts("No Solution");
    		else printf("%lld\n",ans);
    	}
    	return 0;
    }

    3.Pohlig-Hellman

    http://www-math.ucdenver.edu/~wcherowi/courses/m5410/phexam.html

    啃了一天,贴一下网页局部,描述一下算法流程,讲点心得

    a^{x}\equiv b(mod\ p)的最小x值,p仅为素数但p<=1e18,此时O(\sqrt p)解决不了x,

    但p的特点在于,p-1可以表示为小质数的乘积,如例p-1=2^{2}*3^{4}*5^{2}

    如果第一步能先求出x模每个素因子的幂p_{i}^{k_{i}}的值,届时第二步CRT合并即可

     

    考虑第一步,

    要求最小循环节,初始循环节cycle=p-1

    应当检查每个素因子是否满足,对于当前循环节cycle而言,

    a^{\frac{cycle}{p_{i}}}\equiv 1(mod\ p)是否成立,如果成立,

    说明cycle可以除掉p_{i},使循环节更小,

    那就除掉p_{i},直至该素因子p_{i}不满足a^{\frac{cycle}{p_{i}}}\equiv 1(mod\ p)

    再去检查下一素因子,直至检查完所有素因子

     

    经验证,这里的p的最小循环节为p-1,

    先预处理a^{1*\frac {p-1}{p_{i}}},...a^{(p_{i}-1)*\frac {p-1}{p_{i}}},对于x2而言,只有a^{1*\frac{p-1}{2}}\equiv 8100(mod\ 8101)

    然后去求(a^{x})^{\frac{p-1}{2}}\equiv7531^{\frac{p-1}{2}}\equiv8100(mod\ p)

    x_{2}是x除以4的余数,可以表示为x_{2}=c_{0}+c_{1}*2

    x=c_{0}+c_{1}*2+c_{2}*4+...,显然后面包含*2的项是循环节的倍数,被循环节消掉了,

    (a^{x})^{\frac{p-1}{2}}\equiv(a^{c_{0}+c_{1}*2+c_{2}*4+...})^{\frac{p-1}{2}}\equiv a^{c_0*\frac{p-1}{2}},查表知c_{0}=1

    对原余数7531乘以a^{c_{0}}的逆元,新的x=c_{1}*2+c_{2}*4+...,重复该过程,

    (a^{x})^{\frac{p-1}{4}}\equiv(a^{c_{1}*2+c_{2}*4+...})^{\frac{p-1}{4}}\equiv a^{c_1*2*\frac{p-1}{4}}\equiv a^{c_1*\frac{p-1}{2}}\equiv 1,查表知c_{1}=0

    由于x_{2}=c_{0}+c_{1}*2,代入解得x_{2}=1,该过程终止

     

    对于每个素因子p_{i}^{k_{i}},都类似地解出其余数x_{i},得到同余方程组\begin{bmatrix} x \equiv x_{2}(mod\ 2^{2})\\ x \equiv x_{3}(mod\ 3^{4}) \\ x \equiv x_{5}(mod\ 5^{2}) \end{bmatrix}

    由于素因子幂次之间显然互质,裸的CRT,合并构造一下即得x,

     

    附:hdu6632 discrete logarithm problem

     求最小的x满足a^{x}\equiv b(mod\ p),p-1的素因子只由2、3组成

    2<=a,b<=p-1,65537<=p<=10^{18}

    据说是CTF界人尽皆知sb题,然而我一个打ACM的怎么可能会知道

    (抓周树人和我鲁迅有什么关系)

    仿着网页的过程敲,写了一波又丑常数又大的代码

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e4+10;
    const int N=60;
    const int MAXP=20;//最大素因子的大小 
    typedef long long ll;
    bool ok[maxn],yes;
    int prime[maxn],cnt;
    int t;
    int v[N],num[N],tot;//素数因子v[i] 出现了num[i]次 
    ll X[N],P[N],sz;//返回(x',pi^ki) x'=x(mod pi^ki) 用于CRT 
    ll a,b,p;
    void sieve()
    {
    	for(ll i=2;i<maxn;++i)
    	{
    		if(!ok[i])prime[cnt++]=i;
    		for(int j=0;j<cnt;++j)
    		{
    			if(i*prime[j]>=maxn)break;
    			ok[i*prime[j]]=1;
    			if(i%prime[j]==0)break; 
    		}
    	}
    }
    int fac(ll x)
    {
    	int tot=0;
    	for(int i=0;i<cnt;++i)
    	{
    		if(1ll*prime[i]*prime[i]>x)break;
    		if(x%prime[i]==0)
    		{
    			v[++tot]=prime[i];
    			num[tot]=0;
    			while(x%prime[i]==0)
    			{
    				num[tot]++;
    				x/=prime[i];
    			}
    		}
    	}
    	if(x>1)
    	{
    		v[++tot]=x;
    		num[tot]=1;
    	}
    	return tot;
    }
    ll extgcd(ll a,ll b,ll &x,ll &y)
    {
    	ll d=a;
    	if(b)d=extgcd(b,a%b,y,x),y-=(a/b)*x;
    	else x=1,y=0;
    	return d;
    }
    ll mul(ll u,ll v,ll p)
    {
    	return (u*v-(ll)((long double)u*v/p)*p+p)%p;
    }
    ll modpow(ll x,ll n,ll mod)
    {
    	ll res=1;
    	for(;n;n/=2,x=mul(x,x,mod)%mod)
    	if(n&1)res=mul(res,x,mod)%mod;
    	return res;
    }
    ll Pholig_Hellman(ll a,ll B,ll p)//返回(x',pi^ki) x'=x(mod pi^ki) 
    {
    	ll r=p-1;
    	//找a的阶(最小)r p-1不一定是最小 类似判原根的过程 
    	//最后r应该化为最小的阶
    	tot=fac(r);
    	for(int i=1;i<=tot;++i)
    	while(r%v[i]==0&&modpow(a,r/v[i],p)==1)r/=v[i];
    	tot=fac(r);//重新求素因子 
    	ll x,y,inv,tmp[MAXP],sz=0;
    	extgcd(a,p,x,y);
    	inv=(x%p+p)%p;
    	for(int i=1;i<=tot;++i)
    	{
    		int c=v[i],d=num[i];
    		ll b=B,f=0,g=1;
    		ll l=1,now=r,h=modpow(a,now/c,p);
    		//printf("h:%lld\n",h);
    		for(int k=0;k<c;++k,l=mul(l,h,p))
    		{
    			tmp[k]=l; 
    			//printf("tmp[%d]:%lld\n",k,tmp[k]);
    		}
    		for(int j=1;j<=d;++j)
    		{
    			now/=c;//(p-1)/(c^j)
    			ll e=modpow(b,now,p);
    			ll co=-1,dif;//系数co 
    			for(int k=0;k<c;++k)
    			{
    				if(e==tmp[k])
    				{
    					co=k; 
    					break;
    				}
    			}
    			if(co==-1)return -1;//无解
    			//printf("co:%lld\n",co);
    			co*=g;//实际系数 
    			f+=co;
    			dif=modpow(inv,co,p);
    			b=mul(b,dif,p); 
    			g*=c;
    		}
    		//printf("f:%lld g:%lld\n",f,g);
    		X[++sz]=f;
    		P[sz]=g;
    	}
    	return sz;
    }
    ll CRT(ll r[],ll m[],int n)//x'==x(mod p)方程组 共n项 要求pi互素 
    {
    	ll ans=0,M=1,x,y;
    	for(int i=1;i<=n;++i)
    	M*=m[i];
    	for(int i=1;i<=n;++i)
    	{
    		ll mi=M/m[i];
    		extgcd(mi,m[i],x,y);
    		x=(x%m[i]+m[i])%m[i];
    		ans=(ans+mul(mul(r[i],mi,M),x,M))%M;
    	} 
    	return (ans+M)%M;
    }
    int main()
    {
    	sieve();
    	scanf("%d",&t);
    	while(t--)
    	{
    		scanf("%lld%lld%lld",&p,&a,&b); 
    		sz=Pholig_Hellman(a,b,p);
    		if(sz!=-1)printf("%lld\n",CRT(X,P,sz));
    		else puts("-1");
    	}
    	return 0;
    }
    //p a b
    /*
    105
    65537 2 4
    8101 6 7531
    */

     

    展开全文
  • HashMap面试知识点总结

    2020-09-08 10:25:36
    讲道理hashmap在面试中问的还是蛮多的,有些基本的知识还是一定要会 基本数据结构: JDK1.7:数组 + 链表 JDK1.8以后:数组 + 链表 + 红黑树 hashmap首先是一个线程不安全的容器,也不允许key和value为null。hashmap...
  • 指数和对数的公式总结

    万次阅读 2017-11-27 16:43:55
    指数和对数在近来开始出现,有的是以不等式形式来考,有的是以其中的一些公式变形来考察,由于这点知识大家掌握的不是很好,加之这块涉及到一些公式成立的前提条件,所以大家容易漏掉一些范围,而选错答案,现在我将...
  • 字符串知识点总结

    2019-06-01 11:48:52
    一、严格模式 严格模式下,不允许隐式声明变量。 如何启动严格模式?...二、ES5对数组新增的方法:(都不影响原数组) 1. indexOf(‘元素’,start) 作用: 在指定数组中查找某个元素第一次出现的下标...
  • 复习知识点(浓缩版) 第一章 协方差矩阵的计算、证明变量独立 期望定义、标准正态的绝对值的期望、方差 标准正态查表 中心极限定理、切比雪夫不等式 常用分布对应的密度函数、期望、方差 三大分布(非常重要,要会...
  • 计算机网络 选择(30分) 网络是指“三网”,即电信网络,有线电视网络和计算机网络。 从通信的双方信息交互的...6.香农公式:C=W*log2(1+S/N) ( log2表示以2为底的对数)(bit/s) W是信道带宽(赫),S是信号功率
  • 一次函数、二次函数、反比例函数、指数函数、对数函数、幂函数的性质与图像知识点总结; 今天高中数学必修一函数性质图像的知识点就分享到这里,有更多高中数学学习方法以及解题技巧需要的可以私聊...
  • ufunc(universal function object)是解决对数组进行处理的函数。 ndarray 对象 ndarray 实际上是多维数组的含义。在 NumPy 数组中,维数称为秩(rank),一维数组的秩为 1,二维数组的秩为 2,以此类推。在 NumPy...
  • 根据多次面试经历,总结下golang开发需要掌握的知识点1.slice和数组的区别slice是数组的快照,slice底层数据是一个结构体,包含三个元素,长度、容量和数组指针。所以slice的赋值就如同结构体的赋值一样,slice的...
  • JAVA项目中常用的异常知识点总结 1. java.lang.nullpointerexception这个异常大家肯定都经常遇到,异常的解释是"程序遇上了空指针",简单地说就是调用了未经初始化的对象或者是不存在的对象,这个错误经常出现在...
  • 根据多次面试经历,总结下golang开发需要掌握的知识点1.slice和数组的区别slice是数组的快照,slice底层数据是一个结构体,包含三个元素,长度、容量和数组指针。所以slice的赋值就如同结构体的赋值一样,slice的...
  • 但是添加了索引,MySQL可以直接在索引列中进行查找,由于索引采用的是B+树,具有稳定的对数时间复杂度,所以能够快速定位到满足条件的数据记录。 一、索引种类 1、普通索引(index):是最基本的索引,它没有任何...
  • c++知识点总结(2)

    2018-07-12 20:04:39
    1 : 构造函数中 , 对数组的初始化时不能用初始化表的形式.2: 重写是父类和子类之间才有的关系 , 重载是指函数名相同 , 函数参数个数和类型不同.3: Box box() ; //声明了一个box()方法 , 它 返回Box对象. Box box ...
  • NumPy(Numerical Python)科学计算和数据...能够对数组进行元素级计算以及直接对数组执行数学运算的特殊函数 NumPy数组的基本属性 NumPy中,每一个线性的数组称为是一个轴 NumPy数组的维数称为秩(rank) 一维数组的
  • Numpy - 知识点总结(四)

    千次阅读 2018-06-26 15:35:28
    一 、 位操作bitwise_and:对数组元素执行位与操作bitwise_or:对数组元素执行位或操作invert:计算位非left_shift:向左移动二进制表示的位right_shift:向右移动二进制表示的位import numpy as np a = 3 b = 6 ...
  • 指数和对数运算错误5.三角函数取值错误求极限1.洛必达:(1)没有上下同时求导(2)不满足洛必达使用条件时用洛必达2.等价无穷小:(1)等价无穷小公式记错顺序和符号(2)看到x=0进行替换,实际并不是无穷小(3)整体替换时...
  • 第三章 LLR(对数似然比)知识点补充

    千次阅读 2020-07-04 23:29:11
    知乎上很多大神解释,个人学习总结了一下。”似然”与“概率“两个概念是相对的。 概率:已知概率模型,推测某个事件发生的概率; 似然:根据已得到的数据集合,推测符合的概率模型; 举个例子,下图是根据一组...
  • Numpy - 知识点总结(三)

    千次阅读 2018-06-25 17:28:14
    广播广播是指numpy在算数运算期间处理不同形状的数组的能力,对数组的算数运算通常在相应的元素上进行。如果两个阵列具有完全相同的形状,则这些操作被无缝执行;迭代Numpy包包含一个迭代器对象numpy.nditer,它是一...
  • (1)数组本身体现出来的就是一个 指针常量的 “特性”,即不能对数组的首地址进行修改,内存上的地址就已经是确定了的。而指针本身是一个变量,他指向了一个地址,这个是可以变化的,也就说他可以重新赋值指向新的...
  • 步入高中,数学必修一第一章就是函数,函数在高中的整个阶段是最重要的,从最基础对数函数学起,这时函数部分的最...下面是分享的高中函数知识点总结思维模板以及绘制方法介绍,有需要的朋友可以进行参考使用哦! ...
  • Collection容器:无序、不唯一 ...是一个对数组做了封装的容器类。该容器的优缺点和数组基本一致。比数组的优点在于,提供了对数组元素的封装的方法。 缺点: 1、 插入,删除元素效率低,需要移动大...
  • 1、v-for:循环列表。 <ul> <li v-for="item in items">{{item}}</li> <... 还可以在控制台上直接对数组进行操作:如app.items.push('4'); 2、v-on:事件绑定。v-on:click...
  • 1.对数组中的元素去重的方法之一 //对可变数组进行去重 NSMutableArray *phoneArray = [NSMutableArray array]; for (int i = 0; i &lt; person.persons.count; i++) { [phoneArray addObject:person....
  • 在我们进行排序的时候,难免要用到一些函数来执行。php中排序函数有很多种,就拿...1.概念用于对数组单元从低到高进行排序。注意:本函数会为排序的数组中的单元赋予新的键名,这将删除原有的键名而不仅是重新排序。...
  • 高中数学的学习有几个方面的知识点组成:集合、函数(包含一次函数,二次函数,指数函数,对数函数,三角函数)、几何(直线方程,圆的方程)、概率、向量等等。 其中学生们学习最有难度的模块就属于函数和几何了,...

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 136
精华内容 54
关键字:

对数知识点总结