编写一个C++程序来查找布尔表达式的内核

Write a C++ program to find kernel in boolean expression

提问人:max Chen 提问时间:1/3/2023 更新时间:1/4/2023 访问量:248

问:

我是一名大学生,我有一个编程作业,要求我们在布尔表达式中找到内核。老师提供的文档有一个示例伪代码来指导我们如何编写程序。伪代码如下。

// Kernel Algorithm
FindKernels(cube-free SOP expression F) // F: input Boolean function
{
    K = empty; // K: list of kernel(s)
    for(each variable x in F)
    {
        if(there are at least 2 cubes in F that have variable x)
        {
            let S = {cubes in F that have variable x in them};
            let co = cube that results from intersection of all cubes
            in S, this will be the product of just those literals that appear in
            each of these cubes in S;
            K = K ∪ FindKernels(F / co);
        }
    }
    K = K ∪ F ;
    return( K )
}

但我不知道“co”的定义是什么意思。据我所知,S 是那些具有变量 X 的项。以“abc + abd + bcd = b(ac + ad + cd)”为例,S = {abc, abd, bcd}。但什么是公司??

我还编写了另一个程序

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <iomanip>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

void find_kernels(vector<string> &terms);
bool eliminate_char(string &doing, char eliminate);
void eliminate_char_complete(vector<string> &terms, char eliminate);

int main()
{
    string file_name;
    vector<string> expression;
    vector<string> expression_name;
    string expression_temp, expression_name_temp, input_untruncated;
    vector<vector<string>> terms;//break each expression into each term

    here:

    cout << "Please enter the file name that you want to load: ";
    cin >> file_name;
    ifstream load_file(file_name, ios::in);
    if(!load_file)
    {
        cout << "The file you choose cannot be opened!\n";
        goto here;
    }

    there:

    cout << "Please enter the name of the output file: ";
    cin >> file_name;
    ofstream output_file(file_name, ios::out);
    if(!output_file)
    {
        cout << "The file cannot be created!\n";
        goto there;
    }

    while(load_file >> input_untruncated)
    {
        expression_name_temp = input_untruncated.substr(0, input_untruncated.find("="));
        expression_temp = input_untruncated.substr(input_untruncated.find("=") + 1);
        expression_name.push_back(expression_name_temp);
        expression.push_back(expression_temp);
    }



    //start to truncate every terms
    for(int i = 0 ; i < (int)expression.size() ; i++)
    {
        int j = 0;
        int k = 0;//k >> last time location
        vector<string> terms_temp_vector;
        string terms_temp;
        string expression_trans = expression[i];
        while(j < (int)expression[i].size())
        {
            if(expression_trans[j] == '+' || expression_trans[j] == '-')
            {
                terms_temp = expression_trans.substr(k, j - k);
                terms_temp_vector.push_back(terms_temp);
                k = j + 1;
            }
            j++;
        }
        terms_temp = expression_trans.substr(k);
        terms_temp_vector.push_back(terms_temp);

        terms.push_back(terms_temp_vector);
    }

    /*for(int i = 0 ; i < (int)expression.size() ; i++)
    {
        cout << "expression_name: " << expression_name[i] << endl;
        cout << expression[i] << endl;
        cout << "terms: ";
        for(int j = 0 ; j < (int)terms[i].size() ; j++)
        {
            cout << terms[i][j] << " ";
        }
        cout << endl;
    }*/
    cout << endl;

    for(int i = 0 ; i < (int)expression.size() ; i++)
    {
        //output_file << expression_name[i] << endl;
        //output_file << expression[i] << endl;
        cout << "*";
        while(terms[i].size() != 0)
        {
            find_kernels(terms[i]);
            if(terms[i].size() != 0)
            {
                cout << "terms: ";
                for(int j = 0 ; j < (int)terms[i].size() ; j++)
                {
                    cout << terms[i][j] << " ";
                }
                cout << endl;
            }
        }
        cout << endl;
    }

    /*for(int i = 0 ; i < (int)expression.size() ; i++)
    {
        cout << "expression_name: " << expression_name[i] << endl;
        cout << expression[i] << endl;
        cout << "terms: ";
        for(int j = 0 ; j < (int)terms[i].size() ; j++)
        {
            cout << terms[i][j] << " ";
        }
        cout << endl;
    }*/




    return 0;
}

void find_kernels(vector<string> &terms)
{
    int a = 0, b = 0, c = 0, d = 0, e = 0, g = 0;
    for(int i = 0 ; i < (int)terms.size() ; i++)
    {
        string terms_temp = terms[i];
        for(int j = 0 ; j < (int)terms_temp.size() ; j++)
        {
            switch(terms_temp[j])
            {
                case 'a':
                    a++;
                    break;
                case 'b':
                    b++;
                    break;
                case 'c':
                    c++;
                    break;
                case 'd':
                    d++;
                    break;
                case 'e':
                    e++;
                    break;
                case 'g':
                    g++;
                    break;
            }
        }
    }

    int compare[] = {a, b, c, d, e, g};
    int biggest = 0;
    char eliminate;

    for(int i = 0 ; i < 6 ; i++)
    {
        if(compare[i] > biggest)
        {
            biggest = compare[i];
        }
    }

    if(biggest == 1)
    {
        terms.erase(terms.begin(), terms.end());
        return;
    }

    if(biggest == a)
    {
        eliminate = 'a';
        eliminate_char_complete(terms, eliminate);
    }
    if(biggest == b)
    {
        eliminate = 'b';
        eliminate_char_complete(terms, eliminate);
    }
    if(biggest == c)
    {
        eliminate = 'c';
        eliminate_char_complete(terms, eliminate);
    }
    if(biggest == d)
    {
        eliminate = 'd';
        eliminate_char_complete(terms, eliminate);
    }
    if(biggest == e)
    {
        eliminate = 'e';
        eliminate_char_complete(terms, eliminate);
    }
    if(biggest == g)
    {
        eliminate = 'g';
        eliminate_char_complete(terms, eliminate);
    }


}

bool eliminate_char(string &doing, char eliminate)
{
    for(int i = 0 ; i < (int)doing.size() ; i++)
    {
        if(doing[i] == eliminate)
        {
            doing.erase (i, 1);
            return 1;
        }
    }
    return 0;
}

void eliminate_char_complete(vector<string> &terms, char eliminate)//delete unrelated terms
{
    for(int i = 0 ; i < (int)terms.size() ; i++)
    {
        if(!eliminate_char(terms[i], eliminate))
        {
            terms.erase(terms.begin() + i);
        }
    }
}

输入文件如下

F1=ace+bce+de+g
F2=abc+abd+bcd

我不遵守上面的伪代码。

  1. 首先,我将它们分解为单个项,并将它们推入一个称为项的两维向量中。
    terms[expression number][how many terms in one expression]
  2. 第二,我打电话给find_kernels。基金会计算每个字母在一个表达式中出现的次数。PS:只会出现A、B、C、D、E、G。
  3. 第三,取出出现次数最多的字母。例如:A、AB、ABC...
  4. 然后,在同一表达式的每个术语中消除它们。如果术语没有这些字母,则直接删除该术语。
  5. 继续做同样的事情......

但是,问题是,如果 F1 是 abc+abd+bcd,我应该输出 ac+ad+cd c+d a+c a+d,但我的程序只会输出 ac+ad+cd,导致 abc+abd+bcd = b(ac+ad+cd) >>下一轮 ac+ad+cd。a、c、d都apear两次,所以一起删除了。什么都没有了。

Any suggestion to my code or further explination of the pseudo code will be appreciate. Thank you.
c++ 布尔逻辑

评论

4赞 john 1/3/2023
如果你不明白老师告诉你的事情,那么要问的人就是你的老师。我当然不会有信心试图解释这个伪代码。我什至不确定“立方体”是什么。
0赞 max Chen 1/3/2023
阿科我会试着问他。其实我也不知道。我认为它的意思是一个“术语”。
0赞 john 1/3/2023
那么,“无立方体SOP表达”是什么意思呢?我也不知道SOP是什么意思。
0赞 max Chen 1/3/2023
无立方体意味着没有立方体(项)平均划分表达式,例如,ab + c 是无立方体的,但 ab + ac = a(b + c) 不是无立方体的
0赞 Milan Š. 1/3/2023
不过,这些定义很重要,将无立方体更改为无矩形/无正方形将使我们能够推断出含义(也许)。但在这种情况下,如果您使用定义,最好定义它们。然而,基于:似乎通过交叉,他的意思是,如果你有和。则相交的立方体为 。这能回答你的问题“什么是公司吗?let co = cube that results from intersection of all cubesexp1 = ab + ac + dexp2 = ab + ac + eab + ac

答:

1赞 sim 1/3/2023 #1

一般来说,你应该清楚你要解决的问题和应用的定义。否则,你总是会遇到严重的麻烦。 在这里,您要计算 SOP 中给出的布尔表达式的内核(乘积求和形式,例如 abc+cde)。

布尔表达式 F 的内核是无立方体表达式,当您将 F 除以单个立方体时会产生该表达式。 这个单一的多维数据集称为共核。(这是伪代码中)co

从无立方体表达式中,不能因式分解出不留余数的单个立方体。

例子

  1. F=ae+be+cde+ab
      Kernel            Co-Kernel
       {a,b,cd}         e
       {e,b}            a
       {e,cd}           b
       {ae,be, cde, ab} 1
  1. F=ace+bce+de+g
    Kernel            Co-Kernel
       {a,b}            c
       {ac, bc, d}      e

正如你所看到的,协核是你消除的变量,加上包含该变量的多维数据集中出现的所有其他常见变量。

为了实现这一点,您现在对每个变量递归应用此过程,并存储您创建的所有内核。基本上就是这样!

实际上,对于实现,我建议使用一些更方便的术语编码而不是字符串。由于您的输入似乎只有单字母变量,因此您可以将其映射到 uint64 中的单个位(如果只考虑小写,甚至可以映射到 uint32)。这将给出一个直接的实现,就像这样(认为,它是为了简单而不是性能而优化的):

#include <iostream>
#include <vector>
#include <string>
#inlcude <set>

void read_terms();
uint64_t parse_term(string sterm);
void get_kernels(vector<uint64_t>& terms, vector<pair<vector<uint64_t>, uint64_t> >& kernels);
string format_kernel(vector<uint64_t>& kernel);

int main()
{
    read_terms();
    return 0;
}

/*
Convert a cube into a string
*/
string cube_to_string(uint64_t cube) {
    string res;
    char ch = 'a';
    for (uint64_t curr = 1; curr <= cube && ch <= 'z'; curr <<= 1, ch++) {
        if ((curr & cube) == 0) {
            continue;
        }

        res += ch;
    }
    ch = 'A';
    for (uint64_t curr = (1<<26); curr <= cube && ch <= 'Z'; curr <<= 1, ch++) {
        if ((curr & cube) == 0) {
            continue;
        }

        res += ch;
    }

    return res;
}


/*
Convert a kernel or some other SOP expression into into a string
*/
string format_kernel(vector<uint64_t>& kernel) {
    string res = "";

    for (uint64_t k : kernel) {
        string t = cube_to_string(k) + "+";
        if (t.size() > 1) {
            res += t;
        }
    }

    if (res.size() > 0) {
        res.resize(res.size() - 1);
    }

    return res;
}


/*
Queries the expression from the stdin and triggers the kernel calculcation.
*/
void read_terms() {
    cout << "Please enter the terms in SOP form (0 to end input):" << endl;
    vector<uint64_t> terms;
    vector<pair<vector<uint64_t>, uint64_t> > kernels;
    string sterm;
    cout << "Term: ";
    while (cin >> sterm) {
        if (sterm == "0") {
            break;
        }
        cout << "Term: ";
        terms.push_back(parse_term(sterm));
    }

    get_kernels(terms, kernels);

    set<string> set_kernels;

    for (pair<vector<uint64_t>, uint64_t>k : kernels) {
        set_kernels.insert(format_kernel(k.first));
    }

    for (string k : set_kernels) {
        cout << k << endl;
    }

    return;
}


/*
Convert a term given as string into a bit vector.
*/
uint64_t parse_term(string sterm) {
    uint64_t res = 0;
    for (char c : sterm) {
        if (c >= 'a' && c <= 'z') {
            res |= 1ull << uint64_t(c - 'a');
        }
        else if (c >= 'A' && c <= 'Z') {
            res |= 1ull << uint64_t(c - 'A' + 26);
        }
    }

    return res;
}



/*
Returns a bitvector having a for a each variable occuring in one or more of the cubes.
*/
uint64_t get_all_vars(vector<uint64_t>& terms) {
    uint64_t res = 0;
    for (uint64_t t : terms) {
        res |= t;
    }
    return res;
}


/*
Returns a bitvector having a one for each variable that is shared between all cubes.
*/
uint64_t get_common_vars(vector<uint64_t>& terms) {
    if( terms.size() == 0 ) {
        return 0ull;
    }
    uint64_t res = terms[0];
    for (uint64_t t : terms) {
        res &= t;
    }
    return res;
}



/*
Divides all set variables from the cubes and returns then in a new vector.
*/
void div_terms(vector<uint64_t>& terms, uint64_t vars, vector<uint64_t>& result) {
    result.resize(terms.size());
    uint64_t rvars = vars ^ ~0ull; //flip all vars
    for (size_t i = 0; i < terms.size(); i++) {
        result[i] = terms[i] & rvars;
    }
}


/*
Core calculation to get the kernels out of an expression. 
*/
void get_kernels(vector<uint64_t>& terms, vector<pair<vector<uint64_t>, uint64_t> >& kernels ) {
    uint64_t vars = get_all_vars(terms);
    for (uint64_t curr = 1; curr <= vars; curr <<= 1) {
        if ((curr & vars) == 0) {
            continue;
        }
        vector<uint64_t> curr_terms, curr_div_terms;
        for (uint64_t uterm : terms) {
            if ((uterm & curr) != 0ull) {
                curr_terms.push_back(uterm);
            }
        }
        if (curr_terms.size() > 1) {
            uint64_t new_kernel = 0ull;
            uint64_t new_co = get_common_vars(curr_terms); // calculate the new co-kernel
            div_terms(curr_terms, new_co, curr_div_terms);//divide cubes with new co-kernel
            kernels.push_back(pair<vector<uint64_t>, uint64_t>(curr_div_terms, new_co));
            get_kernels(curr_div_terms, kernels);
        }
    }

}

特别是消除多次出现的内核效率不高,因为它只是在最后发生。通常,您会更早地执行此操作,并防止多次计算。

此实现从 stdin 获取输入,并将结果写入 stdout。因此,在将其用作家庭作业时,您可以更改它以使用文件。

认为其他实现,如计算发生次数并利用它也是可能的。

评论

0赞 max Chen 1/4/2023
由于我对C++不太了解,所以我不理解您代码的某些部分。例如,我没有学过uint64_t。但是,在谷歌搜索之后,我明白它只是由 .(我不知道这是否正确。XD) 对于其他部分,我只是用谷歌搜索它们,最后一点一点地了解它们。总之,谢谢你回复我,祝你有美好的一天!chartypedef
0赞 sim 1/4/2023
好吧,uint64_t更像是一个由 8 个字符组成的数组。