Programming Forums
User Name Password Register
 

RSS Feed
FORUM INDEX | TODAY'S POSTS | UNANSWERED THREADS | ADVANCED SEARCH

Reply
 
Thread Tools Display Modes
Old Jun 2nd, 2008, 4:21 PM   #1
Sane
Programming Guru
 
Sane's Avatar
 
Join Date: Apr 2005
Posts: 1,825
Rep Power: 5 Sane will become famous soon enough
SMAC #1 [05-08] - Ranged Reach (Senior)

Attached to this post is the test data for Ranged Reach (Advanced).


This problem requires a brute forced solution with some careful optimizations.

Since the input is restricted to [a,b] = [1,100000] and k=50, it is okay to try every possibile n value, and count the number of n values which produce an nRk within the given range. But taking the solution from Junior and modifying it for just that is not enough. We need to be a little bit smarter.

The first optimization that we can use is the Prime Sieve of Eratosthenes. This is a relatively intuitive prime number generator that I'm sure you could come up on your own with a little bit of thought.

Essentially, you start by considering every number to be prime (except for 0 and 1). Since no multiples of 2 can be prime, you unmark all multiples of 2, starting at the first prime+2 (4). Since no multiples of 3 can be prime, you unmark all multiples of 3, starting at the next prime+3 (6). Since no multiples of 5 can be prime, you unmark all multiples of 5, starting at the next prime+5 (10).

And so on and so forth...

We repeat this process up until the square root of the maximum range (sqrt(100,000)), and what we end up with is a very efficient way of precalculating all potential prime numbers. From now on, all primality tests become a constant time operation involving indexing a location in an array.

Here is my implementation of the Sieve of Eratosthenes:

#define MAXP 100001
int isprime[MAXP];

void genprimes() {
    int i, p=2;
    for(i=0; i<MAXP; i++) primes[i] = 1;
    primes[0] = 0;
    primes[1] = 0;
    while(p*p < MAXP) {
        for(i=p*2; i<MAXP; i+=p) 
            primes[i] = 0;
        while(!primes[++p]) continue;
    }
}

Here is our new and improved primality test:

#define isprime(x) ((x)<0?0:primes[(x)])

And here is the Wikipedia article on the Sieve of Eratosthenes:

http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes


The other necessary optimization is a cache within the original reach function. Here is the original solution from Junior:

int reach(int n, int i, int k) {
    int r;
    if(i>k) return n;
    
    r = max(reach(n+i, i+1, k),   // Option #1
            reach(n-i, i+1, k));  // Option #2
    if(isprime(n))                // Option #3
        r = max(reach(n*2, i+1, k),  r);
    
    return r; // Return the maximum possible value from all 3 options
}

We now use a "trick" identical to that of Dynamic Programming. We solve repeated subproblems by never repeating the same function call twice. But since the numbers can be negative, and there are a couple restrictions on the limits, it's simpler to stick with recursion+caching rather than iterative DP. A recursive function with a cache will have the same run-time complexity as an iterative DP function, except it will be slowed down a fair bit due to the overhead of recursion. The difference is negligible in this case.

Observe that every time we call reach(n,i,k), the result will always be distinct for the parameters (n,i) (since k is constant). That means we can wrap the function with a cache which takes values (n,i) and returns the result if it has already been calculated.

int reach(int n, int i, int k) {
    int x, y, z;
    
    if(n<MIN)  return low-1;  // nRk will be outside range
    if(n>high) return high+1; // nRk will be outside range
    if(i>k)    return n;
    
    if(cache(n,i) != NIL) return cache(n,i);
    
    x = reach(n+i, i+1, k);
    y = reach(n-i, i+1, k);
    if(isprime(n)) z = reach(n*2, i+1, k);
    else           z = NIL;

    return cache(n,i) = max(max(x,y),z);
}


With the number of subproblems proportional to O((max_n - min_n)(k)), that corresponds to about 5 million operations for the worst case, which should run in time.


The last thing that needs addressing is the maxmimum and minimum n values that could possibly produce an nRk within [a, b]. The purpose of this is three-fold: to determine where we should stop the reach function, to determine the constraints of our cache, and to determine over what range we need to test each n value.

Firstly, the lower bound of the range is always a >= 1. So, we only need to consider n values which can possibly achieve an nRk > 0.

If n is negative, its nRk will always be calculated by increasing n by 1, 2, 3, ... k, until it's positive. Why is this true? Because negative numbers are not prime, and the only other option would cause the number to become more negative.

Therefore, given k, the minimum n is the negative sum of the arithmetic sequence from 1 .. k, which is approximately -1,500 for k=50. Thus, any n<-1500 could never possibly have an nRk >= a.

The maximum n is always the value of b minus the arithmetic sequence from 1 .. k. But instead of wasting brain cells and risking off-by-1 errors by mathematically determining this value, it suffices to consider the maximum n to equal b. If n were any higher than b, its nRk would be > b (proof of this should be intuitive). Thus, any n>b could never possibly have an nRk <= b.

With the maximum and minimum n in place, the solution fits together nicely. It may be somewhat repulsive, but it gets the job done in time:


C Syntax (Toggle Plain Text)
  1. #include <stdio.h>
  2. #include <math.h>
  3.  
  4. #define MIN -1500
  5. #define SIZE 105001
  6. #define MAXP 100001
  7. #define NIL -1000000
  8. #define K 51
  9.  
  10. #define cache(n,i) (docache[(n)-MIN][(i)])
  11. #define isprime(x) ((x)<0?0:primes[(x)])
  12. #define max(a,b) ((a)>(b)?(a):(b))
  13.  
  14. int primes[MAXP];
  15. int docache[SIZE][K];
  16. int low, high;
  17.  
  18. int reach(int n, int i, int k) {
  19. int x, y, z;
  20.  
  21. if(n<MIN) return low-1; // nRk will be outside range
  22. if(n>high) return high+1; // nRk will be outside range
  23. if(i>k) return n;
  24.  
  25. if(cache(n,i) != NIL) return cache(n,i);
  26.  
  27. x = reach(n+i, i+1, k);
  28. y = reach(n-i, i+1, k);
  29. if(isprime(n)) z = reach(n*2, i+1, k);
  30. else z = NIL;
  31.  
  32. return cache(n,i) = max(max(x,y),z);
  33. }
  34.  
  35. void genprimes() {
  36. int i, p=2;
  37. for(i=0; i<MAXP; i++) primes[i] = 1;
  38. primes[0] = 0;
  39. primes[1] = 0;
  40. while(p*p < MAXP) {
  41. for(i=p*2; i<MAXP; i+=p)
  42. primes[i] = 0;
  43. while(!primes[++p]) continue;
  44. }
  45. }
  46.  
  47. int main() {
  48. int a, b, i, x, k, c;
  49.  
  50. freopen("ranger.in", "rt", stdin);
  51. freopen("ranger.out", "wt", stdout);
  52.  
  53. scanf("%d %d %d", &low, &high, &k);
  54. for(a=0; a<SIZE; a++)
  55. for(b=0; b<K; b++)
  56. docache[a][b] = NIL;
  57.  
  58. genprimes();
  59. for(c=0, i=MIN; i<=high; i++) {
  60. x = reach(i, 1, k);
  61. if(x >= low && x <= high) c++;
  62. }
  63.  
  64. printf("%d\n", c);
  65. return 0;
  66. }
Attached Files
File Type: zip ranger.zip (1.7 KB, 1 views)
Sane is online now   Reply With Quote
Reply

Bookmarks

Tags
smac advanced solutions

« Previous Thread in Forum | Next Thread in Forum »

Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
SMAC #1 [05-08] - Results Sane Software Design and Algorithms 6 Jun 2nd, 2008 4:56 PM
SMAC #1 [05-08] - Hurtles (Senior) Sane Software Design and Algorithms 0 Jun 1st, 2008 3:59 PM
Ideas for my Senior Project? joshbax88 Java 1 Apr 26th, 2008 5:57 AM
Architect / Senior Programmer : Asp.net / C# techsupport C# 3 Mar 7th, 2006 9:47 AM
Senior Capstone-Spying Screen Saver Hounder Project Ideas 23 Dec 16th, 2005 7:00 PM




DaniWeb IT Discussion Community
All times are GMT -5. The time now is 4:29 PM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC