代码实现 —— 多项式的最大公因式(线性代数)

系列文章目录



前言

本系列为使用C语言实现实用的数学公式


一.最大公因式

1.定义:

设f(x),g(x) ∊ P[x]. 若有d(x) ∊ P[x]满足
(i) d(x)是f(x),g(x)的公因式.
(ii) f(x),g(x)的公因式全是d(x)的因式.
则称d(x)为f(x)与g(x)的一个最大公因式.

2.说明:

最大公因式在相差一个非零常数的意义下是唯一确定的
即d₁(x) | d₂(x), d₂(x) | d₁(x)
由整除的性质知: d₁(x)=c d₂(x).

二.辗转相除法

1.带余除法

设f(x) , g(x) ∊ P[x], g(x) != 0,则存在唯一的多项式q(x), r(x) ∊ P[x]
使 f(x) = q(x) g(x) + r(x) 其中r(x)=0 或 ∂(r(x)) < ∂(g(x)).
称上式中的q(x) 为g(x) 除f (x)的商,r(x)为g(x)除f (x)的余式.

2.引理

设f(x),g(x) ∊ P[x].如果等式f(x) = q(x)g(x) + r(x)
成立,则f(x),g(x) 和g(x),r(x)有相同的公因式.
(这里毕竟是用C语言去实现该功能,感兴趣的朋友可以自行查阅,就不在这里证明了)

3.辗转相除法求最大公因数

//辗转相除法求最大公因数
int GCF(int A, int B) {
    while (A % B) {
        int tmp = A % B;
        A = B;
        B = tmp;
    }
    return B;
}

4.辗转相除法求最小公倍数

//辗转相除法求最小公倍数
int LCM(int A, int B) {
    int AM = A;
    int BM = B;
    int RET = 0;
    while (A % B) {
        int tmp = A % B;
        A = B;
        B = tmp;
    }
    RET = AM / B * BM; 
    return RET;
}

这里即是让大家了解一下辗转相除法的思想,也是帮那些只需要这段代码的朋友们节省时间[doge]

三.C语言实现

1.目标

首先,我们的目标是通过带余除法,让两个多项式辗转相除法得到一个余式,使得这个余式与除式再一次相除,依此类推直到得到一个最高次幂为0的余式。这里我们要清楚我们需要一个循环来进行多次相除。

2.多项式的传入

首先,我们面临的第一个问题是如何传入一个多项式,很显然同时将各项的系数和次数同时传入一个数组是一件非常困难且混乱的事情。这时我们应该去寻找两个多项式共同具有的特点,他们的次数都是0~n递增。这里是不是似曾相识啊?是的,这不就是数组的下标嘛!问题迎刃而解,我们只需要按照对应的下标储存该次项所对应的系数就行了,是不是超级简单!
这里我才用的是先输入最高次幂,然后依此从高到低输入各次项系数,好处就是在输入的时候不会混乱,并且简化了输入的难度,和代码实现的难度。

//多项式一
    int N1 = 0;                                    // 最高次幂
    long COE1[20] = {0};                           // 各项系数
    printf("多项式(一)的最高次幂:>");
    scanf("%d", &N1);
    for (int i = N1; i >= 0; i--) {
        printf("请输入 %d 次幂的系数:>", i);
        scanf("%ld", COE1 + i);
    }
    //多项式二
    int N2 = 0;                                    // 最高次幂
    long COE2[20] = {0};                           // 各项系数
    printf("多项式(二)的最高次幂:>");
    scanf("%d", &N2);
    for (int i = N2; i >= 0; i--) {
        printf("请输入 %d 次幂的系数:>", i);
        scanf("%ld", COE2 + i);
    }

3.函数传参

写代码要尽量去做到高内聚低耦合并具有较高的可移植性,我们不能仅仅为了实现这一个功能让他只能在这个空间内跑动。正如程序员不能只会在一个编译器上敲代码,换成个编辑器就什么都不会了。所以当我们时,要让他可以在其他程序里运行,我们就要将其写成一个函数,在需要时随时调用。

想做一劳永逸的事情并非那么容易。首先我们要让函数内部知道这个多项式是几次幂,这里就用到了我们上一步输入时的最高次幂N1和N2,其次传入两个数组。此时问题再一次接踵而至,我们需要让他返回一个多项式,并且不能破坏原先的数组。在函数内部创建的数组是临时变量,你没有办法将地址返回到main里,临时变量的生命周期在它所在的函数结束时一并结束。这里我们在传入一个ret用于接收函数运行结果所得的多项式的最高次幂,同时一个RET用于接收最大公因式各项的系数。

4.创建变量

由于我们要确保啊函数的安全运行,这里我们对空指针进行一下断言。然后我们原则上不可以改变原先数组的内容,我们要重新创建数组,将里面的内容拷贝到临时数组中。这里创建的指针MAX和MIN目的是为了确定次数较高的多项式和较低的多项式,当MIN > MAX时两者进行地址交换。

// 返回系数数组 指针函数:(次数1, 系数数组1, 次数2, 系数数组2, 返回次数, 返回系数数组)
long *EuclideanAlgorithm(int max, const long *COE1, int min, const long *COE2, int *ret, long *RET) {
    //空指针断言
    assert(COE1 && COE2 && RET);
    //创建变量并赋值
    long M1[20] = {0};
    long M2[20] = {0};
    long *MAX = M1;
    long *MIN = M2;
    memcpy(M1, COE1, (max + 1) * 8);
    memcpy(M2, COE2, (min + 1) * 8);
}
    //判断次数大小
    if (max < min) {
        long *TMP = MAX;
        MAX = MIN;
        MIN = TMP;
        memchange(&max, &min, sizeof(max)); //内存交换函数
    }

5.内存交换函数

内存交换函数,这里我选择的是进行每个字节的内存交换 ,这里实现的时候要之分注意,只能交换两个同类型变量的内容。void *可以用来接收任意类型变量的地址,len是这两个变量的类型所占bit位数。按找char类型一个bit一个bit进行交换。

//内存交换
void memchange(void *A, void *B, int len) {
    while (len--) {
        char tmp = *(char *) A;
        *(char *) A = *(char *) B;
        *(char *) B = tmp;
        A = (char *) A + 1;
        B = (char *) B + 1;
    }
}

6.返回值的设置

此时的我们先假设功能已经实现完成,由于辗转相除往往不能一次就算出最大公因式,所以需要用循环语句多次执行。

我们不妨假设,多项式MAX除以MIN时一下子就出尽了。我们此时此刻并不清楚辗转相除功能是如何实现的,所以我们可以选择在步入到辗转相除功能之前,将MIN的地址和它系数的值赋给RET和ret就可以了。即使在运行过程中对变量造成改变,也不影响RET和ret的返回值。此时此刻我们就有了一个大的框架。

long *EuclideanAlgorithm(int max, const long *COE1, int min, const long *COE2, int *ret, long *RET) {
    //空指针断言
    assert(COE1 && COE2 && RET);

    //创建变量并赋值
    long M1[20] = {0};
    long M2[20] = {0};
    long *MAX = M1;
    long *MIN = M2;
    memcpy(M1, COE1, (max + 1) * 8);
    memcpy(M2, COE2, (min + 1) * 8);

    while (max > 0) {

        //判断次数大小
        if (max < min) {
            long *TMP = MAX;
            MAX = MIN;
            MIN = TMP;
            memchange(&max, &min, sizeof(max));
        }

        /*
        ** 记录返回值
        */

        *ret = min;
        memcpy(RET, MIN, 160);

        //函数功能始
        /*
        ** 内容
        */
        //函数功能终

    }
}

7.辗转相除

现在我们到了最为重要的一步,如何实现辗转相除?

在这里插入图片描述
简单来说就是消去MAX的最高次幂,那么晚们首先要用到的就是MIN的最高次幂的的系数。
条件:存在一个倍数A,使得当MIN的最高次幂系数乘以A时,必有(MIN的首项系数 * A) == (MAX的系数)
这样我们就将MAX的最高次幂消去了,当然MIN的其余项系数也要乘以A。

思路非常明了对不对,但是!!!当遇到A为无理数该怎么办?你除不尽啊!

这里我们依然要用到最大公因式的思想,我们要做的就是求出这两个最高次幂系数的最大公因数,然后让两个系数分别除以最大公因数得出与之对应的倍数num1和num2,而num1和num2一定是互素的。接着我们让求出的num1与MIN的各项系数相乘,num2与MAX的各项系数相乘,这样我们就得到了这两个多项式首项的最小公倍数。由于我的最大公倍数求出来可能会很大所以这里系数用是长整形。这样即达到了消去首项的目的,又满足了倍数为一个整数,这样是不是让问题变得超级简单了!这里我们可以单独写一个求最大公因数的函数。

//求最大公因数
long GCF(long A, long B) {
    if (A < B) {
        memchange(&A, &B, sizeof(A));
    }
    while (A % B) {
        long tmp = A % B;
        A = B;
        B = tmp;
    }
    return B;
}
    long num = GCF(MAX[max], MIN[min]);
    long num1 = MAX[max] / num;
    long num2 = MIN[min] / num;
    for (int i = 0; max - i + 1; i++) {
        MAX[max - i] *= num2;
    }
    for (int i = 0; min - i + 1; i++) {
        MIN[min - i] *= num1;
    }

我们现在有了两整理好的多项式,只需要让他们相减就可以了,在这里我们要对MAX的最高次幂max进行变动,所以设置一个临时的Tmax = max来替代max。将两式相减后的各项系数重新赋值给MAX,如果相减结果为0,且比它更高次幂的项系数也为0,那么我们就让max–来降低它的最高次数。如果遇到了系数非零的项,那么比他次数底,但系数为0那么max也将不会再改变。这里我用的是 && 运算符如果前面条件成立,则运算后面条件,如果前面条件不成立,则不运算后面的条件的特性。

    //两式相减
    int stop = 0;     // stop用于自高次向低次,遍历两个多项式相减所得的新多项式,系数是否为0,如果遇到不为0系数则停止遍历
    long Tmax = max;  // max要进行变动,创建临时变量Tmax取代max的位置
    for (int i = 0; min - i + 1; i++) {
        MAX[Tmax - i] -= MIN[min - i];
        if (!MAX[Tmax - i] && !stop && !max--) {
        } else if (MAX[Tmax - i]) {
            stop++;
        }
    }

8.化简系数

我们前面用最小公倍数的方法将最高次幂项消去,这势必会让系数过大,这里我们只需要再次求各项的最大公因数,然后让各项除以所得的最大公因数就可以了。

    int count = *RET;
    for (int i = 1; i <= *ret; i++) {
        count = GCF(count, *(RET + i));
    }
    for (int i = 0; i <= *ret; i++) {
        RET[i] /= count;
    }

啊哈!这样一步一步走下来是不是让问题变得超级简单了,其实问题也没有那么复杂不是吗!
嘿嘿!当然,这是用来让大家锻炼一种解决问题的思考方式的,大家可千万不要用来偷懒写高代线代的作业啊![doge]


四.完整代码

#include <stdio.h>
#include <string.h>
#include <assert.h>

//实数域多项式求最大公因式

//内存交换
void memchange(void *A, void *B, int len) {
    while (len--) {
        char tmp = *(char *) A;
        *(char *) A = *(char *) B;
        *(char *) B = tmp;
        A = (char *) A + 1;
        B = (char *) B + 1;
    }
}

//求最大公因数
long GCF(long A, long B) {
    if (A < B) {
        memchange(&A, &B, sizeof(A));
    }
    while (A % B) {
        long tmp = A % B;
        A = B;
        B = tmp;
    }
    return B;
}

//辗转相除法求多项式的最大公因式
// 返回系数数组 指针函数:(次数1, 系数数组1, 次数2, 系数数组2, 返回次数, 返回系数数组)
long *EuclideanAlgorithm(int max, long *COE1, int min, long *COE2, int *ret, long *RET) {

    //空指针断言
    assert(COE1 && COE2 && RET);

    //创建变量并赋值
    long M1[20] = {0};
    long M2[20] = {0};
    long *MAX = M1;
    long *MIN = M2;
    memcpy(M1, COE1, (max + 1) * 8);
    memcpy(M2, COE2, (min + 1) * 8);

    while (max > 0) {

        //判断次数大小
        if (max < min) {
            long *TMP = MAX;
            MAX = MIN;
            MIN = TMP;
            memchange(&max, &min, sizeof(max));
        }

        /*
        ** 记录返回值
        */

        *ret = min;
        memcpy(RET, MIN, 160);

        /*
        ** 最小公倍数
        */

        long num = GCF(MAX[max], MIN[min]);
        long num1 = MAX[max] / num;
        long num2 = MIN[min] / num;
        for (int i = 0; max - i + 1; i++) {
            MAX[max - i] *= num2;
        }
        for (int i = 0; min - i + 1; i++) {
            MIN[min - i] *= num1;
        }


        /*
        ** 两式相减
        */

        int stop = 0;     // stop用于自高次向低次,遍历两个多项式相减所得的新多项式,系数是否为0,如果遇到不为0系数则停止遍历
        long Tmax = max;  // max要进行变动,创建临时变量Tmax取代max的位置
        for (int i = 0; min - i + 1; i++) {
            MAX[Tmax - i] -= MIN[min - i];
            if (!MAX[Tmax - i] && !stop && !max--) {
            } else if (MAX[Tmax - i]) {
                stop++;
            }
        }
    }

    //化简系数
    int count = *RET;
    for (int i = 1; i <= *ret; i++) {
        count = GCF(count, *(RET + i));
    }
    for (int i = 0; i <= *ret; i++) {
        RET[i] /= count;
    }
    return RET;
}

int main() {
    //多项式一
    int N1 = 0;                                    // 最高次幂
    long COE1[20] = {0};                           // 各项系数
    printf("多项式(一)的最高次幂:>");
    scanf("%d", &N1);
    for (int i = N1; i >= 0; i--) {
        printf("请输入 %d 次幂的系数:>", i);
        scanf("%ld", COE1 + i);
    }
    //多项式二
    int N2 = 0;                                    // 最高次幂
    long COE2[20] = {0};                           // 各项系数
    printf("多项式(二)的最高次幂:>");
    scanf("%d", &N2);
    for (int i = N2; i >= 0; i--) {
        printf("请输入 %d 次幂的系数:>", i);
        scanf("%ld", COE2 + i);
    }

    //函数传惨
    int ret = 0;            //接收返回多项式的最高次数
    long RET[20] = {0};     //接收返回的多项式
    EuclideanAlgorithm(N1, COE1, N2, COE2, &ret, RET);


    //打印多项式的系数
    ret += 1;
    while (ret--) {
        printf("%d次方的系数为:%ld\n", ret, RET[ret]);
    }
    return 0;
}

总结

本文详细讲述了线性代数中,求多项式的最大公因式的方法,未来博主会继续更新相关数学函数的实现,欢迎大家交流学习。

  • 23
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 23
    评论
多项式转换是指将一组多项式系数表示的函数转换为另一组多项式系数表示的函数,常用于信号处理和控制系统设计中。以下是一个使用C++实现多项式转换的简单案例。 假设我们有一个二次函数 f(x) = 2x^2 + 3x + 1,我们希望将其转换为一个三次函数 g(x) = 4x^3 + 3x^2 + 2x + 1。 首先,我们需要将 f(x) 转换为一个多项式系数表示的向量,即 [2, 3, 1]。然后,我们定义一个大小为 4 的数组 coeff,其中 coeff[i] 表示 g(x) 中 x^i 的系数。根据多项式转换的定义,我们可以得到以下方程组: coeff[0] = f(0) coeff[1] = f'(0) coeff[2] = f''(0) / 2 coeff[3] = f'''(0) / 6 其中 f'(x)、f''(x) 和 f'''(x) 分别表示 f(x) 的一阶导数、二阶导数和三阶导数。由于 f(x) 是一个二次函数,我们可以直接计算出这些导数: f'(x) = 4x + 3 f''(x) = 4 f'''(x) = 0 因此,我们可以得到: coeff[0] = f(0) = 1 coeff[1] = f'(0) = 3 coeff[2] = f''(0) / 2 = 2 coeff[3] = f'''(0) / 6 = 0 最终,我们得到了 g(x) 的系数向量 [1, 2, 3, 4]。下面是完整的 C++ 代码实现: ```c++ #include <iostream> #include <vector> using namespace std; vector<double> polyTransform(vector<double>& fCoeff, int n) { vector<double> gCoeff(n); gCoeff[0] = fCoeff[0]; gCoeff[1] = fCoeff[1]; gCoeff[2] = fCoeff[2] / 2; gCoeff[3] = fCoeff[3] / 6; return gCoeff; } int main() { vector<double> fCoeff = {2, 3, 1}; int n = 4; vector<double> gCoeff = polyTransform(fCoeff, n); for (int i = 0; i < n; i++) { cout << "g(x^" << i << ") = " << gCoeff[i] << endl; } return 0; } ``` 输出结果为: ``` g(x^0) = 1 g(x^1) = 2 g(x^2) = 3 g(x^3) = 4 ``` 即 g(x) = 4x^3 + 3x^2 + 2x + 1。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十里坡小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值