I'm working with some tools, and the only way it can determine if a particular transaction is successful is if it passes various checks. However, it is limited in the way that it can only do one check at a time, and it must be sequential. Everything must be computed from left to right.
For example,
A || C && D
It will be computed with A || C first, and then the result will be AND'ed with D.
It gets tougher with parenthesis. I am unable to compute an expression like this, since B || C would need to be compututed first. I cannot work with any order of operations;
A && ( B || C)
I think I've worked this down to this sequential boolean expression,
C || B && A
Where C || B is computed first, then that result is AND'd with A
Can all boolean expressions be successfully worked into a sequential boolean expression? (Like the example I have)
The answer is no:
Consider A || B && C || D which has the truth table:
A | B | C | D |
0 | 0 | 0 | 0 | 0
0 | 0 | 0 | 1 | 0
0 | 0 | 1 | 0 | 0
0 | 0 | 1 | 1 | 0
0 | 1 | 0 | 0 | 0
0 | 1 | 0 | 1 | 1
0 | 1 | 1 | 0 | 1
0 | 1 | 1 | 1 | 1
1 | 0 | 0 | 0 | 0
1 | 0 | 0 | 1 | 1
1 | 0 | 1 | 0 | 1
1 | 0 | 1 | 1 | 1
1 | 1 | 0 | 0 | 0
1 | 1 | 0 | 1 | 1
1 | 1 | 1 | 0 | 1
1 | 1 | 1 | 1 | 1
If it were possible to evaluate sequentially there would have to be a last expression which would be one of two cases:
Case 1:
X || Y such that Y is one of A,B,C,D and X is any sequential boolean expression.
Now, since there is no variable in A,B,C,D where the entire expression is true whenever that variable is true, none of:
X || A
X || B
X || C
X || D
can possibly be the last operation in the expression (for any X).
Case 2:
X && Y: such that Y is one of A,B,C,D and X is any sequential boolean expression.
Now, since there is no variable in A,B,C,D where the entire expression is false whenever that variable is false, none of:
X && A
X && B
X && C
X && D
can possibly be the last operation in the expression (for any X).
Therefore you cannot write (A || B) && (C || D) in this way.
The reason you are able to do this for some expressions, like: A && ( B || C) becoming C || B && A is because that expression can be built recursively out of expressions which have one of the two properties above:
IE.
The truth table for A && ( B || C) is:
A | B | C |
0 | 0 | 0 | 0
0 | 0 | 1 | 0
0 | 1 | 0 | 0
0 | 1 | 1 | 0
1 | 0 | 0 | 0
1 | 0 | 1 | 1
1 | 1 | 0 | 1
1 | 1 | 1 | 1
Which we can quickly see has the property that it is false whenever A is 0. So Our expression Could be X && A.
Then we take A out of the truth table and look at only the rows where A is 1 is the original:
B | C
0 | 0 | 0
0 | 1 | 1
1 | 0 | 1
1 | 1 | 1
Which has the property that it is True whenever B is 1 (or C, we can pick here). So we can write the expression as
X || B and the entire expression becomes X || B && A
Then we reduce the table again to the portion where B was 0 and we get:
C
0 | 0
1 | 1
X is just C. So the final expression is C || B && A
This is a problem of rewriting an expression so that no parentheses occur on the right. Logical AND (∧) and OR (∨) are both commutative:
A ∧ B = B ∧ A
A ∨ B = B ∨ A
So you can rewrite any expression of the form “X a (Y)” as “(Y) a X”:
A ∧ (B ∧ C) = (A ∧ B) ∧ C
A ∧ (B ∨ C) = (B ∨ C) ∧ A
A ∨ (B ∧ C) = (B ∧ C) ∨ A
A ∨ (B ∨ C) = (B ∨ C) ∨ A
They are also distributive, by the following laws:
(A ∧ B) ∨ (A ∧ C)
= A ∧ (B ∨ C)
= (B ∨ C) ∧ A
(A ∨ B) ∧ (A ∨ C)
= A ∨ (B ∧ C)
= (B ∧ C) ∨ A
So many Boolean expressions can be rewritten without parentheses on the right. But, as a counterexample, there is no way to rewrite an expression such as (A ∧ B) ∨ (C ∧ D) if A ≠ C, because of the lack of common factors.
Can't you just do this:
(( A || C ) && D)
and for your second example:
((( A && C ) || B ) && A )
Would that work for you?
Hope that helps...
You'll hit problems if you need to do something like (A || B) && (C || D) unless you can store the intermediate values for later use.
If you're allowed to construct more than one chain and try them all until either one of them passes or they all fail (so each chain is effectively ORed with the next) then I should think you can handle any combination. The example above would become (where each line is a separate query):
(A && C) ||
(A && D) ||
(B && C) ||
(B && D)
However, for a very complex check this could easily get out of hand!
Related
I've been struggling with boolean simplification in class, and took it to practice some more at home. I found a list of questions, but they don't have any answers or workings. This one I'm stuck on, if you could answer clearly showing each step I'd much appreciate:
Q=A.B.(~B+C)+B.C+B
I tried looking for a calculator to give me the answer and then to work out how to get to that, but I'm lost
(I'm new to this)
Edit: ~B = NOT B
I've never done this, so I'm using this site to help me.
A.B.(B' + C) = A.(B.B' + B.C) = A.(0 + B.C) = A.(B.C)
So the expression is now A.(B.C) + B.C + B.
Not sure about this, but I'm guessing A.(B.C) + (B.C) = (A + 1).(B.C).
This equals A.(B.C).
So the expression is now A.(B.C) + B.
As A.(B + C) = B.(A.C), the expression is now B.(A.C) + B, which equals (B + 1).(A.C) = B.(A.C).
NOTE: This isn't complete yet, so please avoid downvoting as I'm not finished yet (posted this to help the OP understand the first part).
Let's be lazy and use sympy, a Python library for symbolic computation.
>>> from sympy import *
>>> from sympy.logic import simplify_logic
>>> a, b, c = symbols('a, b, c')
>>> expr = a & b & (~b | c) | b & c | b # A.B.(~B+C)+B.C+B
>>> simplify_logic(expr)
b
There are two ways to go about such a formula:
Applying simplifications,
Brute force
Let's look at brute force first. The following is a dense truth table (for a better looking table, look at Wα), enumerating all possible value for a, b and c, alongside the values of the expression.
a b c -- a & b & (~b | c) | b & c | b = Q
0 0 0 0 0 10 1 0 0 0 0 0 = 0
0 0 1 0 0 10 1 1 0 0 1 0 = 0
0 1 0 0 1 01 0 0 1 0 0 1 = 1
0 1 1 0 1 01 1 1 1 1 1 1 = 1
1 0 0 1 0 10 1 0 0 0 0 0 = 0
1 0 1 1 0 10 1 1 0 0 1 0 = 0
1 1 0 1 1 01 1 0 1 0 0 1 = 1
1 1 1 1 1 01 1 1 1 1 1 1 = 1
You can also think of the expression as a tree, which will depend on the precedence rules (e.g. usually AND binds stronger than OR, see also this question on math.se).
So the expression:
a & b & (~b | c) | b & c | b
is a disjunction of three terms:
a & b & (~b | c)
b & c
b
You can try to reason about the individual terms, knowing that only one has to be true (as this is a disjunction).
The last two will be true, if and only if b is true. For the first, this a bit harder to see, but if you look closely: you have now a conjunction (terms concatenated by AND): All of them must be true, for the whole expression to be true, so a and b must be true. Especially b must be true.
In summary: For the whole expression to be true, in all three top-level cases, b must be true (and it will be false, if b is false). So it simplifies to just b.
Explore more on Wolfram Alpha:
https://www.wolframalpha.com/input/?i=a+%26+b+%26+(~b+%7C+c)+%7C+b+%26+c+%7C+b
A.B.(~B+C) + B.C + B = A.B.~B + A.B.C + B.C + B ; Distribution
= A.B.C + B.C + B ; Because B.~B = 0
= B.C + B ; Because A.B.C <= B.C
= B ; Because B.C <= B
I have an expression of the form
A or A and B
Can we represent it more concisely by representing the expression some other way?
As stated the expression may be slightly ambiguous. It can be interpereted in two ways:
(A or A) and B
Obviously A or A is logically equivalent to A, so in this case the entire statement is simply equivalent to A and B
More likely, this is intended to be read as
A or (A and B)
Let's write a truth table for this
A B | A or (A and B) | result
-----------------------------
0 0 | 0 or (0 and 0) | 0
0 1 | 0 or (0 and 1) | 0
1 0 | 1 or (1 and 0) | 1
1 1 | 1 or (1 and 1) | 1
Now you can pretty clearly see, in this case the statement is equivalent to A alone.
For problem simplify(abs(1-b)+abs(1+b)), I want maple to take out the abs and get results for different ranges of b.
Conversion of your expression to Maple's piecewise structure gets you started.
restart;
expr1 := abs(1-b)+abs(1+b);
expr1 := |b - 1| + |1 + b|
convert(expr1, piecewise, b);
/ -2 b b < -1
|
< 2 b < 1
|
\ 2 b 1 <= b
And you can even turn that into a list.
PiecewiseTools:-ToList(%);
[[b < -1, -2 b], [b < 1, 2], [1 <= b, 2 b]]
But for either of the above representations you can notice that some of the information is implicit. The expr1 is identically equal to 2 when b<1 AND the prior condition(s) don't hold. It's implicit that you also need the negation of the first condition in order to get to the second. Maple handles that by proceeding through the conditions until it gets one that's satisfied, when it evaluates a piecewise at a particular point (ie, at a value for b).
We can write a procedure to obtain a more explicit (if somewhat redundant) representation.
F1 := proc(ee, x::name)
local T;
T := PiecewiseTools:-ToList(convert(ee,piecewise,x));
piecewise(seq([`and`(solve(`and`(seq(`not`(T[j,1]),j=1..i-1),
T[i,1]),{x})[]),
T[i,2]][],i=1..nops(T)));
end proc:
And, using that,
F1(expr1, b);
/ -2 b b < -1
|
< 2 -1 <= b and b < 1
|
\ 2 b 1 <= b
PiecewiseTools:-ToList(%);
[[b < -1, -2 b], [-1 <= b and b < 1, 2], [1 <= b, 2 b]]
Observe the difference between that and what we got before. Now the middle condition explicitly expresses the negation of the first.
We could write another procedure to express b in terms of real ranges.
F2 := proc(ee, x::name)
local T;
T := PiecewiseTools:-ToList(convert(ee,piecewise,x));
piecewise(seq([x::solve(`and`(seq(`not`(T[j,1]),j=1..i-1),
T[i,1]),x),
T[i,2]][],i=1..nops(T)));
end proc:
And, using that,
F2(expr1, b);
/ -2 b b::(RealRange(-infinity, Open(-1)))
|
< 2 b::(RealRange(-1, Open(1)))
|
\ 2 b b::(RealRange(1, infinity))
PiecewiseTools:-ToList(%);
[[b::(RealRange(-infinity, Open(-1))), -2 b],
[b::(RealRange(-1, Open(1))), 2], [b::(RealRange(1, infinity)), 2 b]]
Note that those may not each be the widest ranges. We could have alternatively used solve to find b::RealRange(-1,1) for the second condition, since 2 = 2*b when b=1. The expression is continuous at b=1 where the second and third conditions meet. And similarly for the other conditions.
solve(expr1 = -2*b, b);
RealRange(-infinity, -1)
solve(expr1 = 2, b);
RealRange(-1, 1)
solve(expr1 = 2*b, b);
RealRange(1, infinity)
A similar thing holds for the inequality results from F1. Obtaining these wider results with intervals closed at some ends (or non-strict inequalities) could be done using modified versions of the procedures, to use solve instead of just negating all prior conditions at each stage. I haven't done that here.
Let's look at another example, for fun,
expr2 := abs(1-c)+abs(1+c)-abs(3-c)-abs(7+c);
expr2 := |c - 1| + |1 + c| - |c - 3| - |7 + c|
convert(expr2, piecewise, c);
/ 4 c < -7
|
| -2 c - 10 c < -1
|
< -8 c < 1
|
| 2 c - 10 c < 3
|
\ -4 3 <= c
F1(expr2, c);
/ 4 c < -7
|
| -2 c - 10 -7 <= c and c < -1
|
< -8 -1 <= c and c < 1
|
| 2 c - 10 1 <= c and c < 3
|
\ -4 3 <= c
PiecewiseTools:-ToList(%);
[[c < -7, 4], [-7 <= c and c < -1, -2 c - 10],
[-1 <= c and c < 1, -8],
[1 <= c and c < 3, 2 c - 10], [3 <= c, -4]]
F2(expr2, c);
/ 4 c::(RealRange(-infinity, Open(-7)))
|
| -2 c - 10 c::(RealRange(-7, Open(-1)))
|
< -8 c::(RealRange(-1, Open(1)))
|
| 2 c - 10 c::(RealRange(1, Open(3)))
|
\ -4 c::(RealRange(3, infinity))
PiecewiseTools:-ToList(%);
[[c::(RealRange(-infinity, Open(-7))), 4],
[c::(RealRange(-7, Open(-1))), -2 c - 10],
[c::(RealRange(-1, Open(1))), -8],
[c::(RealRange(1, Open(3))), 2 c - 10],
[c::(RealRange(3, infinity)), -4]]
We could plot these results (which I won't inline here).
plots:-display(Array([[plot(expr2,c=-8..4),
plot(convert(expr2,piecewise,c),c=-8..4)],
[plot(F1(expr2,c),c=-8..4),
plot(F2(expr2,c),c=-8..4)]]));
We could even handle other unknowns, although conversion to piecewise might require assumptions.
expr3 := abs(1-c) + abs(1+c) - abs(3-c) - abs(K-c):
convert(expr3, piecewise, c) assuming K>1, K<3;
/ -3 - K c < -1
|
| -1 + 2 c - K c < 1
|
< 4 c - 3 - K c <= K
|
| 2 c - 3 + K c < 3
|
\ 3 + K 3 <= c
F1(expr3, c) assuming K>1, K<3;
/ -3 - K c < -1
|
| -1 + 2 c - K -1 <= c and c < 1
|
< 4 c - 3 - K 1 <= c and c <= K
|
| 2 c - 3 + K K < c and c < 3
|
\ 3 + K 3 <= c
I have 4 variables this was the best I could do, but it returns true if there's 3 true variables.
if(a ^ b ^ c ^ d)
If you're talking C, where booleans are simply 0/1 values, you can use:
a + b + c + d == 1
Otherwise, provided you can use && and ||:
( a && !b && !c && !d) ||
(!a && b && !c && !d) ||
(!a && !b && c && !d) ||
(!a && !b && !c && d)
This simply breaks it down to the four possible cases where each has only one variable set.
Now you can "simplify" that with Karnaugh maps as follows (all blanks are false):
AB
0 0 1 1
0 1 0 1
+---+---+---+---+
CD 00 | | T | T | |
+---+---+---+---+
01 | T | | | |
+---+---+---+---+
10 | T | | | |
+---+---+---+---+
11 | | | | |
+---+---+---+---+
to be:
((a ^ b) && !c && !d) || ((c ^ d) && !a && !b)
but it's a lot harder to understand (and debug) than the simpler expression above, and doesn't really save much so, unless you well versed in boolean algebra, DeMorgan's Theorem, Karnaugh maps and such, stick with the first one.
Just to show that the expressions are equivalent, consider the following C code for checking all possibilities:
#include <stdio.h>
int main (void) {
int a, b, c, d, x, y;
for (a = 0; a < 2; a++) {
for (b = 0; b < 2; b++) {
for (c = 0; c < 2; c++) {
for (d = 0; d < 2; d++) {
x =
( a && !b && !c && !d) ||
(!a && b && !c && !d) ||
(!a && !b && c && !d) ||
(!a && !b && !c && d);
y = ((a ^ b) && !c && !d) || ((c ^ d) && !a && !b);
printf ("%d %d %d %d | %d %d | %s\n",
a, b, c, d, x, y, (x == y) ? "Okay" : "Bad");
}
}
}
}
return 0;
}
This outputs all 16 possibilities, showing that the two expressions generate the same outcome:
0 0 0 0 | 0 0 | Okay
0 0 0 1 | 1 1 | Okay
0 0 1 0 | 1 1 | Okay
0 0 1 1 | 0 0 | Okay
0 1 0 0 | 1 1 | Okay
0 1 0 1 | 0 0 | Okay
0 1 1 0 | 0 0 | Okay
0 1 1 1 | 0 0 | Okay
1 0 0 0 | 1 1 | Okay
1 0 0 1 | 0 0 | Okay
1 0 1 0 | 0 0 | Okay
1 0 1 1 | 0 0 | Okay
1 1 0 0 | 0 0 | Okay
1 1 0 1 | 0 0 | Okay
1 1 1 0 | 0 0 | Okay
1 1 1 1 | 0 0 | Okay
You can write out your expression explicitly. In the long form, for four boolean variables, it would be (!a && !b && !c && d) || (!a && !b && c && !d) || (!a && b && !c && !d) || (a && !b && !c && !d)
For an arbitrary number of variables, for any arbitrary boolean functions, you can use a truth table to get the appropriate boolean expression and the karnaugh map to simplify it.
May be you wanna try ternary operator as the execution time for this is slightly less, even when we consider the best case scenario of the above solutions.
(a?(b?false:(c?false:(d?false:true))):(b?(c?false:(d?false:true)):(c?(d?false:true):(d?true:false))))
I have the following program: (Scala 2.9.2, Java6)
object Forcomp {
def main(args: Array[String]): Unit = {
val xs = List(-1, 0, 1)
val xss = for (a <- xs; b <- xs if a != 0 && b != 0) yield (a,b)
println(xss)
}
}
It produces this output: List((-1,-1), (-1,1), (1,-1), (1,1))
I would have expected it to only filter out values where a and b are both 0 – not all values where either a or b are 0.
I can get the behaviour I want by changing the if-clause to this: if (a,b) != (0,0) – however, should I really have to? Is this a bug or is this intentional behaviour? I, for one, was surprised by this.
The truth table for the filter you have is this:
a==0 | b==0 | (a!=0 && b!=0)
--------------------------------
0 | 0 | 0
0 | 1 | 0
1 | 0 | 0
1 | 1 | 1
whereas the behaviour you say you want is:
a==0 | b==0 | !(a==0 && b==0)
--------------------------------
0 | 0 | 0
0 | 1 | 1
1 | 0 | 1
1 | 1 | 1