Home > other >  Write a C program to find kernel in boolean expression
Write a C program to find kernel in boolean expression

Time:01-04

I am a college student, and I have a programming assignment asked us to find kernels in boolean expression. The document teacher provide has a sample pseudo code to guide us how to write a program. The pseudo code is as below.

// 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 )
}

But I don.t know what is the meaning of the definition of "co". As what I understand S is those terms that have the variable X. Take "abc abd bcd = b(ac ad cd)" for example, S = {abc, abd, bcd}. But what is co??

I also write another program

#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);
        }
    }
}

input file be like

F1=ace bce de g
F2=abc abd bcd

I don't obey the pseudo code above.

  1. First, I break them into single terms and push them into a two dimention vector called terms.
    terms[expression number][how many terms in one expression]
  2. Second, I call find_kernels. The founction calculate every letters appear how many times in one expression. ps: only a, b, c, d, e, g will appear.
  3. Third, take out the letter that appear the most time. ex: a, ab, abc...
  4. Then, eliminate them in every terms of the same expression. If a terms do not have those letters, then delete the term directly.
  5. Continue doing the same thing....

However, the question is that if F1 is abc abd bcd, I should output ac ad cd c d a c a d, but my program will output ac ad cd only, cause abc abd bcd = b(ac ad cd) >> next round ac ad cd. a, c, d all apear twice, so there are deleted together. Nothing left.

Any suggestion to my code or further explination of the pseudo code will be appreciate. Thank you.

CodePudding user response:

In general you should be clear about the problem you want to solve and the applied definitions. Otherwise you will always run into severe troubles. Here you want to calculate the kernels of a boolean expression given in SOP (sum over products form, e.g., abc cde).

A kernel of a boolean expression F is a cube-free expression that results when you divide F by a single cube. That single cube is called a co-kernel. (This is the co in the pseudo code)

From a cube-free expression you cannot factor out a single cube that leaves behind no remainder.

Examples

  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

As you can see the co-kernel is the variable that you eliminate plus all other common variables that occur in the cubes containing the variable.

To implement that you apply this procedure now recursicely for each variable and store all kernel you create. Essentially thats all!

Practically, for an implementation I would propose to use some more handy encoding of the terms and not strings. As your input seems to only single letter variables you can map it to single bits in uint64 (or even uint32 when only lower case is considered). This will give an straight forward implementation like that (thought, it is optimzed for simplicity not performance):

#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);
        }
    }

}

Especially the elimination of kernels that occur several times is not very efficient, as it just happens at the end. Usually you would do this earlier and prevent multiple calculation.

This implementation takes the inputs from the stdin and writes the results to the stdout. So you might change it for using files when using it as your homework.

Thought other implementations like counting the number of occurences and making use out of that is also possible.

  • Related