Partizioni di un numero

Pubblicità
Scusa, ma continuo a non capire cosa stai cercando di ottenere precisamente (e a tal proposito sarebbe utile sapere cosa devi farci con queste partizioni).

Dal post iniziale mi sembrava di capire che il tuo scopo fosse quello di generare una sequenza come quella da te postata con i numeri colorati che ti permettesse di estrarre le partizioni di n mediante la seguente procedura da te descritta:

Ora invece mi pare che tu ti stia soffermando su come ottenere le partizioni di n+1 partendo da quelle di n. E a tal proposito non mi sembra che per passare per esempio dalle partizioni di 7 a quelle di 8 sia sufficiente "attaccare uno a tutte le partizioni".

Visto che il problema mi sembrava interessante ho provato a risolverlo pure io, ma se non chiarisci i punti sopra esposti diventa difficile capire su cosa confrontarsi...
Allora il problema è quello di creare una lista di liste come nell'immagine ovviamente non si può stampare perché ti stamperesti solo la prima lista essendo infinita quindi stampo le partizioni di un numero per capire se ho creato la lista di liste giusta. ( la cosa interessante creare questa cosa poi non ci faccio nulla con le partizioni il programma rimane fine a se perché non ha senso creare le partizioni con questo metodo) .

Il fatto di ridurre tutto a trovare le partizioni di n+1 da n è un escamotage per creare ricorsivamente quella lista poiché per definizione è la lista infinita delle partizioni. ( questo metodo che ho usato è brutal Force immagino e credo ce ne siano altri anche per questo ho aperto questo post ).

Per passare dalla partizione n a quella n+1 devi aggiungere 1 all'ultimo elemento e concatenare a questa lista la lista delle partizioni precedenti aggiungendo ad ognuna 1 come ultimo elemento. Così ne hai troppe e quindi devi eliminare i doppioni ovvero le liste non ordinate così le hai tutte e sole esempio:
parto dalle partizioni di 4
Codice:
[1,1,1,1]
[2,1,1]
[3,1]
[2,2]
[4]
primo passo aggiungo 1 all'ultimo termine
Codice:
[1,1,1,2]
[2,1,2]
[3,2]
[2,3]
[5]
unisco con le partizioni precedenti aggiungendo un 1 alla fine delle partizioni di 4
Codice:
[1,1,1,1,1]
[2,1,1,1]
[3,1,1]
[2,2,1]
[4,1]
[1,1,1,2]
[2,1,2]
[3,2]
[2,3]
[5]
tolgo quelle non ordinate
Codice:
[1,1,1,1,1]
[2,1,1,1]
[3,1,1]
[2,2,1]
[4,1]
[3,2]
[5]
ho trovato tutte e sole le partizioni di 5.

@BAT con questo metodo la partizione [2,2,2] la trovi dalla partizione [2,2,1] dopo il primo passo.

l'unica differenza usando liste infinite è che aggiungo una lista infinita di 1 e non un solo 1 nel passo 2.
 
Ultima modifica da un moderatore:
Allora il problema è quello di creare una lista di liste come nell'immagine ovviamente non si può stampare perché ti stamperesti solo la prima lista essendo infinita quindi stampo le partizioni di un numero per capire se ho creato la lista di liste giusta. ( la cosa interessante creare questa cosa poi non ci faccio nulla con le partizioni il programma rimane fine a se perché non ha senso creare le partizioni con questo metodo) .

Il fatto di ridurre tutto a trovare le partizioni di n+1 da n è un escamotage per creare ricorsivamente quella lista poiché per definizione è la lista infinita delle partizioni. ( questo metodo che ho usato è brutal Force immagino e credo ce ne siano altri anche per questo ho aperto questo post ).
Ok, adesso le cose mi sono un po' più chiare, anche se non del tutto.

In ogni caso ti dico quello che ho fatto io, poi mi dici tu se c'entra o meno con quello che stavi cercando di fare.
Per prima cosa mi calcolo le partizioni di n_max, e per farlo non sfrutto ricorsioni o cose del genere, ma mi limito a costruire la partizione successiva partendo da quella corrente sfruttando l'ordine lessicografico e sapendo che la partizione iniziale è data da una sequenza di n_max "1", mentre quella finale dal solo valore "n_max".
In questo modo trovo l'elenco v_p_1 delle partizioni di n_max in ordine lessicografico, poi riordinando le suddette partizioni ottengo un secondo elenco v_p_2 che soddisfa il criterio di estrapolazione delle partizioni di n (con n minore o uguale a n_max), da te così descritto:
Una volta che ho questo stream di stream per prendere le partizioni di 27 faccio un takewhile testa della lista == 27 e poi di queste liste prendo di nuovo takewhile sommasegmenti iniziali == 27
Per passare da v_p_1 a v_p_2 sfrutto un criterio di ordinamento basato in prima istanza sul numero di "1" e in seconda istanza sul numero di elementi delle singole partizioni.

Di seguito il codice in C++:
C++:
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

//GENERA LA PARTIZIONE SUCCESSIVA IN ORDINE LESSICOGRAFICO
//LA SINGOLA PARTIZIONE E' IN ORDINE LESSICOGRAFICO INVERSO
//IL VETTORE w CONTIENE L'INDICE, IL NUMERO DI "1" E IL NUMERO DI ELEMENTI DI OGNI PARTIZIONE
vector<unsigned int> partizione_successiva(const unsigned int n_max, const vector<unsigned int> &p, vector<vector<unsigned int>> &w)
{
    vector<unsigned int> p_succ;
    unsigned int m = 0;
    unsigned int i = p.size() - 2;
    for(; i && p[i] == p[i - 1]; --i);
    for(unsigned int j = 0; j <= i; ++j)
    {
        p_succ.push_back(p[j]);
        m += p_succ.back();
    }
    for(++p_succ.back(), ++m, i = n_max - m; i; --i)
    {
        p_succ.push_back(1);
    }
    w.push_back({(unsigned int)w.size(), n_max - m, (unsigned int)p_succ.size()});
    return p_succ;
}

bool funzione_comparazione(const vector<unsigned int> &v_1, const vector<unsigned int> &v_2)
{
    return v_2[1] < v_1[1] || v_2[1] == v_1[1] && v_2[2] < v_1[2];
}

//RIORDINA LE PARTIZIONI DI n_max PER RISPETTARE IL CRITERIO DI ESTRAPOLAZIONE DELLE
//PARTIZIONI DI n <= n_max UTILIZZATO IN stampa_2()
vector<vector<unsigned int>> partizioni_ordinate(const vector<vector<unsigned int>> &v_p_1, vector<vector<unsigned int>> &w)
{
    vector<vector<unsigned int>> v_p_2;
    sort(w.begin(), w.end(), funzione_comparazione);
    for(unsigned int i = 0; i < v_p_1.size(); ++i)
    {
        v_p_2.push_back(v_p_1[w[i][0]]);
    }
    return v_p_2;
}

void stampa_1(const vector<vector<unsigned int>> &v_p)
{
    for(unsigned int i = 0; i < v_p.size(); ++i)
    {
        cout << "\n " << i + 1 << ":\t";
        for(unsigned int j = 0; j < v_p[i].size(); ++j)
        {
            cout << v_p[i][j] << " ";
        }
    }
    cout << "\n";
}

void stampa_2(const vector<vector<unsigned int>> &v_p_2, const unsigned int n_max, const unsigned int n)
{
    if(n && n <= n_max)
    {
        unsigned int i = 0;
        unsigned int j;
        unsigned int sum;
        do
        {
            j = sum = 0;
            cout << "\n " << i + 1 << ":\t";
            do
            {
                cout << v_p_2[i][j] << " ";
            }
            while((sum += v_p_2[i][j++]) != n);
        }
        while(v_p_2[i++][0] != n);
        cout << "\n";
    }
}

int main()
{
    unsigned int n_max = 9;
    unsigned int n = 7;

    if(n_max)
    {
        vector<vector<unsigned int>> v_p_1(1, vector<unsigned int>(n_max, 1));
        vector<vector<unsigned int>> w(1, {0, n_max, n_max});
        for(; v_p_1.back().front() != n_max; v_p_1.push_back(partizione_successiva(n_max, v_p_1.back(), w)));
        vector<vector<unsigned int>> v_p_2 = partizioni_ordinate(v_p_1, w);
        stampa_1(v_p_1);
        stampa_1(v_p_2);
        stampa_2(v_p_2, n_max, n);
    }
}

Questo invece è l'output, che mostra prima l'elenco delle partizioni di n_max=9 in ordine lessicografico, poi l'elenco delle partizioni di n_max=9 ordinato ai fini dell'estrapolazione e infine l'elenco delle partizioni di n=6 ricavato dal secondo elenco:
Codice:
 1:     1 1 1 1 1 1 1 1 1
 2:     2 1 1 1 1 1 1 1
 3:     2 2 1 1 1 1 1
 4:     2 2 2 1 1 1
 5:     2 2 2 2 1
 6:     3 1 1 1 1 1 1
 7:     3 2 1 1 1 1
 8:     3 2 2 1 1
 9:     3 2 2 2
 10:    3 3 1 1 1
 11:    3 3 2 1
 12:    3 3 3
 13:    4 1 1 1 1 1
 14:    4 2 1 1 1
 15:    4 2 2 1
 16:    4 3 1 1
 17:    4 3 2
 18:    4 4 1
 19:    5 1 1 1 1
 20:    5 2 1 1
 21:    5 2 2
 22:    5 3 1
 23:    5 4
 24:    6 1 1 1
 25:    6 2 1
 26:    6 3
 27:    7 1 1
 28:    7 2
 29:    8 1
 30:    9

 1:     1 1 1 1 1 1 1 1 1
 2:     2 1 1 1 1 1 1 1
 3:     3 1 1 1 1 1 1
 4:     2 2 1 1 1 1 1
 5:     4 1 1 1 1 1
 6:     3 2 1 1 1 1
 7:     5 1 1 1 1
 8:     2 2 2 1 1 1
 9:     3 3 1 1 1
 10:    4 2 1 1 1
 11:    6 1 1 1
 12:    3 2 2 1 1
 13:    4 3 1 1
 14:    5 2 1 1
 15:    7 1 1
 16:    2 2 2 2 1
 17:    4 2 2 1
 18:    3 3 2 1
 19:    4 4 1
 20:    5 3 1
 21:    6 2 1
 22:    8 1
 23:    3 2 2 2
 24:    3 3 3
 25:    4 3 2
 26:    5 2 2
 27:    5 4
 28:    6 3
 29:    7 2
 30:    9

 1:     1 1 1 1 1 1 1
 2:     2 1 1 1 1 1
 3:     3 1 1 1 1
 4:     2 2 1 1 1
 5:     4 1 1 1
 6:     3 2 1 1
 7:     5 1 1
 8:     2 2 2 1
 9:     3 3 1
 10:    4 2 1
 11:    6 1
 12:    3 2 2
 13:    4 3
 14:    5 2
 15:    7

Infine credo di aver trovato anche un modo più efficiente rispetto a quello da te proposto per passare dalle partizioni di n a quelle di n+1, ma dimmi tu se ha ancora senso approfondire questa cosa alla luce di quanto scritto in questo post.
 
@Mursey grazie per la gentile correzione non mi era arrivata nessuna notifica (o me la sarò persa).
Ok, adesso le cose mi sono un po' più chiare, anche se non del tutto.

In ogni caso ti dico quello che ho fatto io, poi mi dici tu se c'entra o meno con quello che stavi cercando di fare.
Per prima cosa mi calcolo le partizioni di n_max, e per farlo non sfrutto ricorsioni o cose del genere, ma mi limito a costruire la partizione successiva partendo da quella corrente sfruttando l'ordine lessicografico e sapendo che la partizione iniziale è data da una sequenza di n_max "1", mentre quella finale dal solo valore "n_max".
In questo modo trovo l'elenco v_p_1 delle partizioni di n_max in ordine lessicografico, poi riordinando le suddette partizioni ottengo un secondo elenco v_p_2 che soddisfa il criterio di estrapolazione delle partizioni di n (con n minore o uguale a n_max), da te così descritto:

Per passare da v_p_1 a v_p_2 sfrutto un criterio di ordinamento basato in prima istanza sul numero di "1" e in seconda istanza sul numero di elementi delle singole partizioni.

Di seguito il codice in C++:
C++:
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

//GENERA LA PARTIZIONE SUCCESSIVA IN ORDINE LESSICOGRAFICO
//LA SINGOLA PARTIZIONE E' IN ORDINE LESSICOGRAFICO INVERSO
//IL VETTORE w CONTIENE L'INDICE, IL NUMERO DI "1" E IL NUMERO DI ELEMENTI DI OGNI PARTIZIONE
vector<unsigned int> partizione_successiva(const unsigned int n_max, const vector<unsigned int> &p, vector<vector<unsigned int>> &w)
{
    vector<unsigned int> p_succ;
    unsigned int m = 0;
    unsigned int i = p.size() - 2;
    for(; i && p[i] == p[i - 1]; --i);
    for(unsigned int j = 0; j <= i; ++j)
    {
        p_succ.push_back(p[j]);
        m += p_succ.back();
    }
    for(++p_succ.back(), ++m, i = n_max - m; i; --i)
    {
        p_succ.push_back(1);
    }
    w.push_back({(unsigned int)w.size(), n_max - m, (unsigned int)p_succ.size()});
    return p_succ;
}

bool funzione_comparazione(const vector<unsigned int> &v_1, const vector<unsigned int> &v_2)
{
    return v_2[1] < v_1[1] || v_2[1] == v_1[1] && v_2[2] < v_1[2];
}

//RIORDINA LE PARTIZIONI DI n_max PER RISPETTARE IL CRITERIO DI ESTRAPOLAZIONE DELLE
//PARTIZIONI DI n <= n_max UTILIZZATO IN stampa_2()
vector<vector<unsigned int>> partizioni_ordinate(const vector<vector<unsigned int>> &v_p_1, vector<vector<unsigned int>> &w)
{
    vector<vector<unsigned int>> v_p_2;
    sort(w.begin(), w.end(), funzione_comparazione);
    for(unsigned int i = 0; i < v_p_1.size(); ++i)
    {
        v_p_2.push_back(v_p_1[w[i][0]]);
    }
    return v_p_2;
}

void stampa_1(const vector<vector<unsigned int>> &v_p)
{
    for(unsigned int i = 0; i < v_p.size(); ++i)
    {
        cout << "\n " << i + 1 << ":\t";
        for(unsigned int j = 0; j < v_p[i].size(); ++j)
        {
            cout << v_p[i][j] << " ";
        }
    }
    cout << "\n";
}

void stampa_2(const vector<vector<unsigned int>> &v_p_2, const unsigned int n_max, const unsigned int n)
{
    if(n && n <= n_max)
    {
        unsigned int i = 0;
        unsigned int j;
        unsigned int sum;
        do
        {
            j = sum = 0;
            cout << "\n " << i + 1 << ":\t";
            do
            {
                cout << v_p_2[i][j] << " ";
            }
            while((sum += v_p_2[i][j++]) != n);
        }
        while(v_p_2[i++][0] != n);
        cout << "\n";
    }
}

int main()
{
    unsigned int n_max = 9;
    unsigned int n = 7;

    if(n_max)
    {
        vector<vector<unsigned int>> v_p_1(1, vector<unsigned int>(n_max, 1));
        vector<vector<unsigned int>> w(1, {0, n_max, n_max});
        for(; v_p_1.back().front() != n_max; v_p_1.push_back(partizione_successiva(n_max, v_p_1.back(), w)));
        vector<vector<unsigned int>> v_p_2 = partizioni_ordinate(v_p_1, w);
        stampa_1(v_p_1);
        stampa_1(v_p_2);
        stampa_2(v_p_2, n_max, n);
    }
}

Questo invece è l'output, che mostra prima l'elenco delle partizioni di n_max=9 in ordine lessicografico, poi l'elenco delle partizioni di n_max=9 ordinato ai fini dell'estrapolazione e infine l'elenco delle partizioni di n=6 ricavato dal secondo elenco:
Codice:
 1:     1 1 1 1 1 1 1 1 1
 2:     2 1 1 1 1 1 1 1
 3:     2 2 1 1 1 1 1
 4:     2 2 2 1 1 1
 5:     2 2 2 2 1
 6:     3 1 1 1 1 1 1
 7:     3 2 1 1 1 1
 8:     3 2 2 1 1
 9:     3 2 2 2
 10:    3 3 1 1 1
 11:    3 3 2 1
 12:    3 3 3
 13:    4 1 1 1 1 1
 14:    4 2 1 1 1
 15:    4 2 2 1
 16:    4 3 1 1
 17:    4 3 2
 18:    4 4 1
 19:    5 1 1 1 1
 20:    5 2 1 1
 21:    5 2 2
 22:    5 3 1
 23:    5 4
 24:    6 1 1 1
 25:    6 2 1
 26:    6 3
 27:    7 1 1
 28:    7 2
 29:    8 1
 30:    9

 1:     1 1 1 1 1 1 1 1 1
 2:     2 1 1 1 1 1 1 1
 3:     3 1 1 1 1 1 1
 4:     2 2 1 1 1 1 1
 5:     4 1 1 1 1 1
 6:     3 2 1 1 1 1
 7:     5 1 1 1 1
 8:     2 2 2 1 1 1
 9:     3 3 1 1 1
 10:    4 2 1 1 1
 11:    6 1 1 1
 12:    3 2 2 1 1
 13:    4 3 1 1
 14:    5 2 1 1
 15:    7 1 1
 16:    2 2 2 2 1
 17:    4 2 2 1
 18:    3 3 2 1
 19:    4 4 1
 20:    5 3 1
 21:    6 2 1
 22:    8 1
 23:    3 2 2 2
 24:    3 3 3
 25:    4 3 2
 26:    5 2 2
 27:    5 4
 28:    6 3
 29:    7 2
 30:    9

 1:     1 1 1 1 1 1 1
 2:     2 1 1 1 1 1
 3:     3 1 1 1 1
 4:     2 2 1 1 1
 5:     4 1 1 1
 6:     3 2 1 1
 7:     5 1 1
 8:     2 2 2 1
 9:     3 3 1
 10:    4 2 1
 11:    6 1
 12:    3 2 2
 13:    4 3
 14:    5 2
 15:    7

Infine credo di aver trovato anche un modo più efficiente rispetto a quello da te proposto per passare dalle partizioni di n a quelle di n+1, ma dimmi tu se ha ancora senso approfondire questa cosa alla luce di quanto scritto in questo post.

Questo codice scritto qui è corretto ma non è quello che cercavo che purtoppo non si può scrivere in c++ poichè non gestisce le liste infinite. Il fatto sta proprio nel creare uno stream di stream e poi volendo estrarre le partizioni ma se io chiedo al programma di stamparmi lo stream di stream lui inizia a stamparmi tutte le liste ovviamente essendo infinite si blocca sulla prima ma ipoteticamente lui le stamperebbe tutte. Posso per esempio chiedergli di stamparmi tutte le teste delle liste e questo in un linguaggio lazy funziona e mi stampa una lita infinita con tutte le teste. Quindi se hai un suggerimento su come passare dalle partizioni di n a quelle di n+1 è ben accetto.
 
Questo codice scritto qui è corretto ma non è quello che cercavo che purtoppo non si può scrivere in c++ poichè non gestisce le liste infinite. Il fatto sta proprio nel creare uno stream di stream e poi volendo estrarre le partizioni ma se io chiedo al programma di stamparmi lo stream di stream lui inizia a stamparmi tutte le liste ovviamente essendo infinite si blocca sulla prima ma ipoteticamente lui le stamperebbe tutte.
Il programma che ho scritto calcola direttamente l'elenco delle partizioni di n_max in ordine lessicografico e poi le riordina affinché si possa agevolmente estrarre l'elenco delle partizioni per qualsiasi n minore di n_max senza doverle calcolare da capo, e mi sembrava la cosa che più si avvicinasse alla richiesta da te fatta nel post iniziale, visto anche l'immagine coi numeretti colorati che hai postato e la tua seguente affermazione:
Una volta che ho questo stream di stream per prendere le partizioni di 27 faccio un takewhile testa della lista == 27 e poi di queste liste prendo di nuovo takewhile sommasegmenti iniziali == 27


Quindi se hai un suggerimento su come passare dalle partizioni di n a quelle di n+1 è ben accetto.
Ipotizzo ci siano margini di miglioramento al procedimento da te descritto per passare da n a n+1, ma prima di soffermarci su queste minuzie non si può ignorare l'elefante nella stanza... Ti chiedo quindi se puoi chiarire i miei seguenti dubbi:
- innanzitutto l'elenco delle partizioni che ottieni passando da n a n+1 deve comunque rispettare il criterio di estrapolazione da te enunciato?
- ma in tal caso che senso avrebbe estrapolare partizioni che hai già dovuto calcolare direttamente nel passaggio da n a n+1? Nel mio caso invece calcolo direttamente solo le partizioni di n_max, mentre quelle per n inferiori le ottengo indirettamente.
- non sono sicuro di aver compreso appieno il livello di astrazione consentito dall'haskell, ma penso che poi all'atto pratico per poterlo utilizzare quel codice un certo n_max devi comunque fissarlo, o sbaglio? A questo punto anche il mio codice, essendo parametrizzato su n_max, possiede un certo livello di astrazione, ma almeno ti calcola direttamente solo le partizioni di n_max e non è obbligato a calcolare le partizioni per tutti i numeri da 1 a n_max. Che poi se n_max=10 diciamo che in termini di efficienza i nostri approcci se la possono pure giocare, ma già se poniamo n_max=100 o n_max=1000 penso che non ci sarebbe storia. Mi sfugge forse qualcosa?
 
Ultima modifica:
Si ho capito cosa calcola il tuo programma ma non è il cuore del problema perché quello scritto da me funziona prescindere da un numero di input se lancio la funzione allPartition gira e inzia a stampare la lista di liste (essendo infinita non finirà mai) questo serve perché poi posso interrogarlo per esempio chiedergli quale è la 12 lista e lui mi stampa solo la 12-esima lista etc..
per il miglioramento delle partizioni io non le creo tutte di nuovo ma creo solo quelle nuove( in cui ci sta qualche doppione non ordinato) e poi ci concateno quelle vecchie. Quindi sull'efficienza é simile al tuo poiché le partizioni di n+1 contengono quelle di n (a meno di concatenare 1 alla fine) più altre.
 
Quindi se hai un suggerimento su come passare dalle partizioni di n a quelle di n+1 è ben accetto.
Io imposterei un algoritmo del genere:
- sia m il numero di partizioni del numero n;
- si considerano le partizioni di n nell'ordine in cui sono elencate;
- se la partizione considerata ha un solo elemento o se la differenza tra il suo penultimo e ultimo elemento è >=1, allora aggiungo all'elenco una nuova partizione uguale a quella considerata, ma con l'ultimo elemento incrementato di 1;
- aggiungo l'elemento "1" in coda alla partizione considerata;
- applico questo procedimento alle prime m partizioni.

Se le partizioni di n di partenza rispettano il criterio di estrapolazione, allora lo rispetteranno anche le partizioni di n+1 così trovate (ovviamente se si parte dalla partizione {1} del numero 1 non ci sono problemi da questo punto di vista).

Di seguito il codice in C++:
C++:
#include <iostream>
#include <vector>

using namespace std;

void genera_partizioni_successive(vector<vector<unsigned int>> &v_p)
{
    for(unsigned int i = 0, m = v_p.size(); i < m; ++i)
    {
        if(v_p[i].size() == 1 || v_p[i][v_p[i].size() - 2] - v_p[i].back() >= 1)
        {
            v_p.push_back(vector<unsigned int>(v_p[i].begin(), v_p[i].end()));
            ++v_p.back().back();
        }
        v_p[i].push_back(1);
    }
}

void stampa_1(const vector<vector<unsigned int>> &v_p)
{
    for(unsigned int i = 0; i < v_p.size(); ++i)
    {
        cout << "\n " << i + 1 << ":\t";
        for(unsigned int j = 0; j < v_p[i].size(); ++j)
        {
            cout << v_p[i][j] << " ";
        }
    }
    cout << "\n";
}

void stampa_2(const vector<vector<unsigned int>> &v_p, const unsigned int n_max, const unsigned int n)
{
    if(n && n <= n_max)
    {
        unsigned int i = 0;
        unsigned int j;
        unsigned int sum;
        do
        {
            j = sum = 0;
            cout << "\n " << i + 1 << ":\t";
            do
            {
                cout << v_p[i][j] << " ";
            }
            while((sum += v_p[i][j++]) != n);
        }
        while(v_p[i++][0] != n);
        cout << "\n";
    }
}

int main()
{
    unsigned int n_max = 9;
    unsigned int n = 7;

    vector<vector<unsigned int>> v_p(1, {1});
    for(unsigned int i = 2; i <= n_max; ++i)
    {
        genera_partizioni_successive(v_p);
    }
    stampa_1(v_p);
    stampa_2(v_p, n_max, n);
}
 
É esattamente la controparte sui vettori di quello che ho fatto io sulle liste. Poiché sulle liste non puoi accedere alle ultime due componenti la dovrei scorrere tutta quindi é di costo uguale a controllare se il vettore é ordinato. Però se sì usano i vettori Purtroppo non implementabili su haskell é sicuramente più efficiente perché non scorri i vettori ma accedi solo alle ultime due componenti
 
Poiché sulle liste non puoi accedere alle ultime due componenti la dovrei scorrere tutta quindi é di costo uguale a controllare se il vettore é ordinato.
Aspetta, un conto è dover scorrere tutta la lista perché non puoi accedere direttamente agli ultimi due elementi, ben diverso invece è creare nuove liste da aggiungere alla lista di liste per poi eventualmente rimuoverle come hai descritto in un tuo precedente post.
 
No faccio un esempio. prendiamo le partizioni di 5 che sono 7 e devo crearmi quelle di 6 che sono 11 quindi ne devo agggiungere 4 a quelle di 5.

La mia funzione prende quelle di 5 crea altre 7 liste e ne scarta 3 scorrendole tutte e poi le unisce a quelle di 5 (che utilizzando liste infinte hanno gia l'uno alla fine quindi vanno gia bene) (volendo c'è uno spreco di memoria)

il tuo prende 7 liste le scorre tutte e ne genera altre 4 che mette in coda alle 7. per generare il duplicato della lista ci sono due modi o lo generi con il primo passaggio per controllare gli ultimi due valori e in caso la scarti (stessa complessità di quello sopra) oppure la generi dopo ma a questo punto la devi scorrere nuovamente
 
La mia funzione prende quelle di 5 crea altre 7 liste e ne scarta 3 scorrendole tutte e poi le unisce a quelle di 5 (che utilizzando liste infinte hanno gia l'uno alla fine quindi vanno gia bene) (volendo c'è uno spreco di memoria)
Come già detto non conosco le peculiarità dell'haskell, ma mi sembra ovvio che in questo caso oltre allo spreco di memoria c'è uno spreco di tempo dovuto alla creazione ed eliminazione di liste superflue che possono essere scartate a priori. Lo spreco di tempo, oltre all'allocazione e deallocazione della memoria, è dovuto anche all'aggiornamento dei nodi della lista di liste nelle operazioni di inserimento ed eliminazione.
 
No il tempo non si spreca perché la creazione di liste é tetha di 1 e non c'è allocazione e de allocazione di memoria in haskell le liste più che eliminarle non si usano e haskell poi le cancella dalla memoria . I due programmi hanno la stessa complessità uno sulle liste uno sui vettori uno in haskell uno in c++. Infine nella lista di liste aggiungo solo quelle già giuste non tolgo nulla e ovviamente questo e leggermente più costoso ma perché ho decisamente più output del programma in c++.
 
Quello che dici non mi sembra abbia molto senso, ma se ne sei convinto...

Riflettendo sul problema sono riuscito ad ottenere lo scheletro delle partizioni di n_max, ossia l'elenco delle partizioni escluso gli "1", da cui risulta immediato estrapolare l'elenco delle partizioni per ogni n minore o uguale di n_max.
Di seguito il codice in C++ e l'output per n_max=11 e n=8:
C++:
#include <iostream>
#include <vector>

using namespace std;

vector<vector<unsigned int>> genera_scheletro_partizioni(const unsigned int n_max)
{
    vector<vector<unsigned int>> v_p(1, {1});
    unsigned int *u = new unsigned int[n_max + 1]();
    for(unsigned int n = 2; n <= n_max; v_p.push_back({n++}))
    {
        u[n] = v_p.size();
        for(unsigned int n_1 = n % 2 + 2; n_1 < n - 1; ++n_1)
        {
            for(unsigned int i = u[n - n_1], i_sup = u[n - n_1 + 1]; v_p[i].front() <= n_1 && i < i_sup; ++i)
            {
                v_p.push_back({n_1});
                v_p.back().insert(v_p.back().end(), v_p[i].begin(), v_p[i].end());
            }
        }
    }
    delete[] u;
    return v_p;
}

void stampa_vector_bidimensionale(const vector<vector<unsigned int>> &v_p)
{
    for(unsigned int i = 0; i < v_p.size(); ++i)
    {
        cout << "\n " << i + 1 << ":\t";
        for(unsigned int j = 0; j < v_p[i].size(); ++j)
        {
            cout << v_p[i][j] << " ";
        }
    }
    cout << "\n";
}

void stampa_partizioni_n(const vector<vector<unsigned int>> &v_p, const unsigned int n_max, const unsigned int n)
{
    if(n && n <= n_max)
    {
        for(unsigned int n_1 = n - 1, i = 0; !i || v_p[i - 1].front() != n; v_p[i++].size() == 1 ? --n_1 : 0)
        {
            cout << "\n " << i + 1 << ":\t";
            for(unsigned int j = 0; j < v_p[i].size(); ++j)
            {
                cout << v_p[i][j] << " ";
            }
            for(unsigned int j = n_1; j; --j)
            {
                cout << "1 ";
            }
        }
        cout << "\n";
    }
}

int main()
{
    unsigned int n_max = 11;
    unsigned int n = 8;

    vector<vector<unsigned int>> v_p = genera_scheletro_partizioni(n_max);
    stampa_vector_bidimensionale(v_p);
    stampa_partizioni_n(v_p, n_max, n);
}

Codice:
 1:     1
 2:     2
 3:     3
 4:     2 2
 5:     4
 6:     3 2
 7:     5
 8:     2 2 2
 9:     3 3
 10:    4 2
 11:    6
 12:    3 2 2
 13:    4 3
 14:    5 2
 15:    7
 16:    2 2 2 2
 17:    3 3 2
 18:    4 2 2
 19:    4 4
 20:    5 3
 21:    6 2
 22:    8
 23:    3 2 2 2
 24:    3 3 3
 25:    4 3 2
 26:    5 2 2
 27:    5 4
 28:    6 3
 29:    7 2
 30:    9
 31:    2 2 2 2 2
 32:    3 3 2 2
 33:    4 2 2 2
 34:    4 3 3
 35:    4 4 2
 36:    5 3 2
 37:    5 5
 38:    6 2 2
 39:    6 4
 40:    7 3
 41:    8 2
 42:    10
 43:    3 2 2 2 2
 44:    3 3 3 2
 45:    4 3 2 2
 46:    4 4 3
 47:    5 2 2 2
 48:    5 3 3
 49:    5 4 2
 50:    6 3 2
 51:    6 5
 52:    7 2 2
 53:    7 4
 54:    8 3
 55:    9 2
 56:    11

 1:     1 1 1 1 1 1 1 1
 2:     2 1 1 1 1 1 1
 3:     3 1 1 1 1 1
 4:     2 2 1 1 1 1
 5:     4 1 1 1 1
 6:     3 2 1 1 1
 7:     5 1 1 1
 8:     2 2 2 1 1
 9:     3 3 1 1
 10:    4 2 1 1
 11:    6 1 1
 12:    3 2 2 1
 13:    4 3 1
 14:    5 2 1
 15:    7 1
 16:    2 2 2 2
 17:    3 3 2
 18:    4 2 2
 19:    4 4
 20:    5 3
 21:    6 2
 22:    8

Quest'approccio mi sembra molto più efficiente rispetto a quello che sfrutta il passaggio dalle partizioni di n a quelle di n+1, per esempio per n_max=80 risulta circa 3 volte più veloce.
Se qualcuno vuole discutere sull'algoritmo che c'è dietro mi faccia sapere.
 
Pubblicità
Pubblicità
Indietro
Top