D Recurrences
Steven Schveighoffer
schveiguy at yahoo.com
Thu Jun 9 10:49:44 PDT 2011
On Thu, 09 Jun 2011 13:31:18 -0400, Ben Grabham <Evil.Nebster at gmail.com>
wrote:
> On 09/06/11 18:11, Steven Schveighoffer wrote:
>> On Thu, 09 Jun 2011 13:06:54 -0400, Ben Grabham <Evil.Nebster at gmail.com>
>> wrote:
>>
>>> On 09/06/11 17:57, bearophile wrote:
>>>> Ben Grabham:
>>>>
>>>>> import std.range;
>>>>> import std.stdio;
>>>>> int main() {
>>>>> auto a = recurrence!("a[n-1] + a[n-2]")(0,1);
>>>>> int i = 0;
>>>>> foreach(int n; a) {
>>>>> if(i++> 20) break;
>>>>> writefln("%d", n);
>>>>> }
>>>>> return 0;
>>>>> }
>>>>
>>>> This program does something similar to yours (but it doesn't print
>>>> newlines):
>>>>
>>>>
>>>> import std.stdio, std.range;
>>>>
>>>> void main() {
>>>> auto fib = recurrence!q{ a[n-1] + a[n-2] }(0, 1);
>>>> writeln(take(fib, 21));
>>>> }
>>>>
>>>> Bye,
>>>> bearophile
>>>
>>> Yeah, thanks
>>>
>>> I just wanted to post a bit of code which went wrong :P
>>> Didn't look for optimisations.
>>>
>>> Also, how come recurrence isn't properly lazy?
>>> If I define a recurrence and iterate over it twice with foreach, it
>>> takes the same amount of time due to the stack size being set. Is
>>> there a way of defining a lazy list that stores the results when
>>> calculated?
>>
>> That's not lazy, that's caching. lazy is 'calculate this when asked'.
>>
>> You can cache with array:
>>
>> auto cached = array(take(fib, 21));
>> // cached now contains the first 21 elements of fib.
>>
>> -Steve
>
> I tried that, but how can I calculate the values only when I want them?
> Would I have to store them in a linked list? Since if I know that I will
> need 100000 prime numbers, it takes my old pc about 4.7 seconds to
> calculate them. But can I only calculate them when I need them?
It's a recurring sequence. As such, each element depends on the previous
n elements. I don't see how you can only calculate them when needed,
unless you want to do some sort of memoization with recursion. recurrence
expects to be traversed in a certain order, it's sort of like dynamic
programming.
The array simply stores all the values needed to an array, so you can
re-access the same values without running through the recurrence.
One thing you could do is using Appender, you can continue a recurrence.
So let's say you need the 5th element of the array:
alias recurrence!q{ a[n-1] + a[n-2] } fib;
Appender!int app;
app.put(take(fib(0, 1), 5));
auto elems = app.data;
auto the5thElement = elems[4];
And now, you need the 10th element:
app.put(take(fib(elems[$-2], elems[$-1]), 10 - elems.length)); // extend
sequence to 10 elements
elems = app.data; // need to re-retrieve the elements
auto the10thElemet = elems[9];
Kind of ugly, but maybe it's close enough to what you want.
> I am running through the recurrence twice so I don't want to calculate
> it twice.
Yes, you only run through it once, then use the array with the cached data
to retrieve what you want. Accessing the array does not recalculate the
data.
The Appender solution simply keeps a running cache of the data. You could
probably abstract out the 'extending' function. In fact, this whole thing
could be abstracted into a 'cached recurrence' type.
-Steve
More information about the Digitalmars-d
mailing list