提问人:max Chen 提问时间:1/3/2023 更新时间:1/4/2023 访问量:248
编写一个C++程序来查找布尔表达式的内核
Write a C++ program to find kernel in boolean expression
问:
我是一名大学生,我有一个编程作业,要求我们在布尔表达式中找到内核。老师提供的文档有一个示例伪代码来指导我们如何编写程序。伪代码如下。
// 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
我不遵守上面的伪代码。
- 首先,我将它们分解为单个项,并将它们推入一个称为项的两维向量中。
terms[expression number][how many terms in one expression]
- 第二,我打电话给find_kernels。基金会计算每个字母在一个表达式中出现的次数。PS:只会出现A、B、C、D、E、G。
- 第三,取出出现次数最多的字母。例如:A、AB、ABC...
- 然后,在同一表达式的每个术语中消除它们。如果术语没有这些字母,则直接删除该术语。
- 继续做同样的事情......
但是,问题是,如果 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.
答:
一般来说,你应该清楚你要解决的问题和应用的定义。否则,你总是会遇到严重的麻烦。 在这里,您要计算 SOP 中给出的布尔表达式的内核(乘积求和形式,例如 abc+cde)。
布尔表达式 F 的内核是无立方体表达式,当您将 F 除以单个立方体时会产生该表达式。
这个单一的多维数据集称为共核。(这是伪代码中)co
从无立方体表达式中,不能因式分解出不留余数的单个立方体。
例子
- F=ae+be+cde+ab
Kernel Co-Kernel
{a,b,cd} e
{e,b} a
{e,cd} b
{ae,be, cde, ab} 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。因此,在将其用作家庭作业时,您可以更改它以使用文件。
认为其他实现,如计算发生次数并利用它也是可能的。
评论
char
typedef
评论
let co = cube that results from intersection of all cubes
exp1 = ab + ac + d
exp2 = ab + ac + e
ab + ac