Ricerca nel sito web

Usa Python per risolvere il problema aziendale di un ente di beneficenza


Confrontare come diversi linguaggi di programmazione risolvono lo stesso problema è divertente e istruttivo. Il prossimo passo è Python.

Nel mio primo articolo di questa serie, ho descritto il problema di dividere le forniture sfuse in cesti di valore simile da distribuire ai vicini in difficoltà nella tua comunità. Ho anche scritto di come mi diverto a risolvere piccoli problemi come questo con piccoli programmi in varie lingue e di confrontare come lo fanno.

Nel primo articolo ho risolto questo problema con il linguaggio di programmazione Groovy. Groovy è come Python in molti sensi, ma sintatticamente è più simile a C e Java. Pertanto, dovrebbe essere interessante e istruttivo creare la stessa soluzione in Python.

La soluzione Python

In Java, dichiaro che le classi di utilità contengono tuple di dati (la nuova funzionalità di record sarà eccezionale per questo). In Groovy utilizzo il supporto linguistico per le mappe e seguo lo stesso approccio in Python.

Utilizza un elenco di dizionari per conservare gli articoli sfusi ritirati dal grossista:

packs = [
	{'item':'Rice','brand':'Best Family','units':10,'price':5650,'quantity':1},
	{'item':'Spaghetti','brand':'Best Family','units':1,'price':327,'quantity':10},
	{'item':'Sardines','brand':'Fresh Caught','units':3,'price':2727,'quantity':3},
	{'item':'Chickpeas','brand':'Southern Style','units':2,'price':2600,'quantity':5},
	{'item':'Lentils','brand':'Southern Style','units':2,'price':2378,'quantity':5},
	{'item':'Vegetable oil','brand':'Crafco','units':12,'price':10020,'quantity':1},
	{'item':'UHT milk','brand':'Atlantic','units':6,'price':4560,'quantity':2},
	{'item':'Flour','brand':'Neighbor Mills','units':10,'price':5200,'quantity':1},
	{'item':'Tomato sauce','brand':'Best Family','units':1,'price':190,'quantity':10},
	{'item':'Sugar','brand':'Good Price','units':1,'price':565,'quantity':10},
	{'item':'Tea','brand':'Superior','units':5,'price':2720,'quantity':2},
	{'item':'Coffee','brand':'Colombia Select','units':2,'price':4180,'quantity':5},
	{'item':'Tofu','brand':'Gourmet Choice','units':1,'price':1580,'quantity':10},
	{'item':'Bleach','brand':'Blanchite','units':5,'price':3550,'quantity':2},
	{'item':'Soap','brand':'Sunny Day','units':6,'price':1794,'quantity':2}]

C'è una confezione sfusa da 10 sacchetti di riso e 10 confezioni sfuse con un sacchetto ciascuna di spaghetti. Nell'esempio sopra, la variabile packs è impostata su un elenco di dizionari Python. Questo risulta essere molto simile all'approccio Groovy. Alcuni punti degni di nota sulla differenza tra Groovy e Python:

  1. In Python, non esiste una parola chiave utilizzata per definire la variabile packs; Python si aspetta che al primo utilizzo venga impostato un valore.
  2. Chiavi del dizionario Python (ad esempio, item, brand, units, price, quantity ) richiedono virgolette per indicare che sono stringhe; Groovy presuppone che si tratti di stringhe, ma accetta anche virgolette.
  3. In Python, la notazione { … } indica una dichiarazione di dizionario; Groovy utilizza le stesse parentesi quadre di un elenco, ma la struttura in entrambi i casi deve avere coppie chiave-valore.

E sì, quei prezzi non sono in dollari USA.

Successivamente, disimballa i pacchetti sfusi. Disimballando il singolo pacco sfuso di riso, ad esempio, si otterranno 10 unità di riso; ovvero, il numero totale di unità prodotte è unità * quantità. Lo script Groovy utilizza una pratica funzione chiamata collectMany che può essere utilizzata per appiattire elenchi di elenchi. Per quanto ne so, Python non ha nulla di simile, quindi usa due list comprehension per produrre lo stesso risultato:

units = [[{'item':pack['item'],'brand':pack['brand'],
	'price':(pack['price'] / pack['units'])}] *
	(pack['units'] * pack['quantity']) for pack in packs]
units = [x for sublist in units for x in sublist]

La prima comprensione dell'elenco (assegnazione alle unità) costruisce l'elenco degli elenchi dei dizionari. Il secondo lo "appiattisce" solo in un elenco di dizionari. Tieni presente che sia Python che Groovy forniscono un operatore * che accetta un elenco a sinistra e un numero N a destra e replica l'elenco N volte.

Il passaggio finale consiste nel reimballare le unità nei cesti per la distribuzione. Come nella versione Groovy, devi essere un po' più specifico riguardo al valore ideale del cesto, e potresti anche non essere eccessivamente restrittivo quando scendi a poche unità rimaste:

valueIdeal = 5000
valueMax = valueIdeal * 1.1

OK! Riconfezionare i cesti:

import random
hamperNumber = 0           # [1]
while len(units) > 0:      # [2]
    hamperNumber += 1
    hamper = []
    value = 0
    canAdd = True              # [2.1]
    while canAdd:              # [2.2]
        u = random.randint(0,len(units)-1)  # [2.2.1]
        canAdd = False                      # [2.2.2]
        o = 0                               # [2.2.3]
        while o < len(units):               # [2.2.4]
            uo = (u + o) % len(units)
            unit = units[uo]
            unitPrice = unit['price']          # [2.2.4.1]
            if len(units) < 3 or not (unit in hamper) and (value + unitPrice) < valueMax:
                                               # [2.2.4.2]
                hamper.append(unit)
                value += unitPrice
                units.pop(u)                   # [2.2.4.3]
                canAdd = len(units) > 0
                break                          # [2.2.4.4]
            o += 1                             # [2.2.4.5]
                                            # [2.2.5]
    print('')
    print('Hamper',hamperNumber,'value',value)
    for item in hamper:
        print('%-25s%-25s%7.2f' % (item['item'],item['brand'],item['price'])) # [2.3]
    print('Remaining units',len(units))                                       # [2.4]

Alcuni chiarimenti, con numeri tra parentesi nei commenti sopra (ad esempio, [1]) corrispondenti ai chiarimenti seguenti:

  • 1. Importa le strutture del generatore di numeri casuali di Python e inizializza il numero del cesto.
    • 2.1 Incrementa il numero del cesto, ottieni un nuovo cesto vuoto (un elenco di unità) e imposta il suo valore su 0; inizia supponendo che tu possa aggiungere più oggetti al cesto.
      • 2.2.1 Ottieni un numero casuale compreso tra zero e il numero di unità rimanenti meno 1.
      • 2.2.2 Supponiamo che non riesci a trovare più unità da aggiungere.
      • 2.2.3 Crea una variabile da utilizzare per l'offset dal punto di partenza in cui stai cercando gli oggetti da mettere nel cesto.
        • 2.2.4.1. Scopri quale unità guardare (punto di partenza casuale + offset) e ottieni il suo prezzo.
        • 2.2.4.2 Puoi aggiungere questa unità al cesto se ne rimangono solo poche o se il valore del cesto non è troppo alto una volta aggiunta l'unità.
        • 2.2.4.3 Aggiungere l'unità al cesto, incrementare il valore del cesto del prezzo unitario, rimuovere l'unità dall'elenco delle unità disponibili.
        • 2.2.4.4 Finché rimangono unità, puoi aggiungerne altre, quindi esci da questo ciclo per continuare a cercare.
        • 2.2.4.5 Incrementare l'offset.
      • 2.2.5 All'uscita da questo ciclo while, se hai ispezionato ogni unità rimanente e non sei riuscito a trovarne una da aggiungere al cesto, il cesto è completo; altrimenti ne hai trovato uno e puoi continuare a cercarne altri.
    • 2.3 Stampare il contenuto del cesto.
    • 2.4 Stampare le informazioni sulle unità rimanenti.

Quando esegui questo codice, l'output sembra abbastanza simile all'output del programma Groovy:

Hamper 1 value 5304.0
UHT milk                 Atlantic                  760.00
Tomato sauce             Best Family               190.00
Rice                     Best Family               565.00
Coffee                   Colombia Select          2090.00
Sugar                    Good Price                565.00
Vegetable oil            Crafco                    835.00
Soap                     Sunny Day                 299.00
Remaining units 148

Hamper 2 value 5428.0
Tea                      Superior                  544.00
Lentils                  Southern Style           1189.00
Flour                    Neighbor Mills            520.00
Tofu                     Gourmet Choice           1580.00
Vegetable oil            Crafco                    835.00
UHT milk                 Atlantic                  760.00
Remaining units 142

Hamper 3 value 5424.0
Soap                     Sunny Day                 299.00
Chickpeas                Southern Style           1300.00
Sardines                 Fresh Caught              909.00
Rice                     Best Family               565.00
Vegetable oil            Crafco                    835.00
Spaghetti                Best Family               327.00
Lentils                  Southern Style           1189.00
Remaining units 135

…

Hamper 21 value 5145.0
Tomato sauce             Best Family               190.00
Tea                      Superior                  544.00
Chickpeas                Southern Style           1300.00
Spaghetti                Best Family               327.00
UHT milk                 Atlantic                  760.00
Vegetable oil            Crafco                    835.00
Lentils                  Southern Style           1189.00
Remaining units 4

Hamper 22 value 2874.0
Sardines                 Fresh Caught              909.00
Vegetable oil            Crafco                    835.00
Rice                     Best Family               565.00
Rice                     Best Family               565.00
Remaining units 0

L'ultimo cesto è abbreviato nel contenuto e nel valore.

Pensieri conclusivi

A prima vista, non c'è molta differenza tra le versioni Python e Groovy di questo programma. Entrambi hanno un insieme simile di costrutti che rendono la gestione di elenchi e dizionari molto semplice. Nessuno dei due richiede molti "codici standard" o altre azioni "cerimoniali".

Inoltre, come nell'esempio di Groovy, c'è qualche complicazione nel poter aggiungere unità al cesto. Fondamentalmente, scegli una posizione casuale nell'elenco delle unità e, partendo da quella posizione, scorri l'elenco finché non trovi un'unità il cui prezzo ne consente l'inclusione o finché non esaurisci l'elenco. Inoltre, quando rimangono solo pochi oggetti, li getti nell'ultimo cesto.

Un altro problema degno di nota: questo non è un approccio particolarmente efficiente. Rimuovere elementi dalle liste, non prestare attenzione alle espressioni ripetute e poche altre cose lo rendono meno adatto a un enorme problema di ridistribuzione. Tuttavia, funziona in un batter d'occhio sulla mia vecchia macchina.

Se tremi per il mio utilizzo dei cicli while e per la modifica dei dati in questo codice, probabilmente vorresti che lo rendessi più funzionale. Non riuscivo a pensare a un modo per utilizzare map e ridurre le funzionalità in Python insieme a una selezione casuale di unità per il riconfezionamento. Puoi?

Nel prossimo articolo, lo rifarò in Java solo per vedere quanto meno sforzo sono Groovy e Python, e gli articoli futuri riguarderanno Julia e Go.

Articoli correlati: