/* subseq.cpp - code to generate all inverses mod a prime and * find the longest increasing subsequence in them, then * output this to standard out. */ #include #include #include #include using namespace std; //finds the longest increasing subsequence in an array of integers //input is a pointer to the array, and len is the length of the array // //dynamic programming alrogithm works as follows: T is a set that is //maintained in sorted order and L is an array that contains the //lengths of the longest increasing subsequences in parts of the input //ie. L[10] contains the length of the longest increasing subsequence //of the elements input[0...10]. // //T maintains, in sorted order, the locations of the ends of the longest //increasing subsequences found so far, and only saves those that need to //be saved. For example, if input[10] = 10 and input[20] = 5, and L[10] = L[20], //then is is only necesary to remember L[20], because a subsequence begining with //...5 is guananteed to be at least as long as a subsequence containing ...10, if //the length of the "..." is the same for each. // //apparently, this algorithm can be sped up significantly by replacing //T with something called a van emde boas priority queue. hopefully, this will //be done soon, and i will also "upgrade" the algorithm to compute all increasing //subsequences. int subseqlen(int * input, int len) { int * L = new int[len]; set T; for(int i = 1; i < len; i++) { int m = input[i]; std::pair::iterator, bool> temp = T.insert(m); std::set::iterator mitr = temp.first; //insert m into the set, and look at its predecessor and successor(down a bit) //if is has them. make L[m] := 1 + L[predecessor(m)]. if(mitr != T.begin()) { std::set::iterator prev = --mitr; mitr++; L[m] = L[*prev] + 1; } else L[m] = 1; std::set::iterator temp2 = T.end(); temp2--; //if L[m] := L[successor(m)], erase the successor from the list if(mitr != temp2) { std::set::iterator next = ++mitr; mitr--; if(L[*next] == L[m]) { T.erase(next); } } } std::set::iterator max = T.end(); max--; int MAX = L[*max]; delete [] L; return MAX; } //the main fucntion. easily modified to do various things. normally, //simply starts from a certain number, and finds all inverses, and calls //subseqlen(), then prints out the prime and the length. int main() { int prime = 4500000; //starting point // for(int k = 0; k < 10000; k++) { //to compute a fixed number while(prime < 5000000) { //to compute a range prime = NTL::NextPrime(prime+1); int * inverses = new int[prime]; for(int i = 1; i < prime; i++) { inverses[i] = NTL::InvMod(i, prime); //computing the inverse here uses the extended euclidean algorithm // //it has been suggested by myself and others that a faster way to //find ALL inverses is to find a generator for the field, or just //look at powers of any number, not necessarily a generator. //as soon as these powers loop, the inverses for all numbers in //the sequence of powers can be found. // //this does save some time, but not too much, especially //for small primes, and i have //not been able to find a way to do it that does not require //too much memory to be useful for large primes. // //around 25 or 30 million for the prime, this program //already consumes much memory. a 25 million integer array is //100 million bytes, and thus program already uses over //200 million bytes of memory for data in this range. } std::cout << prime << (char)9; int len = subseqlen(inverses, prime); std::cout << len << endl; delete [] inverses; } return 0; }