NOTE: This is NOT homework.
I want to come up with the right approach to designing the right algorithm to deal with this simple problem.
I have a state (representable with a positive integer) which changes over time. I have another value which is a constant specific state (representable with a particular positive integer) which the first state may become equal to.
It is best illustrated thus:
// this is C pseudocode
int things_happen(int *value) {
... // value possibly gets changed!
}
const int y = VALUE_Y_CONST;
int x = y; // to simplify things we assume x starts out equal to y
while (things_happen(&x)) {
// I am now interested in changes to x with respect to y.
if (/* expression of interest */) {
x_is_changed(); // I want to know whenever x is no longer y
}
if (/* another expression of interest */) {
x_is_back(); // and I want to know whenever x becomes equal to y again
}
}
How do I go about determining when I should be calling x_is_changed() and x_is_back()?
I have run into this situation several times by now, when trying to program things. Every time, the solution I come up with looks unreasonably complicated, and often there are bugs.
So far, my solutions require me to create a third variable, which I use to cache the value of x at the bottom of that while loop. It allows me to know which value my x has changed from. With this knowledge I then use what seems like entirely too many conditional statements:
int x_cache = y;
while(things_happen(&x)) {
if (x_cache != x) {
if (x == y && x_cache != y)
x_is_back();
else if (x != y && x_cache == y)
x_is_changed();
x_cache = x;
}
}
That is the most succinct possible way I have done it so far. The code is difficult to follow. What I want to know is, isn't there a better algorithm to solve this problem? What kind of approach should I take? I thought I could draw a truth-table, but I can only do that on truth values. I did it with equalities between the 3 variables and got this table:
x_cache == x | x_cache == y | x == y || x_is_changed | x_is_back
||
T T T || F F
T T F || F F
T F T || F F
T F F || F F
F T T || F F
F T F || T F
F F T || F T
F F F || F F
This seems to be the only sort of thing I remember from my logic classes. I noticed that rows 2,3, and 5 are impossibilities due to transitivity. So i definitely seem to be limiting myself if I only consider the equality checking between the values.
Should I continue to come up with propositional variables, and look for particular combinations that reduce my total number of operations? There's gotta be an easier way to do it? The general problem of coming up with the most efficient algorithm for arbitrary conditions is obviously NP-complete (on the number of variables).
Staring at that table some further, crossing out rows 2, 3, and 5, I see that the condition x_cache != x will eliminate the first 4 rows, which is good, then I am left with 3 possibilities. I can see that x_cache == y is equal to x_is_changed at this point, and also x == y is equal to x_is_back.
So that means that I can simplify from above to this:
...
if (x_cache != x) {
if (x == y)
x_is_back();
else if (x_cache == y)
x_is_changed();
x_cache = x;
}
...
I still feel like this is non optimal. I don't think there exist other relational operators that could help in this problem. It might actually be optimal, now that I think about it.
I intuited that rows 2, 3, and 5 are impossible. Without that knowledge I couldn't reduce the problem to so few operations. Is there some mathematical/logical concept that allows me to systematically perform this "pruning"?
I think the simplest form is:
int x_cache = 1;
while(things_happen(&x)) {
if (x_cache != (x==y)) {
if (x == y)
x_is_back();
else
x_is_changed();
x_cache = (x==y);
}
}
Another alternative is
for (;;) {
while (things_happen(&x) && x==y) { }
if (x==y) break;
x_is_changed();
while (things_happen(&x) && x!=y) { }
if (x!=y) break;
x_is_back();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With