Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #107579

Re: Optimizing Memory Allocation in a Simple, but Long Function

Path csiph.com!fu-berlin.de!uni-berlin.de!not-for-mail
From Derek Klinge <schilke.60@gmail.com>
Newsgroups comp.lang.python
Subject Re: Optimizing Memory Allocation in a Simple, but Long Function
Date Sun, 24 Apr 2016 20:12:23 -0700
Lines 269
Message-ID <mailman.63.1461553965.32212.python-list@python.org> (permalink)
References <CAGuRns8KhOFL-a1_a=0hMP=NbktoJw78ih3j4uUSbvkF8bf3pw@mail.gmail.com> <CAPTjJmrQfk20EuoZVMLqKv8F5r=YyKj0nr8iNPw7DWoCEYLmYA@mail.gmail.com> <CAGuRns86c4G5BHX_FF-OBnV_eRVKyngx_Dfp0u7hoHRT0WN++w@mail.gmail.com> <CAPTjJmq5g848-PMTgHOg7sawm+JgA5mSfwDHA-ZZgsmYr6yLCA@mail.gmail.com> <CAGuRns8LsThq3iV-bkdBbg9fZO7s61z4ggBy=YkGDqhaB_J4Xw@mail.gmail.com> <CAGuRns9uvHVBXP79X1Sp8vsW4JpWF-3g1mJoWHOq-cGog61cug@mail.gmail.com> <CAPTjJmpvtP5pj_NiJNXGLmu2QCqSYZNgZjKisRRMRTtzvGHKyQ@mail.gmail.com> <CAHVvXxSwb8=a+b5WQfd3KeXTe8VuL4RMnwu3TSNp8129FJHJGw@mail.gmail.com> <CAGuRns911cXoppsS_8f657UDdCtVFhAzwY7ohafOqrsEuRq7vQ@mail.gmail.com> <CAGuRns9byxLC3pGrkPG8m-qLTzB1+PYxLHNy2=fOHcwWR21x6A@mail.gmail.com>
Mime-Version 1.0
Content-Type text/plain; charset=UTF-8
X-Trace news.uni-berlin.de 2ICoUw2xqToq/ImKcKPMmQeBGEAON+bykMg0/Elcu51g==
Return-Path <schilke.60@gmail.com>
X-Original-To python-list@python.org
Delivered-To python-list@mail.python.org
X-Spam-Status OK 0.000
X-Spam-Evidence '*H*': 1.00; '*S*': 0.00; 'value,': 0.03; 'essentially': 0.04; 'binary': 0.05; 'linear': 0.07; 'smallest': 0.07; 'subject:Function': 0.07; 'accuracy.': 0.09; 'benjamin': 0.09; 'client:': 0.09; 'compute': 0.09; 'falls': 0.09; 'here?': 0.09; 'loop.': 0.09; 'methods,': 0.09; 'okay': 0.09; 'rounding': 0.09; 'skip:\\ 30': 0.09; 'subject:Long': 0.09; 'way:': 0.09; 'python': 0.10; 'def': 0.13; 'suggest': 0.15; 'received:74.125.82.44': 0.15; 'result.': 0.15; '(thank': 0.16; '2016': 0.16; '24,': 0.16; 'accuracy,': 0.16; 'algorithm.': 0.16; 'algorithmic': 0.16; 'bouncing': 0.16; 'decimals?': 0.16; 'definition.': 0.16; 'mean,': 0.16; 'numerically': 0.16; 'quadratic': 0.16; 'received:io': 0.16; 'received:psf.io': 0.16; 'sequence.': 0.16; 'wrote:': 0.16; '&lt;': 0.18; 'nested': 0.18; 'skip:l 30': 0.18; '&gt;': 0.18; 'email addr:gmail.com&gt;': 0.18; 'runs': 0.18; 'math': 0.20; 'algorithm': 0.20; 'fix': 0.21; 'to:2**1': 0.21; '(the': 0.22; '(all': 0.22; 'provided,': 0.22; 'see:': 0.22; 'trying': 0.22; 'am,': 0.23; 'bit': 0.23; 'seems': 0.23; 'accuracy': 0.23; 'this:': 0.23; 'tried': 0.24; 'import': 0.24; 'header:In-Reply-To:1': 0.24; 'mon,': 0.24; 'example': 0.26; 'chris': 0.26; 'error': 0.27; 'compare': 0.27; 'equivalent': 0.27; 'message-id:@mail.gmail.com': 0.27; 'sequence': 0.27; 'skip:e 30': 0.27; 'values': 0.28; 'initial': 0.28; 'calculated': 0.29; 'decimal': 0.29; 'implicitly': 0.29; "people's": 0.29; 'url:wikipedia': 0.29; 'code:': 0.29; "i'm": 0.30; 'classes': 0.30; 'url:wiki': 0.30; 'url:mailman': 0.30; 'code': 0.30; 'e.g.': 0.30; 'putting': 0.30; 'error.': 0.31; 'guess': 0.31; 'skip:s 30': 0.31; 'skip:_ 10': 0.32; 'computing': 0.32; 'run': 0.33; 'point': 0.33; 'class': 0.33; 'problem': 0.33; 'url:python': 0.33; 'achieving': 0.33; 'subject:Simple': 0.33; 'values.': 0.33; 'version:': 0.33; 'similar': 0.33; 'url:listinfo': 0.34; 'changing': 0.34; 'definition': 0.34; 'handle': 0.34; 'previous': 0.34; 'gives': 0.35; 'received:google.com': 0.35; 'i.e.': 0.35; 'mine': 0.35; 'replace': 0.35; 'something': 0.35; 'level': 0.35; 'received:74.125.82': 0.35; 'expected': 0.35; 'step': 0.36; 'but': 0.36; 'list,': 0.36; 'should': 0.36; 'instead': 0.36; 'there': 0.36; 'url:org': 0.36; 'stock': 0.36; 'world': 0.64; 'goal': 0.64; 'due': 0.65; 'between': 0.65; 'limit': 0.65; 'series': 0.65; 'differences': 0.66; 'url:%28': 0.66; 'url:%29': 0.66; 'here': 0.66; 'situation': 0.67; 'skip:\xc2 10': 0.67; 'url:%1': 0.67; 'finally': 0.70; 'increase': 0.73; 'increasing': 0.76; 'accurately': 0.84; 'actually,': 0.84; 'confusing': 0.84; 'oscar': 0.84; 'quicker': 0.84; 'doubling': 0.91; 'steps.': 0.91; 'increases': 0.93; 'instantly': 0.93; 'url:%2b': 0.95
DKIM-Signature v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:from:date:message-id:subject:to; bh=idieKk0mmEu7pyXAhry6qPrPHsooeBSHo3yVSDycyfw=; b=WD8VCXfuK50ZTkboRkS/HXa+sjR4DcqzsAy6kK7l6PZjbAmdA2I0wE49zVokA9paoh QgzfMvrGu7fpLoM9t2+omROgKz4AwAQ7QXmaqaBuRbb8Ie3b+LtYd/0laTitCFexzm06 zfj+Kd3yY5RQSC+EA3/1CR1oeblWWkpJjtuPqjkUAHsrYwtNMUOV1b0XLj/cArec5jcY CNHtbZ4LMJo8W/D994STOW05XDzPiatg16pfmGYvCINg39nrESkjt9NVkr3cceDBKd7D IbZ0DOQgW7yOUHuNs8lnoKtcpJ4uW6TbOIchknHq7CEGCNegGenQTuTA1toKKPL4xpHy uhig==
X-Google-DKIM-Signature v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to; bh=idieKk0mmEu7pyXAhry6qPrPHsooeBSHo3yVSDycyfw=; b=Adm4VQQzRFh18Ir6i8VJzW0IqZ5uksH9+bta/pC144maKXmsZgjLtybEWezOrpPo34 DWeIAgMG5ygGV9yc5KvH1zP0R6AR6C+/M7FMHwp11wLGNW9F7SCPruAWBENRCzXDLm1g 89/P/wg+m7qKbbKPp6YtW20ULqqrocWSEGQjToorKHhVk5kqZCdNzShbsemt6YsGrfz5 8EOzjCdTOodqBjnDw3ZPA0Vn/Whfky0IV1uJ8VmxJ0HLVz5dYNYIeFZ+srfLeM12AKW4 Gfs8sxDpOh09wsh8E6gz5XiPTIUueunW3+XSxtaPj1nShoYJU1wg8cY3nneMUrfUNAH1 3Wkw==
X-Gm-Message-State AOPr4FXJxUs7UL9jP1wjZbdUM5KP4H7iBSrBdW2kj/SUsCEmIPh98jhSqGuZ/N9fPxHRw5dQpPSWO17RZ89ZFw==
X-Received by 10.28.91.199 with SMTP id p190mr9251219wmb.47.1461553962977; Sun, 24 Apr 2016 20:12:42 -0700 (PDT)
In-Reply-To <CAGuRns911cXoppsS_8f657UDdCtVFhAzwY7ohafOqrsEuRq7vQ@mail.gmail.com>
X-Content-Filtered-By Mailman/MimeDel 2.1.22
X-BeenThere python-list@python.org
X-Mailman-Version 2.1.22
Precedence list
List-Id General discussion list for the Python programming language <python-list.python.org>
List-Unsubscribe <https://mail.python.org/mailman/options/python-list>, <mailto:python-list-request@python.org?subject=unsubscribe>
List-Archive <http://mail.python.org/pipermail/python-list/>
List-Post <mailto:python-list@python.org>
List-Help <mailto:python-list-request@python.org?subject=help>
List-Subscribe <https://mail.python.org/mailman/listinfo/python-list>, <mailto:python-list-request@python.org?subject=subscribe>
X-Mailman-Original-Message-ID <CAGuRns9byxLC3pGrkPG8m-qLTzB1+PYxLHNy2=fOHcwWR21x6A@mail.gmail.com>
X-Mailman-Original-References <CAGuRns8KhOFL-a1_a=0hMP=NbktoJw78ih3j4uUSbvkF8bf3pw@mail.gmail.com> <CAPTjJmrQfk20EuoZVMLqKv8F5r=YyKj0nr8iNPw7DWoCEYLmYA@mail.gmail.com> <CAGuRns86c4G5BHX_FF-OBnV_eRVKyngx_Dfp0u7hoHRT0WN++w@mail.gmail.com> <CAPTjJmq5g848-PMTgHOg7sawm+JgA5mSfwDHA-ZZgsmYr6yLCA@mail.gmail.com> <CAGuRns8LsThq3iV-bkdBbg9fZO7s61z4ggBy=YkGDqhaB_J4Xw@mail.gmail.com> <CAGuRns9uvHVBXP79X1Sp8vsW4JpWF-3g1mJoWHOq-cGog61cug@mail.gmail.com> <CAPTjJmpvtP5pj_NiJNXGLmu2QCqSYZNgZjKisRRMRTtzvGHKyQ@mail.gmail.com> <CAHVvXxSwb8=a+b5WQfd3KeXTe8VuL4RMnwu3TSNp8129FJHJGw@mail.gmail.com> <CAGuRns911cXoppsS_8f657UDdCtVFhAzwY7ohafOqrsEuRq7vQ@mail.gmail.com>
Xref csiph.com comp.lang.python:107579

Show key headers only | View raw


Actually, I'm not trying to speed it up, just be able to handle a large
number of n.
(Thank you Chris for the suggestion to use xrange, I am on a Mac using the
stock Python 2.7)

I am looking at the number of iterations of linear approximation that are
required to get a more accurate representation.
My initial data suggest that to get 1 more digit of e (the difference
between the calculated and expected value falls under 10**-n), I need a
little more than 10 times the number of iterations of linear approximation.

I actually intend to compare these to other methods, including limit
definition that you provided, as well as the geometric series definition.

I am trying to provide some real world data for my students to prove the
point that although there are many ways to calculate a value, some are much
more efficient than others.

I tried your recommendation (Oscar) of trying a (1+1/n)**n approach, which
gave me very similar values, but when I took the difference between your
method and mine I consistently got differences of ~10**-15. Perhaps this is
due the binary representation of the decimals?

Also, it seems to me if the goal is to use the smallest value of n to get a
particular level of accuracy, changing your guess of N by doubling seems to
have a high chance of overshoot. I found that I was able to predict
relatively accurately a value of N for achieving a desired accuracy. By
this I mean, that I found that if I wanted my to be accurate to one
additional decimal place I had to multiply my value of N by approximately
10 (I found that the new N required was always < 10N +10).

Derek

On Sun, Apr 24, 2016 at 4:45 PM, Derek Klinge <schilke.60@gmail.com> wrote:

> Actually, I'm not trying to speed it up, just be able to handle a large
> number of n.
> (Thank you Chris for the suggestion to use xrange, I am on a Mac using the
> stock Python 2.7)
>
> I am looking at the number of iterations of linear approximation that are
> required to get a more accurate representation.
> My initial data suggest that to get 1 more digit of e (the difference
> between the calculated and expected value falls under 10**-n), I need a
> little more than 10 times the number of iterations of linear approximation.
>
> I actually intend to compare these to other methods, including limit
> definition that you provided, as well as the geometric series definition.
>
> I am trying to provide some real world data for my students to prove the
> point that although there are many ways to calculate a value, some are much
> more efficient than others.
>
> Derek
>
> On Sun, Apr 24, 2016 at 2:55 PM, Oscar Benjamin <
> oscar.j.benjamin@gmail.com> wrote:
>
>> On 24 April 2016 at 19:21, Chris Angelico <rosuav@gmail.com> wrote:
>> > On Mon, Apr 25, 2016 at 4:03 AM, Derek Klinge <schilke.60@gmail.com>
>> wrote:
>> >> Ok, from the gmail web client:
>> >
>> > Bouncing this back to the list, and removing quote markers for other
>> > people's copy/paste convenience.
>> >
>> > ## Write a method to approximate Euler's Number using Euler's Method
>> > import math
>> >
>> > class EulersNumber():
>> >     def __init__(self,n):
>> >         self.eulerSteps = n
>> >         self.e    = self.EulersMethod(self.eulerSteps)
>> >     def linearApproximation(self,x,h,d): # f(x+h)=f(x)+h*f'(x)
>> >         return x + h * d
>> >     def EulersMethod(self, numberOfSteps): # Repeate linear
>> > approximation over an even range
>> >         e = 1                                                    # e**0
>> = 1
>> >         for step in range(numberOfSteps):
>> >             e = self.linearApproximation(e,1.0/numberOfSteps,e) # if
>> > f(x)= e**x, f'(x)=f(x)
>> >         return e
>> >
>> >
>> > def EulerStepWithGuess(accuracy,guessForN):
>> >     n = guessForN
>> >     e = EulersNumber(n)
>> >     while abs(e.e - math.e) > abs(accuracy):
>> >         n +=1
>> >         e = EulersNumber(n)
>> >         print('n={} \te= {} \tdelta(e)={}'.format(n,e.e,abs(e.e -
>> math.e)))
>> >     return e
>> >
>> >
>> > def EulersNumberToAccuracy(PowerOfTen):
>> >     x = 1
>> >     theGuess = 1
>> >     thisE = EulersNumber(1)
>> >     while x <= abs(PowerOfTen):
>> >         thisE = EulerStepWithGuess(10**(-1*x),theGuess)
>> >         theGuess = thisE.eulerSteps * 10
>> >         x += 1
>> >     return thisE
>> >
>> >
>> >> To see an example of my problem try something like
>> EulersNumberToAccuracy(-10)
>>
>> Now that I can finally see your code I can see what the problem is. So
>> essentially you want to calculate Euler's number in the following way:
>>
>> e = exp(1) and exp(t) is the solution of the initial value problem
>> with ordinary differential equation dx/dt = x and initial condition
>> x(0)=1.
>>
>> So you're using Euler's method to numerically solve the ODE from t=0
>> to t=1. Which gives you an estimate for x(1) = exp(1) = e.
>>
>> Euler's method solves this by going in steps from t=0 to t=1 with some
>> step size e.g. dt = 0.1. You get a sequence of values x[n] where
>>
>>    x[0] = x(0) = 1  # initial condition
>>    x[1] = x[0] + dt*f(x[0]) = x[0] + dt*x[0]
>>    x[2] = x[1] + dt*x[1] # etc.
>>
>> In order to get to t=1 in N steps you set dt = 1/N. So simplifying
>> your code (all the classes and functions are just confusing the
>> situation here):
>>
>> N = 1000
>> dt = 1.0 / N
>> x = 1
>> for n in range(N):
>>     x = x + dt*x
>> print(x)
>>
>> When I run that I get:
>> 2.71692393224
>>
>> Okay that's great but actually you want to be able to set the accuracy
>> required and then steadily increase N until it's big enough to achieve
>> the expected accuracy so you do this:
>>
>> import math
>>
>> error = 1
>> accuracy = 1e-2
>>
>> N = 1
>> while error > accuracy:
>>     dt = 1.0 / N
>>     x = 1
>>     for n in range(N):
>>         x = x + dt*x
>>     error = abs(math.e - x)
>>     N += 1
>> print(x)
>>
>> But what happens here? You have a loop in a loop. The inner loop takes
>> n over N values. The outer loop takes N from 1 up to Nmin where Nmin
>> is the smallest value of N such that we achieve the desired accuracy.
>>
>> This is a classic case of a quadratic performance algorithm. As you
>> make the accuracy smaller you're implicitly increasing Nmin. However
>> the algorithmic performance is quadratic in Nmin i.e. O(Nmin**2). The
>> problem is the nested loops. If you have an outer loop that increases
>> the length of an inner loop by 1 at each step then you have a
>> quadratic algorithm akin to:
>>
>> # This loop is O(M**2)
>> for n in range(N):
>>     for N in range(M):
>>         # do stuff
>>
>> To see that it is quadratic see:
>>
>>     https://en.wikipedia.org/wiki/Triangular_number
>>
>> The simplest fix here is to replace N+=1 with N*=2. Instead of
>> increasing the number of steps by one if the accuracy is not small
>> enough then you should double the number of steps. That will give you
>> an O(Nmin) algorithm.
>>
>>
>> https://en.wikipedia.org/wiki/1/2_%2B_1/4_%2B_1/8_%2B_1/16_%2B_%E2%8B%AF
>>
>> A better method is to do a bit of algebra before putting down the code:
>>
>>     x[1] = x[0] + h*x[0] = x[0]*(1+h) = x[0]*(1+1/N) = (1+1/N)
>>     x[2] = x[1]*(1+1/N) = (1+1/N)**2
>>     ...
>>     x[n] = (1 + 1/n)**n
>>
>> So doing the loop for Euler's method is equivalent to just writing:
>>
>>     x = (1 + 1.0/N)**N
>>
>> This considered as a sequence in N is well known as a sequence that
>> converges to e. In fact this is how the number e was first discovered:
>>
>>
>> https://en.wikipedia.org/wiki/E_%28mathematical_constant%29#Compound_interest
>>
>> Python can compute this much quicker than your previous version:
>>
>> N = 1
>> for _ in range(40):
>>     N *= 2
>>     print((1 + 1.0/N) ** N)
>>
>> Which runs instantly and gives:
>>
>> 2.25
>> 2.44140625
>> 2.56578451395
>> 2.63792849737
>> 2.67699012938
>> 2.69734495257
>> 2.70773901969
>> 2.71299162425
>> 2.71563200017
>> 2.71695572947
>> 2.71761848234
>> 2.71795008119
>> 2.71811593627
>> 2.71819887772
>> 2.71824035193
>> 2.7182610899
>> 2.71827145911
>> 2.71827664377
>> 2.71827923611
>> 2.71828053228
>> 2.71828118037
>> 2.71828150441
>> 2.71828166644
>> 2.71828174745
>> 2.71828178795
>> 2.71828180821
>> 2.71828181833
>> 2.7182818234
>> 2.71828182593
>> 2.71828182719
>> 2.71828182783
>> 2.71828182814
>> 2.7182818283
>> 2.71828182838
>> 2.71828182842
>> 2.71828182844
>> 2.71828182845
>> 2.71828182845
>> 2.71828182846
>> 2.71828182846
>>
>> So your method is computing the above numbers but in a slower way that
>> also has more potential for rounding error. The error here is 1e-13
>> for the last numbers in this sequence. But N=2**40 so your Euler
>> method would need approximately 10**12 iterations in your inner loop
>> to get the same result. That's going to be slow even if you don't use
>> a quadratic algorithm.
>>
>> --
>> Oscar
>> --
>> https://mail.python.org/mailman/listinfo/python-list
>>
>
>

Back to comp.lang.python | Previous | NextNext in thread | Find similar | Unroll thread


Thread

Re: Optimizing Memory Allocation in a Simple, but Long Function Derek Klinge <schilke.60@gmail.com> - 2016-04-24 20:12 -0700
  Re: Optimizing Memory Allocation in a Simple, but Long Function Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2016-04-25 19:39 +1200
    Re: Optimizing Memory Allocation in a Simple, but Long Function Oscar Benjamin <oscar.j.benjamin@gmail.com> - 2016-04-25 14:45 +0100
    Re: Optimizing Memory Allocation in a Simple, but Long Function Derek Klinge <schilke.60@gmail.com> - 2016-04-25 14:35 +0000
    Re: Optimizing Memory Allocation in a Simple, but Long Function Oscar Benjamin <oscar.j.benjamin@gmail.com> - 2016-04-26 10:54 +0100

csiph-web