多项式运算

Posted wfybeliefs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多项式运算相关的知识,希望对你有一定的参考价值。

技术图片

编译环境说明,以下代码能够在 codeblocks 16.01 自带编译器版本中, C语言 项目中正确运行,没有警告,没有错误,如果,更换为其它环境,例如 DEV 请自行修改编译器不同造成的差异。

前言

一元多项式计算器没有用链表写这个多项式之前,哇,觉得这个好难啊!!!链表是人干出来的事情??写完了才发现,“真香警告”。(︶^︶)我才不会承认的。

基本变量

变量名描述
c 系数
n 幂值

程序模块

函数名描述
input() 返回一个多项式升序链表
insert(int c, int n, LIST head) 改良的插入方法,升序插入和幂值相同控制
add(LIST A, LIST B) 两个多项式相加
sub(LIST A, LIST B) 多项式相减 A - B
mul(LIST A, LIST B) 多项式相乘 A * B
output(LIST head) 按照一定形式输出多项式
reversed(LIST head) 链表反转(降幂)
uptate(LIST head) 更新链表的信息(删除系数为0的结点)
show(LIST head) 对输出方法(降幂,升幂,return)封装
init() 对运算函数(add,sub,mul)封装

构建输入函数

不管会不会,输入是必须的,先把数据读入再说。既然选用了链表存储,用链表来实现,那么我们肯定要考虑到链表的两种形式:带头结点的链表不带头结点的链表,这两种有什么区别呢?

同时我们还需要了解下面三个概念:(不想看直接跳过,我已经整理成图片的形式了)

名称解释说明
首元节点 就是指链表中存储第一个数据元素a1的结点。
头结点 它是在首元结点之前附设的一个节点,其指针域指向首元结点。头结点的数据域可以不存储任何信息,也可以存储与数据元素类型的其他附加信息,例如,当数据元素为整数型时,头结点的数据域中可存放该线性表的长度。如上表中的数据域和指针域(在链表和线性中一个结点包含两个域:一个是数据域,另外一个是指针域,指针域它本有自己的存储地址,同时又指向下一个节点的数据域,这就形成了一个逻辑关系)就是一个头结点。
头指针 它是指向链表中的第一个结点的指针。若链表设有头结点,则头指针所指结点为线性表的头结点;若链表不设头结点,则头指针所指结点为该线性表的首元结点。

下面是图片辅助理解:

技术图片

带头结点

技术图片

不带头结点

技术图片

那么看到这个地方,我的选择是带头结点。原因是一般的链表都带头结点,另外就是,带头结点,插入,查找,删除都比较方便。

输入函数的写法

首先看到这个地方,我们需要有一个头指针,暂时标记为head,这个指针这这个地方不存储人任何元素(实际操作这个头结点的数据域可以存储元素,比如链表长度)。

其次,我们应该考虑输入的多项式的格式问题:

技术图片

特征提取得到两个关键的元素,系数指数。这样考虑的原因是因为,X是固定不变的,没有逻辑上的意义,所以不考虑,那么我们得到这些,就可以每次申请一块地址空间,来存放多项式中的某一项,链表的一般方法是插入。我们假定有这样一个方法,那么我们如何存储链呢?

第一种就是尾插法,节点一直插入到链表的尾部:

技术图片 技术图片

这种方式存储我们的链表当然可以。

我们假定我们已经写了 insert 函数

LIST input()
{
    printf("输入系数和指数,以0 0结束 ");
    //输入系数和指数
    LIST head = (LIST)malloc(sizeof(struct node));
    head->NEXT = NULL;
    int n, c;
    while(scanf("%d%d", &c, &n), n || c)
        insert(c, n, head);
    return head;
}

可以看到返回的是链表类型。

那么我们可以直接调用。

printf("请先输入两个多项式
");
LIST A = input();
LIST B = input();

构建插入函数`insert`

刚刚呢我们假设了有一个这样的 insert 函数,把所有的结点都插入在链表的尾部 ,那么会造成什么样的情况呢?

技术图片

如果是按照这样升幂输入的数据当然没问题,但也有可能输入的数据是这样的:

技术图片

类似于我们这样的无序输入,那么我们下一步,肯定需要进行排序的操作,把这个变成升幂或者降幂的形式。那既然这样,为什么我们不直接就让这个链表有序插入呢?我们知道,链表的优势就是易拓展,也就是可以在中间直接插入某元素。

技术图片

这就是关键的地方!!!插入的时候就维持有序状态。

那么是不是还有别的情况?

比如说这两个值相同,换句话说,这两个幂值相同,根据运算的原理,系数肯定是要相加的。

技术图片 技术图片

这两个在实际计算过程中是等价的。

所以,我们直接在插入的时候就考虑这两种情况!!

演示:

技术图片 技术图片

找不到的情况:

技术图片

代码如下:

void insert(int c, int n, LIST head)
{
    //头结点性质
    LIST tmp = (LIST)malloc(sizeof(struct node));
    tmp->n = n, tmp->c = c, tmp->NEXT = NULL;
    LIST P = head;
    while(P->NEXT)
    {
        //相等
        if(P->NEXT->n == tmp->n)
        {
            P->NEXT->c += tmp->c;
            return;
        }
        if(P->NEXT->n > tmp->n)
            break;
        P = P->NEXT;
    }
    //找到最后还没有找到
    if(!P->NEXT)
    {
        P->NEXT = tmp;
        return;
    }
    tmp->NEXT = P->NEXT;
    P->NEXT = tmp;
}

关于加法

有了上面的铺垫是不是问题就非常简单啦!!!

insert 实现了升序和处理相同值的情况,那么加法怎么加呢?

第一种就是A链为母链,B链的所有结点都添加到(insert)A链中。

第二种就是B链为母链,A链的所有结点都添加到(insert)B链中。

第三种就是新增一条链,A链和B链的所有结点都添加到(insert)新链中。

代码如下(使用的第三种方法):

void add(LIST A, LIST B)
{
    LIST head = (LIST)malloc(sizeof(struct node));
    head->NEXT = NULL;
    LIST P = A->NEXT;
    while(P)
        insert(P->c, P->n, head), P = P->NEXT;
    P = B->NEXT;
    while(P)
        insert(P->c, P->n, head), P = P->NEXT;
    uptate(head);//暂时忽略
    show(head);//暂时忽略
}

P从A的首元节点到最末尾,然后从B的首元节点到末尾,也就是把A链和B链的所有结点都添加到(insert)新链中。

关于减法

有了加法,减法还会远吗?

技术图片 技术图片

这两个形式在值上面是等价的。

那么我们直接在加法的基础上面变形。

第一种就是A链为母链,B链的所有结点的系数的相反数都添加到(insert)A链中。

第二种就是B链为母链,A链的所有结点的系数的相反数都添加到(insert)B链中。

第三种就是新增一条链,A链和B链的所有结点的系数的相反数都添加到(insert)新链中。

P从A的首元节点到最末尾,然后从B的首元节点到末尾,也就是把A链和B链的所有结点的系数的相反数添加到(insert)新链中。

代码如下(使用的第三种方法):

void sub(LIST A, LIST B)
{
    LIST head = (LIST)malloc(sizeof(struct node));
    head->NEXT = NULL;
    LIST P = A->NEXT;
    while(P)
        insert(P->c, P->n, head), P = P->NEXT;
    P = B->NEXT;
    while(P)
        insert(-P->c, P->n, head), P = P->NEXT;
    uptate(head);//暂时忽略
    show(head);//暂时忽略
}

关于乘法

有了之前加法和乘法的思路,乘法就不用我说了。

遍历A链的所有结点,遍历B链的所有结点,也就是双重循环,AXB的所有结点添加(insert)到新链中。

因为我们规定的 insert 就是有序,不重复幂值。

所以直接丢进去就行,不用考虑。

void mul(LIST A, LIST B)
{
    LIST head = (LIST)malloc(sizeof(struct node));
    head->NEXT = NULL;
    LIST i = A->NEXT;
    //if A NULL
    LIST j = B->NEXT;
    if(!i)
    {
        while(j)
        {
            insert(j->c, j->n, head);
            j = j->NEXT;
        }
        uptate(head);
        show(head);
        return;
    }
    //if B NULL
    if(!j)
    {
        while(i)
        {
            insert(i->c, i->n, head);
            i = i->NEXT;
        }
        uptate(head);
        show(head);
        return;
    }
    while(i){
        j = B->NEXT;
        while(j)
        {
            insert(i->c * j->c, i->n + j->n, head);
            j = j->NEXT;
        }
        i = i->NEXT;
    }
    uptate(head);
    show(head);
}

非法值处理

什么样的值是特殊的?非法的?肯定是不符合我们规定的形式的。

那么我们可以得到,系数为0,其实这一项就是没有了,所以,剔除。

这里就体现了 头结点 的方便之处,本质上来说,这段代码属于,链表的删除。

基本思想就是:

技术图片

怎么剔除呢?

P->NEXT = P->NEXT->NEXT;

技术图片

这样可能会说,有bug,真的有么?

就算我是最后一个结点,也没有问题。

技术图片
void uptate(LIST head)
{
    LIST P = head;
    while(P->NEXT)
    {
        if(P->NEXT->c == 0){
            P->NEXT = P->NEXT->NEXT;
            continue;
        }
        P = P->NEXT;
    }
}

关于输出

输出就是考虑特殊值,比如说正好,负号,首次输出的符号。

考虑到这些,应该就没有问题了。

首个值,那就让指针动一下,然后 P=P->NEXT 就ok了;

代码如下:

void output(LIST head)
{
    LIST P = head->NEXT;
    if(!p) return;
    //输出第一个
    if(P->c < 0)
        printf("-");
    if(P->n == 0)
        printf("%d"abs(P->c));
    else
        printf("%dx^%d"abs(P->c), P->n);
    P = P->NEXT;
    //继续输出
    while(P)
    {
        if(P->n == 0)
            printf("%c%d", P->c > 0 ? ‘+‘ : ‘-‘,abs(P->c));
        else
            printf("%c%dx^%d", P->c > 0 ? ‘+‘ : ‘-‘abs(P->c), P->n);
        P = P->NEXT;
    }
    printf(" ");
}

两次封装

说白了,就是别人看着舒服。( ╯□╰ )

主要运用 switch 语句。

第一层封装是在main函数。主要以下功能:

名称编号功能
add(A, B) 1 A + B
sub(A, B) 2 A - B
sub(B, A) 3 B - A
mul(A, B) 4 A * B
return 5 返回上一级(结束)

第二层封装主要是三种运算关系调用:

名称编号功能
升幂 1 升幂展示多项式
降幂 2 降幂展示多项式
all 3 升序降幂
return 4 返回上一级

执行结果

技术图片

测试数据

A,B都由常数构成

A = 5; B = 7

A, B常数和多项式

A = 6; B = 7X^2 + 5 X^ 3 + 3

A, B 为多项式

A = 3X^2 + 5X^4 + 3 + 4X; B = 7X^7 + 2X + 3X^4

A, B运算后系数为0

A = 2X^2; B = -2X^2

A,B其中不全存在

A = ; B = 2X^2 + 3X^3 + 2

综合方案

A = 3X^4-5X^2+6X^1-2X^0

B = 5X^20-7X^4+3^1

完整代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct node * LIST;
struct node
{

    int c;//系数
    int n;//指数
    LIST NEXT;
};
LIST input();
void insert(int c, int n, LIST head);//插入和维护升幂
void add(LIST A, LIST B);//A链节点和B链节点直接insert里面丢,进去就有序
void output(LIST head);//
LIST reversed(LIST head);//反转->降幂
void sub(LIST A, LIST B);//A链节点和B链负节点直接insert里面丢,进去就有序
void mul(LIST A, LIST B);//A链节点 * B链节点(for for)直接insert(c*c, n+n, head)里面丢,进去就有序
void uptate(LIST head);//A链节点B链节点删除c=0节点
void show(LIST head);//展示 ,switch,
void init();//初始化
int main(int argc, char const *argv[])
{
    init();
    return 0;
}
void init()//初始化
{
    printf("请先输入两个多项式 ");
    LIST A = input();
    LIST B = input();
    printf("A = ");output(A);
    printf("B = ");output(B);
    while(1){
        int flag;
        printf("请选择操作命令->    1:加法; 2:A - B; 3:B - A; 4:乘法; 5:return ");
        scanf("%d", &flag);
        switch (flag){
            case 1:
                add(A, B);
                break;
            case 2:
                sub(A, B);
                break;
            case 3:
                sub(B, A);
                break;
            case 4:
                mul(A, B);
                break;
            case 5:
                return;
        }
    }
}
void insert(int c, int n, LIST head)
{
    //头结点性质
    LIST tmp = (LIST)malloc(sizeof(struct node));
    tmp->n = n, tmp->c = c, tmp->NEXT = NULL;
    LIST P = head;
    while(P->NEXT)
    {
        //相等
        if(P->NEXT->n == tmp->n)
        {
            P->NEXT->c += tmp->c;
            return;
        }
        if(P->NEXT->n > tmp->n)
            break;
        P = P->NEXT;
    }
    //找到最后还没有找到
    if(!P->NEXT)
    {
        P->NEXT = tmp;
        return;
    }
    tmp->NEXT = P->NEXT;
    P->NEXT = tmp;
}
LIST input()
{
    printf("输入系数和指数,以0 0结束 ");
    //输入系数和指数
    LIST head = (LIST)malloc(sizeof(struct node));
    head->NEXT = NULL;
    int n, c;
    while(scanf("%d%d", &c, &n), n || c)
        insert(c, n, head);
    return head;
}
void add(LIST A, LIST B)
{
    LIST head = (LIST)malloc(sizeof(struct node));
    head->NEXT = NULL;
    LIST P = A->NEXT;
    while(P)
        insert(P->c, P->n, head), P = P->NEXT;
    P = B->NEXT;
    while(P)
        insert(P->c, P->n, head), P = P->NEXT;
    uptate(head);
    show(head);
}
void sub(LIST A, LIST B)
{
    LIST head = (LIST)malloc(sizeof(struct node));
    head->NEXT = NULL;
    LIST P = A->NEXT;
    while(P)
        insert(P->c, P->n, head), P = P->NEXT;
    P = B->NEXT;
    while(P)
        insert(-P->c, P->n, head), P = P->NEXT;
    uptate(head);
    show(head);
}
void mul(LIST A, LIST B)
{
    LIST head = (LIST)malloc(sizeof(struct node));
    head->NEXT = NULL;
    LIST i = A->NEXT;
    //if A NULL
    LIST j = B->NEXT;
    if(!i)
    {
        while(j)
        {
            insert(j->c, j->n, head);
            j = j->NEXT;
        }
        uptate(head);
        show(head);
        return;
    }
    //if B NULL
    if(!j)
    {
        while(i)
        {
            insert(i->c, i->n, head);
            i = i->NEXT;
        }
        uptate(head);
        show(head);
        return;
    }
    while(i){
        j = B->NEXT;
        while(j)
        {
            insert(i->c * j->c, i->n + j->n, head);
            j = j->NEXT;
        }
        i = i->NEXT;
    }
    uptate(head);
    show(head);
}
void uptate(LIST head)
{
    LIST P = head;
    while(P->NEXT)
    {
        if(P->NEXT->c == 0){
            P->NEXT = P->NEXT->NEXT;
            continue;
        }
        P = P->NEXT;
    }
}
void output(LIST head)
{
    LIST P = head->NEXT;
    if(!P) return;
    //输出第一个
    if(P->c < 0)
        printf("-");
    if(P->n == 0)
        printf("%d"abs(P->c));
    else
        printf("%dx^%d"abs(P->c), P->n);
    P = P->NEXT;
    //继续输出
    while(P)
    {
        if(P->n == 0)
            printf("%c%d", P->c > 0 ? ‘+‘ : ‘-‘,abs(P->c));
        else
            printf("%c%dx^%d", P->c > 0 ? ‘+‘ : ‘-‘abs(P->c), P->n);
        P = P->NEXT;
    }
    printf(" ");
}
LIST reversed(LIST head)
{
    LIST rear, P = head->NEXT;
    rear = (LIST)malloc(sizeof(struct node));
    rear->NEXT = NULL;
    while(P)
    {
        LIST tmp = (LIST)malloc(sizeof(struct node));
        tmp->NEXT = NULL, tmp->c = P->c, tmp->n = P->n;
        tmp->NEXT = rear->NEXT;
        rear->NEXT = tmp;
        P = P->NEXT;
    }
    return rear;
}
void show(LIST head)
{
    while(1){
        int flag;
        printf("请问你想升幂输出还是降幂输出->  1:升幂; 2:降幂; 3:all; 4:return ");
        scanf("%d", &flag);
        switch (flag){
            case 1:
                printf("升幂排列是 ");
                output(head);
                break;
            case 2:
                printf("降幂排列是 ");
                output(reversed(head));
                break;
            case 3:
                printf("升幂排列是 ");
                output(head);
                printf("降幂排列是 ");
                output(reversed(head));
                break;
            case 4:
                return;
        }
    }
}

以上是关于多项式运算的主要内容,如果未能解决你的问题,请参考以下文章

链表应用:一元多项式运算器。

多项式运算

02-线性结构2 一元多项式的乘法与加法运算

多项式 - 快速沃尔什变换

[PTA]一元多项式运算器

java中完成一元稀疏多项式的基本运算