Forum >> Principianti >> [Risolto] Cercare il valore più prossimo a zero

Pagina: 1

Ho fatto un lavoretto per calcolare il baudrate per i microcontrollori.
Purtroppo non capisco il perché il valore più prossimo è solo quello positivo. In quanto il range di avvicinarsi al valore migliore dovrebbe anche considerare i valori negativi.




Trovato l' errore, che nel dizionario assegnavo un valore non corrente al ciclo del FOR pertinente.
Infatti la riga deve essere
foundBR = ???br + n
Scordato la parte in rosso :P



--- Ultima modifica di F _ in data 2019-01-09 14:42:15 ---
Allegati
Abbi pazienza ma il tuo codice per me è un geroglifico. Troppe variabili con un nome indecifrabile, nessuna spiegazione, flusso generale poco chiaro. Tra l'altro, se posti codice corto (e bisognerebbe sempre postare codice corto!), sarebbe meglio includerlo nel post invece di allegarlo: tu fai un briciolo di fatica in più a copincollarlo, è vero. Ma chi legge e ti aiuta fa parecchia fatica in meno. Ed è anche più probabile che il messaggio sopravviva a future migrazioni di tecnologia, archiviazioni, etc. e quindi possa tornare utile a chi legge in futuro. I matusa come me usavano il termine "netiquette", ma è da un sacco che non lo vedo più in giro e non credo che esista ancora come concetto.


Detto questo, se il tuo problema è "come trovo il valore più prossimo allo zero in una lista", la risposta non può che passare da "abs", come faresti normalmente con carta e penna. Per esempio,


>>> L = [-5, -2, 3, 6]
>>> min(L, key=abs)
-2
O degli equivalenti più prolissi (ma forse più chiari) basati su cicli e/o comprehension.


>>> min([(abs(i), i) for i in L])[1] # se uno non sa che "min" accetta un argomento "key"!
-2




--- Ultima modifica di RicPol in data 2019-01-07 11:17:53 ---
Grazie Ric !

Convengo che hai diverse ragioni. Ho scritto un sorgente come farebbe un sarto, cominciando ad appuntarlo nella visione di non diffonderlo.

Magari alla fine, tra un paio di settimane avrò la stessa impressione che ha fatto a te.

Allora, le mie scuse per una metodo sghembo, o come dire poco ortodosso. Vi rimetto il programma con un po di commenti

#!/usr/bin/env python
import sys

def closest(lst, Number):
    ''' searching the mimimum within a list
    lst    >> the input list to search from
    Number >> the closest list element to compare with
    '''
    aux = []                                    # temporary list
    for val in lst:                             # looping into the list
        aux.append(abs(Number-val))             # absolute value from the diff
    return aux.index(min(aux))                  # return the pointer to the min


def percent(value, mybr):
    ''' Evaluate the percentage for a given number, for this specific case, which
    is taken from Microchip  DS40044A-page 71, EXAMPLE 12-1.
    Value >> the resulting baudrate
    mybr  >> the requested baudrate from the CLI parameter
    '''
    return (value - mybr)/mybr

def calc(xtal, myBR):
    ''' Calculate the baudrate for a given frequency and expected baudrate
    xtal  >> frequency in MHz
    myBR  >> the requested baudrate
    Unfortunately the hardware has a limitation to satisfy the requested
    baudrate, because of it uses only integer and a minimum value of 1
    '''
    xtalhz = xtal * 1e6                         # Convert MHz into Hz
    # First verification to the closest with higher dividend
    lowbr = int((xtalhz / 64 / myBR) -1)
    # the returned value, rounded to nearest integer
    lowres = (xtalhz / 64 / (lowbr +1))
    # First verification to the closest with lower dividend
    hibr = int((xtalhz / 16 / myBR) -1)
    # the returned value, rounded to nearest integer
    hires = (xtalhz / 16 / (hibr +1))
    return lowbr, lowres, hibr, hires

def main(args):
    ''' args is a argument list taken from the CLI
    '''
    wantedBR = int(args1)                     # 2nd argument from CLI. Baudrate
    # a value range to repeat the test against
    test = -2, -1, 0, 1, 2
    xtal = int(args0)                         # 1st CLI argument, MHz frequency
    myList = []                                 # empty list to collect results
    foundBR = {}                                # empty dict collect results
    i = 0
    print('Low baudrate list')                  # Calculate for the lower dividend
    # It's tried to find the most suitable number within a range -2 to +2
    # using the higher divisor
    for n in test:
        lowbr, lowres, hibr,hires = calc(xtal, wantedBR)
        value = xtal * 1e6 / (64 * (lowbr +n +1))
        myList.append(percent(value, wantedBR))
        # printing the range values of:
        # the resulting percentage
        # the resulting baudrate from the integer divisor
        # the countin index
        print('{0:.2%}\t{1}\t{2}'.format(percent(value, wantedBR),
                                                        lowbr +n,
                                                        i))
        foundBR = lowbr
        i += 1
    print('High baudrate list')
    # It's tried to find the most suitable number within a range -2 to +2
    # using the lower divisor
    for n in test:
        value = xtal * 1e6 / (16 * (hibr +n +1))
        myList.append(percent(value, wantedBR))
        # printing the range values of:
        # the resulting percentage
        # the resulting baudrate from the integer divisor
        # the countin index
        print('{0:.2%}\t{1}\t{2}'.format(percent(value, wantedBR),
                                                 hibr +n,
                                                 i))
        foundBR = hibr
        i += 1
    # it should get the value from the dictionary which index is found by the
    # calcultions
    print('Preferred %s' % foundBR[closest(myList,0)])

if __name__ == '__main__':
    args = (4,)                                 # defaulting to 4 MHz
    if len(sys.argv) > 1:                       # if more than one parameter
        args = (sys.argv[1:])                   # remove the program name
    main(args)                                  # pass arguments to main

Essendomi smemorizzato nel programmare in Python, diciamo che non trovavo la risposta pronta alle funzioni built-in che sono a disposizione. Allora ho trovato su Stackexchange una risposta alquanto convincente e lo messa in atto.
Ripensandoci, ho capito come fa min e cosa mi da index, ma non so affatto di key=. È un po che non leggo i documenti di python. Mi sono perso l' agilità di cercarci.
Ritornando al fatto del programma, la risposta è corretta solo usando l' indice della lista, solo che non mi ritorna quale sarebbe il valore da utilizzare, solo il baudrate calcolato. Allora ho preparato il dizionario cercando di associare il risultato voluto a quella del dizionario, ma questo si avvicina solo ai valori positivi.
Nel specifico caso che siano 16 MHz, il valore preferito è 0,173% e non -0,136% che è più prossimo a zero.


--- Ultima modifica di F _ in data 2019-01-09 13:14:38 ---
Mah, leggendo solo la prima parte del codice, direi che quel che devi fare è sostituire la tua funzione "closest" con la mia ricetta. Tranne che nell'oggetto di questo thread hai scritto "più prossimo a zero", mentre la tua funzione almeno in teoria calcola "il più prossimo a un numero dato qualsiasi", che è un po' diverso (la precisione, questa bestia rara). Non troppo diverso, però: alla fine, si tratta sempre di trovare il numero per cui è minima la differenza in valore assoluto con il numero dato. In pratica, devi solo riportare in codice il procedimento che faresti con carta e penna.


> Allora ho trovato su Stackexchange una risposta alquanto convincente
Hai trovato quel codice su Stackexchange? Brr, è veramente brutto, complimenti a chi l'ha scritto. Non capisco perché diamine uno dovrebbe fare la fatica di costruire una lista ausiliaria, poi trovarne il minimo, poi l'indice del minimo... ma che razza di metodo sarebbe? Anche se non conosci gli hack e non vuoi scrivere codice "cool", va benissimo risolvere il problema con un ciclo. Ma che sia un ciclo sensato, però:


>>> def closest(lst, n):
        min_diff = 10000000 # meglio ancora: min_diff = sys.maxsize
        res = None
        for i in lst:
            diff = abs(i-n)
            if diff < min_diff:
                min_diff = diff
                res = i
        return res
Attraversi una sola volta la lista, e non hai bisogno di stravaganti liste ausiliarie. O in modo equivalente, se ti interessa restituire l'indice invece che il valore:

>>> def closest(lst, n):
        min_diff = 10000000
        res = None
        for idx, i in enumerate(lst):
            diff = abs(i-n)
            if diff < min_diff:
                min_diff = diff
                res = idx
        return res
Poi certo, se invece conosci un po' di trucchi del mestiere e fai un po' di prove, arrivi a cose più "cool" e più compatte come

closest = lambda lst, n: min(lst, key=lambda i:abs(i-n))
Ma non è assolutamente necessario scrivere cose del genere. Però almeno un ciclo fatto in modo sensato, dovrebbe essere il minimo sindacale. Un consiglio: non cercare le cose su stackexchange, usa stackoverflow come fanno tutti. Non c'è paragone.


Oh, ancora una cosa: tieni presente che *se* la lista è già ordinata e se la performance conta, allora è meglio usare bisect.









Sono da classificare come frana paleolitica :) , la mia memoria perde colpi. Perché infatti la ricerca viene da Stackoverflow.
Nella quale pagina c'è anche il metodo oneliner con la lambda.
Siccome, non fatto tanto ragionamento sulle finezze di python, ho preso una che mi sembrava più comprensibile al momento della scelta. Lo scopo era di trovare una soluzione in pochi minuti a scapito di altre condizioni di miglioramento.


Ora andando a vedere la finezza nell'usare un loop, si può persino ridurre il ciclo di ricerche in una lista ordinata, dividendo la lista per ogni ciclo dal lato che il valore è minore. Alla apparenza sembra di minor lavoro, ma se contiamo che un sort() fa il suo bel loop comunque. Tutto sommato ci sarà sempre un certo quantitativo di loop per aver la risposta, da script o dai builtin di python.


Per finire, la funzione creata si orienta ad un valore che si avvicina la parametro richiesto,

che comunque potrebbe essere anche zero ;) . Infatti zero è la necessità del programma per ottenere il valore più vicino al quello dato dalla CLI. In pratica diff sarebbe abs(i) senza la sottrazione da n.

Se non mi sbaglio, hai scritto qualcosa anche per micro controllori.....

Lo scopo del programma se si riesce a capirlo, serve a determinare quale saranno i due parametri che servono al microcontrollore per impostare il voluto baud rate. la formula della immagine, mi determina un metodo per il divisore a 64, ma c'è anche l' opzione per il divisore a 16, che richiede di impostare il prescaler in modo diverso.

Allora il programma ha due passaggi, quello che determina la deriva, dovuta al fatto che si può usare solo numeri interi e la scelta del tipo di divisore da impostare al prescaler. Infatti c'è l'opzione che con un divisore si possa avvicinarsi meglio al baud rate scelto.


Non so se esiste una computazione diversa per trovare la risposta. Io ho preso questo percorso. Che poi ho aggiunto i 5 tentativi per capire quale possa avvicinarsi meglio.
Rientrando nel discorso del sorgente, il risultato non ritorna corretto, tenendo conto che i numeri negativi non sono più vicini allo zero. Forse è la scelta dell'indice nel dizionario, perché l'indice della lista è corretto. Solo che non mi serve il valore dell'indice della lista o il suo contenuto, ma il risultato del parametro che si abbina nel dizionario.


Esempio di inserire il valore di 20 MHz e il baudrate a 38400, il valore ottimale sarebbe 32:
$ python baudratecalc.py 20 38400
Low baudrate list
35.63%  5       0
16.26%  6       1
1.73%   7       2
-9.58%  8       3
-18.62% 9       4
High baudrate list
8.51%   29      5
5.01%   30      6
1.73%   31      7
-1.36%  32      8
-4.26%  33      9
0.013573232323232294
Preferred 31

Provo a vedere se con enumerate() riesco a sincronizzare l'indice del dizionario con il valore percent, oppure se devo fare un costrutto con uno zip().




--- Ultima modifica di F _ in data 2019-01-09 14:25:38 ---
Allegati


Pagina: 1



Esegui il login per scrivere una risposta.