0

Looking for the roots of the equation by the dichotomy method: I have successfully implemented the method itself in Python code, but the essence of the question is different.

In the function I wrote myself, there is print() to output the roots. However, just like that, the function with the generator cannot be output - it only leads to a reference to the object. To see the roots in the console, I make an entry a list(function).

And in this case I get a double output of the roots: first in accordance with the implemented function, then in accordance with the list() notation.

How can I achieve the output of roots only in accordance with the method I wrote (with its rounding of the root and so on).

Code:

import numpy as np
import timeit
import time

def f(x):
    return 1.2-np.log(x)-4*np.cos(2*x)

def dichotomy (a,b,n, eps): # the segment from a to b is divided into n parts, the eps is error
    
    assert a!=0,  'a is 0'
    assert b!=0, 'b is 0'
    
    # first we separate the roots
    grid=np.linspace(a, b, n)
    
    # next , let 's clarify the roots
    for x,y in zip(grid, grid[1:]):
        if f(x) * f(y) > 0: 
            continue
        root = None
        while ( abs(f(y)-f(x)) )>eps:    
            mid = (y+x)/2                   
            if f(mid) == 0 or f(mid)<eps:    
                root = mid                  
                break
            elif (f(mid) * f(x)) < 0:       
                y = mid                     
            else:
                x = mid                     
        if root:
            yield root
            print(f'{root:.4f}', end=', ') # PRINT OUTPUT
    print()
    print(f'Time of the account: ', timeit.timeit('from __main__ import dichotomy'), 'seconds')

print(list(dichotomy(0.0001,184,10000, 0.000001))) # here the user substitutes the values of his arguments into the function and gets a printout of the roots, however, twice 
  • If you only want to see the output once, don't put `print()` in both places. Either print in the function, or print in the caller, but not both. – Barmar Mar 26 '22 at 04:55
  • @Barmar, That's the problem: in the body of the function, I have configured the output format that I need. However, if I don't put print() in the function call, then the output is just a reference in memory to the generator object – Alex_Kazantsev Mar 26 '22 at 04:58
  • You can call `list()` without printing the result. – Barmar Mar 26 '22 at 04:59
  • 1
    Change `print(list(dichotomy(...)))` to just `list(dichotomy(...))` – Barmar Mar 26 '22 at 05:00
  • @Barmar, Probably, the features of Jupiter Notebook overlap here, in which `list()` and `print(list())` are actually one notation. Thanx – Alex_Kazantsev Mar 26 '22 at 05:12
  • I assumed this was in a script, not being run in an interactive REPL. – Barmar Mar 26 '22 at 05:13
  • 1
    See https://stackoverflow.com/questions/23692950/how-do-you-suppress-output-in-jupyter-running-ipython for how to call a function without printing the result. – Barmar Mar 26 '22 at 05:14
  • It's not clear to me how you want the code to work. You want to compute multiple values, and for each one, you want to `print` something with custom formatting of the value? But you want to use the *same* formatting for each value, yes? Do you *also* want to accumulate a list of the values? Do you want to do something *else* with the values? What *problem do you intend to solve* by using a generator in the first place? – Karl Knechtel Mar 26 '22 at 05:14
  • 1
    Also: the way that you attempt to use `timeit.timeit` does not make any sense at all. It will not time the evaluation of the generator to get all of the values; and it does not time the execution of the function (which simply creates the generator object). If it *did* time evaluation of the generator, then it would be overwhelmingly time spent in the `print` calls, rather than math. But what you have simply times the process of importing the module (actually, it will re-read your code and create a module object once, and then look it up in `sys.modules` the other 999999 times). – Karl Knechtel Mar 26 '22 at 05:18
  • If you want an accurate answer to "how long does it take to run through the generator and do the calculations?", then you should a) have a generator that does not do I/O; b) make a wrapper function that iterates over the generator (whether it creates a list of the results, or just ignores them; but it should also not do I/O); c) time *the wrapper function*. – Karl Knechtel Mar 26 '22 at 05:20
  • @KarlKnechtel, if you answer the question about what I need, then I would like to: 1) get the output of the roots in a string and with rounding, 2) see the execution time of the fuction. I now have a third point after that - roots again, but without rounding and in a column – Alex_Kazantsev Mar 26 '22 at 05:26
  • Write the generator so that it does not do I/O. Write one wrapper function that makes a list of results from the generator, and time that for the execution time. Write a different wrapper function that iterates over the generator, `print`s each value inside the loop (with the desired formatting), and possibly `print`s whatever summary at the end. To see the values without rounding, you can simply display the list from the first wrapper. I'm not sure what you mean by "in a column" - if they should just appear one per line, then you can iterate over the list and `print` without formatting. – Karl Knechtel Mar 26 '22 at 05:30
  • (if by "column" you instead mean, for example, something to do with a Pandas Dataframe, you can assign to a column from the list. I think you can assign directly from the generator, too.) – Karl Knechtel Mar 26 '22 at 05:31
  • @KarlKnechtel, "In a column" is one per row. Just what I need to get, as I implemented: several values per line – Alex_Kazantsev Mar 26 '22 at 05:35
  • I'm confident that if you try the approach I described and see those results, that you will be able to figure out the rest for yourself. – Karl Knechtel Mar 26 '22 at 05:38
  • @KarlKnechtel, previously, I did this (see the code below), but I wanted to get away from too long 3-line output notation. Code: `res = list(dichotomy(0.0001,184,10000, 0.000001)) print('The roots of the dichotomy method are at points:') print(', '.join(map(lambda x: f'{x:.4f}', res)))` – Alex_Kazantsev Mar 26 '22 at 05:42

1 Answers1

1

As a result of the discussion above, I implemented the code as follows (see below). The conclusion is as required:

Time of the account: 0.08493947982788086 seconds

The roots according to the dichotomy method are located at the points: 0.0645, 0.5527,...

The only problem that remains is the implementation of the timeit.timeit() method and passing parameters to it. In the given script, this issue is bypassed.

Code:

def print_dichotomy(dichotomy):
    def wrapper(a,b,n, eps):
        start_time = time.time()
        res = list(dichotomy(a,b,n, eps))
        print('Time of the account: %s seconds' % (time.time() - start_time))
        print('The roots according to the dichotomy method are located at the points: ')
        print(', '.join(map(lambda x: f'{x:.4f}', res)))
    return wrapper

@print_dichotomy
def dichotomy (a,b,n, eps): 
    
    assert a!=0,  'a is 0'
    assert b!=0, 'b is 0'
    
    # first we separate the roots
    grid=np.linspace(a, b, n)
    
    # next , let 's clarify the roots
    for x,y in zip(grid, grid[1:]):
        if f(x) * f(y) > 0: 
            continue
        root = None
        while ( abs(f(y)-f(x)) )>eps:    
            mid = (y+x)/2                   
            if f(mid) == 0 or f(mid)<eps:    
                root = mid                  
                break
            elif (f(mid) * f(x)) < 0:       
                y = mid                     
            else:
                x = mid                     
        if root:
            yield root