Please ignore secret bonuses. Secret tests do NOT award bonus. Max hw grade is 30+2 bonus efficiency

Do you need help?

Notice Board

Per partecipare al corso di Fondamenti di programmazione 2023-24 loggatevi e attivatelo nella vostra pagina dei corsi preferiti. A quel punto il corso appare nel menù personale cliccando sul proprio avatar. Per i materiali degli anni precedenti seguite lo stesso metodo.

To join the Programming/Lab 2023-24 course, log-on and select it on the my courses page. It will appear on the personal menu of your avatar. For earlier years use the same method.

consiglio ottimizzazione performance hw1 opt

g
g.cesolari (510 points)
2 2 5
in HW1 opzionale by (510 points)
recategorized by
ciao a tutti,

ho difficoltà a migliorare le performance per l'homework 1 opzionale.

Gli altri test passano, ma quello sulle performance no.

Cosa si può studiare o approfondire per l'argomento?

grazie.

saluti

7 Answers

l.vitale3 (6010 points)
10 22 83
by (6.0k points)
Cerca di mettere un'istruzione che esca dal ciclo quando conteggiando la somma superi il subtotal, se la tua implementazione ha una doppia iterazione.
g
g.cesolari (510 points)
2 2 5
by (510 points)
Intanto grazie mille per la risposta.

Come dici tu il mio codice prevede una doppia iterazione, ma l'uscita nella condizione che descrivi è già implementata.

Attualmente, alzando i timeout del test, sul mio pc che è vecchio, il test many_1s ci mette 3 secondi. Non è proprio pessimo, però vedo che molti colleghi hanno delle tempistiche quasi istantanee.

Credo proprio che mi sfugga qualcosa e vorrei imparare ad analizzare questo tipo di problemi
d
danyspadea (4330 points)
1 4 25
by (4.3k points)
seguo, anche io ho lo stesso problema.
Paradigmi (4170 points)
1 4 28
by (4.2k points)
Dovresti cercare di migliorare l'algoritmo che usi.

La strategia banale è quella di usare un algoritmo di tipo O(n^2), cioè per ogni elemento della lista iteri di nuovo tutti gli elementi.

Tuttavia, con una lista di 20.000 elementi (come lo è una dei test), passi a 400.000.000 iterazioni, per questo i tempi sono molto lenti.

È possibile risolverlo con un algoritmo O(n) (cioè una sola iterazione su tutti gli elementi), prova a pensare cosa succede quando sommi e superi il subtotal.
Ionut Cicio (5960 points)
2 2 43
by (6.0k points)
edited by

Molto probabilmente, con il codice che stai usando adesso, stai contando tutti i subarray continui dell'input: sono tanti! Di fatto sono (n*(n+1))/2 (dove n è il numero di elmenti dell'array in input). In genere si tende ad approssimare questa formula con n^2
Adesso, mettiamo il caso che in input abbiamo 10000 elementi: stai controllando 10000^2 subarray, ovvero stai facendo 100000000 somme e controlli! (di certo non è una soluzione con un tempo d'esecuzione di 30ms).

Detto questo, serve un pò di creatività e di "trial and error" su carta per riuscire a tirare fuori l'algoritmo che va per n (ovvero, se hai 10000 elementi, fa circa 10000 controlli) senza una previa conoscenza di algoritmi, ma dovrebbe essere fattibile (ti consiglio di informarti sull'argomento "sliding window algorithm" https://www.geeksforgeeks.org/window-sliding-technique/, questo e' un problema abbastanza classico per questa categoria).Il consiglio rimane quello di fare vari tentativi su carta prima, provando anche strategie diverse, e vedendo se i conti tornano (anche con input più piccoli, come ci fu suggerito a metodi: trovare l'input più piccolo per cui è possibile confutare una teoria)

Il miglior modo per ottimizzare è per l'appunto quello di usare l'algoritmo più opportuno per risolvere il problema (che fa la differenza di tantissimo). L'altro passo consiste nel fare piccole ottimizzazioni per il codice che hai già, ponendoti qualche domanda:
- C'è qualche operazione che posso evitare in base all'input? Ad esempio, se calcoli la somma di un sottoarray, appena questa supera il subtotale termini il ciclo che fa la somma senza continuare ad aggiungere numeri (non ci sono numeri negativi, quindi la somma non puo' diminuire!), e ti eviti un sacco di operazioni, se hai ad esempio un subarray di 9000 elementi e già dai primi 3 hai superato il subtotale.
- Sto calcolando più di 2 volte la stessa cosa? 

Es. lista[3, 2, 3] e subtotale = 5; il tuo algoritmo trova le somme di tutti i sottoarray, a cui diamo un nome, quindi:
a = [3]
b = [3, 2]
c = [3, 2, 3] # In questo punto ti sei calcolato 2 volte la somma del sottoarray b! C'è un modo in cui potresti evitarlo?
d = [2]
e = [2, 3]
f = [3]

L'esempio precedente era abbastanza piccolo, ma se pensi ad una lista con 10000 elementi, ti calcoli per 100 volte la somma del subarray che va dal primo elemento all'elemento 9900! Potresti provare a pensare a un modo per evitare di ricalcolare la somma degli stessi subarray!
- Ci sono casi speciali a cui devo far attenzione nell'input? In questo caso, abbiamo lo 0!

Es. lista[3, 0, 3] e subtotale = 3; I sottoinsiemi validi sono:

[3]
[3, 0]

[3]
[0, 3]


Come puoi sfruttare questa particolarità dello 0 nel nostro problema (se hai 3 zeri davanti a un sottoinsieme valido, al risultato devi aggiungere altri 3 subarray)?
Per farti capire, il 40% del mio codice (che in realtà ha una ventina di righe) è volto ad ottimizzare i casi con lo 0, portando il tempo d'esecuzione da 68ms a circa 30/40 ms.

Il consiglio principale che darei è quello di fare BENCHMARKING, in Python si fa usando https://docs.python.org/3/library/timeit.html. Se vuoi fare una modifica e vedere se effettivamente è più veloce devi eseguire il codice e vedere se effetivamente il tempo d'esecuzione è diminuito, non c'è altro modo.

Spero di esserti stato d'aiuto laugh e di non aver scritto qualche stupidaggine frown, nel dubbio correggetemi. Non sono un esperto di algoritmi e di ottimizzazione, tutto quello che ho scritto è da prendere con un pizzico di sale. (Se qualcuno vuole sapere come sono arrivato a quel (n*(n+1))/2, se lo chiede, posso fare una dimostrazione)

d
danyspadea (4330 points)
1 4 25
by (4.3k points)
grazie per la risposta, mi è stata d'aiuto
PyonD (1420 points)
1 4 19
by (1.4k points)
edited by

Ciao. 
La soluzione non è sicuramente semplice ma neanche difficile. Non posso condividere codice ma posso spiegarti il ragionamento per aiutarti a implementare un algoritmo efficiente.

La soluzione inizialmente può essere facilmente pensata, il problema è che essa è O(n^2), e pertanto superi il tempo limite. Il che significa che fai un controllo di tutte le possibili substring, e se parliamo di una stringa molto lunga il calcolo si allunga di molto
Attraverso il grafico puoi vedere che quando parliamo di numeri molto grandi, la differenze fra le due soluzioni diventa gigantesca.

Per avere una soluzione O(n) è possibile usare una tecnica denominata "Sliding Window" e controlla l'array in questo modo.

Immagina di avere l'array 1 2 3 4 5 e che devi trovare il target 9. Devi avere due indici, sinistra e destra.
inizi da 1, e controlli se è più grande o uguale a nove, non lo è. Pertanto l'indice di destra scala di 1


1+2 = 3 e non è uguale o maggiore di 9. Pertanto l'indice di destra scala di 1

1+2+3 = 6 e non è uguale o maggiore di 9. Pertanto l'indice di destra scala di 1

1+2+3+4 = 10. Qua 10 è maggiore di 9, pertanto l'indice di sinistra scala a destra facendo:

2+3+4 = 9 è uguale a 9, pertanto incrementi il contatore di 1

Successivamente scaliamo l'indice di sinistra di 1:
3+4 è minore di 9, pertanto:
3+4+5 = 12, pertanto scaliamo l'indice di sinistra
4+5 = 9, pertanto incrementiamo di uno il nostro contatore

Alla fine ritorniamo il valore del contatore (2) e abbiamo così ottenuto il risultato con un algoritmo molto efficiente

Perdonatemi eventuali errori.
Se vuoi approfondire con un video quel che ti ho spiegato qui di consiglio di vedere questo video

EDIT: Per usare questa tecnica è necessario che i subarray siano contigui, cioè che siano "attaccati"

Paradigmi (4170 points)
1 4 28
by (4.2k points)
Attenzione ad includere eventuali 0 che si trovano a sinistra.

Ad esempio, dato 1 0 2 3 4 5 (con target 9),

Quando arrivi al caso 0+2+3+4 e incrementi il contatore di uno, prima di andare avanti ti devi ricordare di contare anche 2+3+4, perché poi il caso successivo diventa 3+4+5.
PyonD (1420 points)
1 4 19
by (1.4k points)
Mea culpa. Ovviamente quando ho implementato il codice non ho commesso questo errore. Mi è uscito malissimo questo passaggio, infatti correggo.

Edit: corretto, ma di fretta, questo perché non vorrei qualcuno si sbagli per colpa mia. In caso i passaggi non tornino avvisate pure.
Gianfriddo (1090 points)
5 6 10
by (1.1k points)

Perdonami, ho bene in mente il passaggio ma sono bloccato riguardo ad una casistica tipo 0 0 2 0 0 con subtotale = 2

Non capisco come effettivamente contare le 9 stringhe senza rendere il codice eccessivamente intricato al punto da inficiarne il comportamento; ho notato delle ricorrenze valutando il numero di zeri a destra ed a sinistra, ma con un caso come:

0 0 1 0 1 0 0 1 sempre con subtotale = 2

non riesco a "tutelare" bene la lista ed a far in modo che il codice non salti pezzi, se magari trattassi una sottostringa scorrendola come avete detto coi due indici sommando e controllando cosa sto sommando, poi alla fine posso anche scorrere da sinistra l'indice sinistro verso destra ma potrei non contare qualche sottostringa, almeno per come e quante volte io abbia provato.

Perdonatemi la poca chiarezza ma sto pensando all'algoritmo da due giorni e non ne sto uscendo con nulla, sento che mi manca solo qualche pezzo ma o tra implementazione o tra algoritmo non sto ancora trovando la soluzione

Mich1803 (2620 points)
14 40 58
by (2.6k points)
Devi stare attento a non sommare ogni volta tutti i numeri della sottostringa, ma trovare un metodo più efficiente che somma due numeri alla volta (per evitare ripetizioni di cicli assurde)

P.S: io ho inoltre adottato una funzione che analizza la presenza di casi specifici e ne calcola il risultato grazie ad una legge matematica (che se vuoi puoi anche dimostrare per induzione). Prova per esempio a pensare al fatto che se si ripetono 20'000 cifre '1' ed il subtotal è 1000, allora l'expected result è 19'001.
e
emmaRGB (220 points)
0 0 1
by (220 points)

Non riesco a risolvere questo esercizio  ...aiuto...

Il mio ragionamento è questo:

#  Transformo la stringa in lista
# applico la sliding windows in questo modo:

 # faccio un ciclo di while le cui condizioni sono due : l'end dello sliding windows deve essere minore della lunghezza della lista , seconda : lo start  della sliding windows deve essere piu piccolo dell'end :

# dentro il while ci sono solo if che si dividono in 3 diverse situazioni , se la somma viene più alta del subtotal , più bassa o uguale, se è uguale ci sono altre 4 situazioni diverse che sono se all'inizio ed alla fine sono zeri si aggiunge 2 al risultato, se solo alla fine si aggiunge 2 al risultato, o se solo all'inizio se non sono zeri ne l'end ne lo start si aggiunge 1 al risultato.

cosa sbaglio?

A me sembra corretto il ragionamento . cosa mi perdo?

grazie