I wrote two versions, one in C++, using dynamic programming/memoizing, and another in Prolog, not using it, so they are not directly comparable.
C++:
#include <iostream>
#include <vector>
const unsigned long N = 4294967296;
bool terminates(unsigned long x) { // x should be less than N
std::vector<bool> visited(N, false);
while(x != 77) {
if(visited[x])
return false;
visited[x] = true;
x = (x*12367+3466) % N;
}
return true;
}
int main() {
std::cout << terminates(819688) << std::endl; // prints 0
std::cout << terminates(955725) << std::endl; // prints 1
}
This finishes in 7s.
Prolog:
The idea behind the non-memoizing version is to loop only N+1
steps. If it doesn't terminate before then, it doesn't terminate at all, since there are only N
states.
terminates(X, R) :- terminates(X, 4294967297, R).
terminates(77, _, true) :- !.
terminates(_, 0, false) :- !.
terminates(_, I, false) :- I =< 0, !.
terminates(X, I, R) :-
I2 is I-1,
X2 is (X*12367+3466) mod 4294967296,
terminates(X2, I2, R).
The first of these queries takes much longer:
?- terminates(819688, R).
R = false.
?- terminates(955725, R).
R = true.
One could do memoizing in Prolog, obviously, using table
or assertz
, but I think you would run into memory problems much sooner than C++. C++'s vector<bool>
uses just 1 bit per element! (Using unordered_map
here would be a mistake, as it has slower lookup and uses much more memory)