Reputation: 8653
Suppose next()
is the function that generates this series:
8
9
16
25
27
32
36
49
64
81
Which is
i=2,3,....
j=2,3,....
f(n) = minimum( pow(i,j) ) && f(n) > f(n-1)
I could come up with this O(n) code. Is there a O(1) or O(lg n) solution to this?
int m =2, n = 2;
int last =4;
void next() {
int a=m+1,b=n+1;
long t = pow(a,b);
int ma = max(m,n);
//cout<<" max = "<<ma<<endl;
for(int i=2;i<=ma+1;++i){
for(int j=2;j<=ma+1;++j){
if(pow(i,j) > last && pow(i,j) <= t) {
a=i,b=j;
t = pow(i,j);
}
}
}
if(a>m) m=a;
if(b>n) n=b;
last = t;
//cout<<"\t"<<a<<"\t"<<b<<"\t"<<pow(a,b)<<endl;
cout<<" \t"<<pow(a,b)<<endl;
return;
}
}
Note : 1. When I was referring to complexity, I was talking only about one single call to next(). 2. Caching will of course be helpful, but can we think of lg-n space for caching ? Heck, everything is faster with caching. :) 3. I do not know for constant-space complexity if there exist a solution with O(lg-n), it is just my hunch that there could be..
Upvotes: 3
Views: 191
Reputation: 21793
The approach I imagine for this is some kind of lazy construction of a sorted list of all power results via a generator.
Imagine an endless field of x^y results - x in one direction, y in another. Now imagine a path that goes from 2^2 (which we'll say is 1 for simplicity's sake), one east to 3^2, one southwest to 2^3, one south to 2^4, northeast twice, one east, southwest thrice, one south... etc etc, diagonally zig-zagging and moving outwards.
As you're moving through this zig-zag path over all x^ys, add them to an automatically sorted collection, which is O(nlogn) in total. However, we're lazy and want to stop as soon as possible.
When you're asked for the nth largest power, do zig-zags and build the collection, keeping track on each single zig-zag what the lowest value produced was. If the lowest value was greater than everything in the list below the nth place in the list currently, we know that we'll never insert a lower number into the list that would change the result we'd return - so we return it.
In addition, if you're asked for the nth largest power, you can check right away if the lowest value is low enough to change the value and immediately return if it's not.
This algorithm is suboptimal because the powers grow faster in one direction than in the other - so a path that was biased to cover more values in the slower growing direction than the faster growing direction would need to search less values to return the nth largest power.
Upvotes: 0
Reputation:
Example python code, obviously not perfect, but does show how to do a lazy iteration. Memory usage is O(number of items served), and for time, multiply by a log of that.
import heapq
def power_seq(min_b, min_p):
heap = []
in_heap_table = {}
cur_b, cur_p = min_b, min_p
heapq.heappush(heap, (power_value(cur_b, cur_p), cur_b, cur_p))
in_heap_table[power_value(cur_b, cur_p)] = True
while True:
power, cur_b, cur_p = heapq.heappop(heap)
yield power,cur_b, cur_p
new_b = cur_b + 1
new_p = cur_p + 1
if power_value(new_b, cur_p) not in in_heap_table:
heapq.heappush(heap, (power_value(new_b, cur_p), new_b, cur_p))
in_heap_table[power_value(new_b, cur_p)] = True
if power_value(cur_b, new_p) not in in_heap_table:
heapq.heappush(heap, (power_value(cur_b, new_p), cur_b, new_p))
in_heap_table[power_value(cur_b, new_p)] = True
# Can be made O(log p) if we want.
def power_value(b,p):
power = 1
while p >= 1:
power = power*b
p = p-1
return power
def main():
count = 0
for tup in power_seq(2,2):
print tup
count += 1
if count > 30:
break
if __name__ == "__main__":
main()
Upvotes: 1
Reputation: 383
(I don't know whether you aware of the fact your question is about the series of perfect numbers)
A brute force algorithm that MIGHT be faster: Generate all possible perfect powers in a given range and insert them into a sorted list. However, I don't know how many duplicates you have to expect. Definitely needs more memory than your approach but you didn't give any constraints :)
Upvotes: 0