消解算法-

题目信息

输入

合式公式 A 的合取范式

输出

当 A 是可满足时,回答YES ;否则回答NO
输入公式的符号说明:
! 非,相当于书面符号中的 ¬
& 与,相当于书面符号中的
| 或,相当于书面符号中的
- 蕴含联结词,相当于书面符号中的
+ 等价联结词,相当于书面符号中的
( 前括号
) 后括号

解答

#include <iostream>
#include <cstring>

using namespace std;
int s[500][30] = {0};
//每行存储一个简单析取式,第二维下标0~25代表命题变项a~z
//取值 0: 该变项没有出现,1: 该变项出现,2: 该变项出现且为否定
int sum0 = -1, sum1 = -1, sum2 = 0; //实现对S1,S2,S3三个集合的指向
void store(char *str)
{//将输入字符串存到s数组
    int len = strlen(str);
    for (int i = 0; i < len; i++)
    {
        if (str[i] >= 'a' && str[i] <= 'z')
        {//当为字母时
            s[sum2][str[i] - 'a'] = 1;
        }
        else if (str[i] == '&')
        {
            sum2++;
        }
        else if (str[i] == '!')
        {
            s[sum2][str[++i] - 'a'] = 2;
        }
    }
}

bool same(int a[], int b[])
{//判断两简单析取式是否相同
    for (int i = 0; i < 26; i++)
    {
        if (a[i] != b[i])
        {
            return false;
        }
    }
    return true;
}

bool check(int c[])
{//检查S1,S2,S3中是否有重复,有重复返回false
    for (int i = 0; i <= sum2; i++)
    {
        if (same(s[i], c))
        {
            return false;
        }
    }
    return true;
}

bool res(int *a, int *b)
{//消解函数,若得到空子句,返回false,否则返回true
    int single = 0; //不能消解的变项个数
    int couple = 0; //可消解的变项个数
    for (int i = 0; i < 26; i++)
    {//遍历26个字母
        if (!a[i] && !b[i])
        {//如果是两个0表示都不存在这些字母,不考虑了
            continue;
        }
        else if ((a[i] == 1 && b[i] == 2) || (a[i] == 2 && b[i] == 1))
        {//恰好能凑对
            couple++;
        }
        else
        {//双正或双负或单一存在
            single++;
        }
    }

    if (couple != 1)
    {//不能消解或有多对可以消解,因为消解公式中提及的项目是一次只消解一个项
        return true;
    }
    if (!single)
    {//现在只有一对可消解且没不能消解的变项,得到空子句
        return false;
    }

    int c[30]; //消解结果
    for (int i = 0; i < 26; i++)
    {
        if ((!a[i] && !b[i]) || (a[i] + b[i] == 3))
        {//全为0或者是一正一负可消解掉
            c[i] = 0;
        }
        else if (a[i] == 1 || b[i] == 1)
        {//两正仍未正
            c[i] = 1;
        }
        else
        {//两负仍未负
            c[i] = 2;
        }
    }

    if (check(c)) //检查c在S0,S1,S2中是否出现过
    {//如果不重复
        sum2++; //将c加入S2
        for (int i = 0; i < 26; i++)
        {
            s[sum2][i] = c[i];
        }
    }
    return true;
}

int main()
{
    //freopen("/Users/zhj/Downloads/test.txt", "r", stdin);
    char str[1000];
    cin >> str;
    store(str);
    do
    {
        sum0 = sum1;
        sum1 = sum2; //将S1并到S0中,令S1等于S2
        for (int i = 0; i <= sum0; i++)
        {
            for (int j = sum0 + 1; j <= sum1; j++)
            {
                if (!res(s[i], s[j]))
                {
                    cout << "NO" << endl;
                    return 0;
                }
            }
        }
        for (int i = sum0 + 1; i < sum1; i++)
        {
            for (int j = i + 1; j <= sum1; j++)
            {
                if (!res(s[i], s[j]))
                {
                    cout << "NO" << endl;
                    return 0;
                }
            }
        }
    } while (sum2 > sum1); //若S2为空,结束
    cout << "YES" << endl;
    return 0;
}
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
void gauss1(CMatrix &ab) { int h,w; ab.size(h,w); if(h+1!=w)//要求n阶方阵 return; int n=h; int i,j; for(i=0;i<n;++i) { //从a[i,i]到a[n,i]找出最大元素所在行 int max=i;//max指向最大列主元素所在行 for(j=i+1;j<h;++j) { if(fabs(ab.elem(j,i))>fabs(ab.elem(max,i))) max=j; } ab.swap(i,max);//交换行 if(ab.elem(i,i)==0.0)//det=0,计算停止 return; //消元 for(j=i+1;j<h;++j) { ab.pt(i,j,-(ab.elem(j,i)/ab.elem(i,i))); } //将a[i,i]化成1.0 ab.mul(i,1.0/ab.elem(i,i)); } for(i=n-1;i>=0;--i) { //消第i列上三角元素 for(j=0;j<i;++j) ab.pt(i,j,-ab.elem(j,i)); } } 最后还有三角分解的方法,直接三角分解(解相同系数的方程组Ax=(b1,b2,...)),平方根法(对称正定方程组),追赶法(对角占优的三对角线方程组)。 追赶法实现: void zhuiganfa(double const *a,double const *b,double const *c, // [in] 方程的三对角 double const *f, // [in] f向量 double *x, // [out] 方程的解 int n, // [in] 方阵的阶 double *beta=NULL // [in,none] ) { int i,tag=0; if(!beta) { beta=new double[n-1]; tag=1; } double *y=x; beta[0]=c[0]/b[0]; for(i=1;i<n-1;++i) beta[i]=c[i]/(b[i]-a[i]*beta[i-1]); y[0]=f[0]/b[0]; for(i=1;i<n;++i) y[i]=(f[i]-a[i]*y[i-1])/(b[i]-a[i]*beta[i-1]); // 追 for(i=n-2;i>=0;--i) x[i]=y[i]-beta[i]*x[i+1]; // 赶 if(tag) delete[] beta; } 第二大类就是迭代法,有雅可比迭代,高斯-塞德尔迭代法,超松弛迭代法等。 高斯-塞德尔迭代法实现: int gauss_seidel(CMatrix &ab, // [in] 方程的增广矩阵 A+b CMatrix &x, // [in,out] 迭代方程的解 double eps // [in] 精度控制 ) { int w,h,wx,hx; ab.size(h,w); // 得到行数和列数 x.size(hx,wx); assert(wx==1 && h==hx); double dx,maxdx; int cnt=0; // 统计迭代次数作为函数返回值 do { maxdx=0.0; for(int k=0;k<h;++k) { double s=0.0; for(int i=0;i<h;++i) { if(k!=i) s+=ab.elem(k,i)*x.elem(i,0); } double rst=(ab.elem(k,h)-s)/ab.elem(k,k); // 迭代计算x(k) if((dx=fabs(rst-x.elem(k,0)))>maxdx) maxdx=dx; x.elem(k,0)=rst; } ++cnt; } while(maxdx>eps); return cnt; } 超松弛迭代法实现: int sor(CMatrix &ab, // [in] 方程的增广矩阵 A+b CMatrix &x, // [in,out] 迭代方程的解 double omg, // [in] 松弛因子 double eps // [in] 精度控制 ) { int w,h,wx,hx; ab.size(h,w); // 得到行数和列数 x.size(hx,wx); assert(wx==1 && h==hx); double dx,maxdx; int cnt=0; // 统计迭代次数作为函数返回值 do { maxdx=0.0; for(int k=0;k<h;++k) { double s=0.0; for(int i=0;i<h;++i) { if(k!=i) s+=ab.elem(k,i)*x.elem(i,0); } double rst=(ab.elem(k,h)-s)/ab.elem(k,k); // 迭代计算x~(k) rst=(1-omg)*x.elem(k,0)+omg*rst; // 加权平均 if((dx=fabs(rst-x.elem(k,0)))>maxdx) maxdx=dx; x.elem(k,0)=rst; } ++cnt; } while(maxdx>eps); return cnt; } 消元法是确定的解法,但在计算机里由于浮点数存在误差,所以实际上也是近似解,对于一些病态方程仍然无从下手,其时间复杂度在O(n3),对一些特殊方程的特殊解法除外,比如追赶法就是O(n)的线性时间复杂度,而迭代法是由一组初值,进行多次迭代收敛到近似解的过程,所以有收敛性问题,在一次迭代中需要一次矩阵乘法的运算量,是O(n2),所以迭代法至少是O(n2)以上的方法,而收敛的速度取决于迭代的次数,这是收敛速度的问题。 迭代法在解一些超大型方程组时是很有效的方法,比如成百上千的未知量,你要说现实中哪有这么大的方程,有的,比如椭圆方程最简单的五点格式,网格中有多少内点就有多少未知量,一个差分网格当然是越多越精确,而这个时候O(n3)的直接法就显得像蜗牛一样,是迭代法大展拳脚的时候。 超松弛迭代法有一个松弛因子,它的研究中心就是怎么求最佳松弛因子,现实中拿到一个方程,你总不能说,我先试一下,慢慢试,试出最佳因子,你试第一个的时候,结果就已经出来了,那你还要松弛因子干嘛,你要的是方程的解。最好的情况是,在迭代过程中,松弛因子会自动适应,解在逼近方程的解,而松弛因子也在逼近最佳松弛因子。 最后一个测试的简单程序: #include "stdafx.h" #include <cmath> #include <cassert> #include "cmatrix.h" int main(int argc, char* argv[]) { double a[]={0,1,1},b[]={3,3,3},c[]={2,2,0},f[]={1,2,3},x[3]; zhuiganfa(a,b,c,f,x,3); printf("x={%lf,%lf,%lf}\n",x[0],x[1],x[2]); double data[]={3,2,0,1, 1,3,2,2, 0,1,3,3}; CMatrix ab(data,3,4),mx(3,1); int cnt; cnt=gauss_seidel(ab,mx,1e-5); //cnt=sor(ab,mx,1.2,1e-5); printf("x={%lf,%lf,%lf}\ncnt=%d\n",mx.elem(0,0),mx.elem(1,0),mx.elem(2,0),cnt); return 0; }

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

zhj12399

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

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

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

打赏作者

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

抵扣说明:

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

余额充值