0

I'm working through some exercises in my python book and was asked to implement an infinite product to estimate the sine function depending on some amount of iterations k. The code I wrote is the following:

def approx_sin(x,k):
    def u(k):
        if k==0:
           return x
        else:
           return (1-((x**2)/((k**2)*(pi**2))))
    u0=u(0)
    yield u0
    for n in range(1,k+1):
        u0=u(n)*u0
        yield u0

Which works as intended. Now I'm asked to sketch this function on some interval for different values of k, meaning we have to extract some values from the generator. To do this, I create the following function:

def sin_sketch(x,k):
    return list(itertools.islice(approx_sin(x,k),k,k+1))[0]

And I can now plot sin_sketch(x,n) for some linspace x and a given value for n. My question boils down to, there has to be a better/more time efficient way to extract the value produced by itertools.islice(approx_sin(x,k),k-1,k) rather then converting it to a list, then taking its only element. Any tips?

William
  • 163
  • 5
  • 2
    Use `next(islice(....))`? But if you are only interested in the last value, why even `yield` all before that and not just `return` the last? – tobias_k Aug 19 '22 at 08:43
  • which book / which approximation method is this? – FiddleStix Aug 19 '22 at 08:50
  • You don't seem to be using the second argument to your approx_sin function (`k`) so, even if it works, I don't think it works for the reasons you think it does. – FiddleStix Aug 19 '22 at 08:56
  • 1
    @FiddleStix the approximation method is the following: https://math.stackexchange.com/questions/674769/sinx-infinite-product-formula-how-did-euler-prove-it – William Aug 19 '22 at 09:33
  • 1
    @FiddleStix Thank you for pointing out that k was essentially useless, it should be used to set the recursion depth, which is now fixed! – William Aug 19 '22 at 10:19

3 Answers3

0

Creating a list of a single element should not be much of a problem, performance-wise, but you could e.g. also use next to get the first (and only) element in that islice. In this case, the upper bound does not matter at all.

def sin_sketch(x, k):
    return next(itertools.islice(approx_sin(x, k), k-1, k))

Or, instead of islice, use next with enumerate and check the index. I did not time which of those is faster, but this might be a bit clearer.

def sin_sketch(x, k):
    return next(v for i, v in enumerate(approx_sin(x, k)) if i == k-1)
tobias_k
  • 81,265
  • 12
  • 120
  • 179
0

It seems like a case of Get the nth item of a generator in Python[*]. But I don't particularly like approaches described there, so here's mine.

From what I understand, you want to get k'th value generated by approx_sin(x, k). So let's do just that:

def sin_sketch(x, k):
    gen = approx_sin(x, k)
    for _ in range(k):
        v = next(gen)
    return v

This might look a bit bulkier, but I think it's way more explicit and transparent, without requiring itertools.


[*]: Looking through answers there, I found the one that I like even more than my own: https://stackoverflow.com/a/72817329/9288580

Nikolaj Š.
  • 1,457
  • 1
  • 10
  • 17
0

I would simplify your appox function a little to become

from math import pi
from itertools import count


def approx_sin(x):
    product = 1
    for n in count(1):
        product = product * (1-((x**2) / ((n**2) * (pi**2))))
        yield x * product

that might make the second part of the question more straightforward.

FiddleStix
  • 3,016
  • 20
  • 21