[英]Write a C++ program to find kernel in boolean expression
我是一名大学生,我有一个编程作业要求我们在 boolean 表达式中找到内核。 老师提供的文档中有一个示例伪代码来指导我们如何编写程序。 伪代码如下。
// 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}。 但是co是什么??
我也写另一个程序
#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
我不遵守上面的伪代码。
terms[expression number][how many terms in one expression]
但是,问题是,如果 F1 是 abc+abd+bcd,我应该 output ac+ad+cd c+d a+c a+d,但我的程序只会 output ac+ad+cd,导致 abc+abd+ bcd = b(ac+ad+cd) >> 下一轮 ac+ad+cd。 a、c、d都出现了两次,所以一并删除。 精光。
Any suggestion to my code or further explination of the pseudo code will be appreciate. Thank you.
一般来说,您应该清楚要解决的问题和应用的定义。 否则你总会遇到严重的麻烦。 此处您要计算 SOP 中给出的 boolean 表达式的内核(乘积求和形式,例如 abc+cde)。
boolean 表达式 F 的 kernel 是一个无立方体的表达式,当您将 F 除以单个立方体时得到该表达式。 那个单一的立方体被称为共同内核。 (这是伪代码中的co
)
从无立方体的表达式中,您无法分解出不留余数的单个立方体。
例子
Kernel Co-Kernel
{a,b,cd} e
{e,b} a
{e,cd} b
{ae,be, cde, ab} 1
Kernel Co-Kernel
{a,b} c
{ac, bc, d} e
如您所见,协核是您消除的变量加上在包含该变量的多维数据集中出现的所有其他公共变量。
要实现这一点,您现在对每个变量递归地应用此过程并存储您创建的所有 kernel。 基本上就是这样!
实际上,对于一个实现,我建议使用一些更方便的术语编码而不是字符串。 由于您的输入似乎只有单字母变量,您可以将它 map 转换为 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。 因此,您可能会在将其用作作业时将其更改为使用文件。
认为其他实现,如计算出现次数和利用它也是可能的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.