3

I want to define a function, e.g. a polynomial, from a list of prefactors. Something like

order = [1,0,1]

def poly(x):
    res = 0
    for i, o in enumerate(order):
      res += o * x**i
    return res

so poly(x) returns 1 + x².

I need to call that function multiple times for different x, but with the same prefactors. The above function executes the for loop every time it is called, which is rather inefficient, especially if the order list is long. How can I loop only once and call the result for different x? What is the pythonic solution?

A Magoon
  • 1,180
  • 2
  • 13
  • 21
Meown
  • 31
  • 2
  • 2
    You have the most efficient solution that I can think of at this time – inspectorG4dget Aug 11 '17 at 15:50
  • I would avoid raising to the power at each iteration by keeping the previous value and just multiplying it by `x` each iteration. This will also eliminate the need in `enumerate` and `i`. – Eugene Sh. Aug 11 '17 at 15:54
  • See if https://stackoverflow.com/questions/533382/dynamic-runtime-method-creation-code-generation-in-python helps. – Mark Ransom Aug 11 '17 at 15:56
  • *How can I loop only once and call the result for different x? What is the pythonic solution?* - there is no even *mathematical* solution for this. Perhaps you can "hide" the loop, but not to avoid it. – Eugene Sh. Aug 11 '17 at 16:01
  • NumPy also has a method that handles evaluating polynomials: https://docs.scipy.org/doc/numpy-1.12.0/reference/generated/numpy.polynomial.polynomial.polyval.html#numpy.polynomial.polynomial.polyval – Anthony Collins Aug 11 '17 at 16:06
  • Thanks AChampion, I didn't find https://stackoverflow.com/questions/40315105/how-to-write-function-with-variable-from-the-outside the answers provided there brought me to the solution: define poly as a function of order, call the function with the wanted order list and write this into a variable. this variable can now be called with different x. But as Eugene point out already, this actually only hides the for loop, still the execution is reasonably faster! Thanks for the fast help! – Meown Aug 11 '17 at 16:08
  • The method @EugeneSh. is referring to is known as [Horner's method](https://en.wikipedia.org/wiki/Horner%27s_method). – chepner Aug 11 '17 at 16:19
  • This was an interesting question, I've never tried to do code generation in Python before. There were some wrinkles I had to work through. – Mark Ransom Aug 11 '17 at 16:43

4 Answers4

1

Code #1: sum for loop (slow)

def poly(x,order=[1,0,1]) : return sum([o*x**i for i,o in enumerate(order)])

Example:

poly(2,order=[2,0,2])

>> 10
>> Execution time: 5.29289245605e-05

Code #2: Sum map (faster)

def poly(x,order=[1,0,1]) : return sum(map(lambda (i,o): o*x**i,enumerate(order)))

Example:

poly(2,order=[2,0,2])

>> 10
>> Execution time: 3.00407409668e-05
Noqomo
  • 168
  • 6
1

Python cannot outwit mathematics. The only optimization that comes to mind is avoiding the calculation of terms you multiply by zero anyway:

def poly(x, order=(1, 0, 1)):
    res = 0
    for i, o in enumerate(order):
        if o:
            res += o * x**i
    return res

As one-liner:

def poly2(x, order=(1, 0, 1)):
    return sum(o * x**i for i, o in enumerate(order) if o)
Mike Müller
  • 82,630
  • 20
  • 166
  • 161
1

Taking the suggestion of @Eugene Sh the repeated raising of powers can be eliminated:

xx = 1

def poly(x, order):
    global xx
    xx = 1
    def f(x, o):
        global xx
        ret = xx * o
        xx *= x
        return ret
    return sum(f(x,o) for o in order)
quamrana
  • 37,849
  • 12
  • 53
  • 71
1
def make_poly(order):
    expression = ''
    for i, o in enumerate(order):
        if o:
            if expression:
                expression += ' + '
            multiplier = '%d * ' % o if o > 1 else ''
            if i == 0:
                expression += str(o)
            elif i == 1:
                expression += multiplier + 'x'
            else:
                expression += multiplier + 'x**%d' % i
    loc = {}
    exec('def f(x):\n    return ' + expression, {}, loc)
    return loc['f']

This creates an expression from the passed list, removing all terms that are zero and other redundant operations. Then exec is used to create a function which is returned.

>>> poly = make_poly([1, 0, 1])
>>> poly(5)
26
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622