Generazione di combinazioni diverse da un elenco di muscoli

7

Supponiamo di avere un modello di un sistema muscolare umano che contiene i seguenti muscoli all'interno di uno specifico gruppo muscolare:

  • Braccia
    • bicipiti
    • tricipiti
    • avambracci
  • Torso anteriore
    • Abs
    • Pancia
    • spalle
  • Torso Indietro
    • trapezio
    • Lats
    • Spalle posteriori
    • Abbassa la parte posteriore
  • Lower Body
    • Quad
    • Hams
    • Hip & Butt
    • I vitelli

Ora, dato un elenco di muscoli selezionati, ho bisogno di trovare combinazioni seguendo le regole date:

Se tutti i muscoli sono selezionati da un gruppo:

Full body (All muscles) 
Arms 
Torso
Torso Front
Torso Back
Lower Body

Se ci sono gruppi completi insieme:

Arms and torso
Arms and torso front
Arms and torso back
Arms and lower body
Torso and lower body
Torso front and lower body
Torso back and lower body

Quando viene selezionata sia la parte anteriore che quella posteriore del torso, può essere considerata solo "torso".

Se ci sono più di 2 muscoli selezionati da un gruppo ma non tutti:

Parts of torso (This is the case where there are muscles selected from both front- and back-torso)
Parts of torso back
Parts of lower body

"Parti di fronte busto" e "Parti di braccia" non si verificano mai da sole in quanto hanno solo un totale di 3 muscoli, quindi tali casi possono essere rappresentati dal loro nome: es. "bicipiti e tricipiti", ecc.

Combinazioni di queste "parti" di cui sopra

Parts of arms and torso
Parts of arms and lower body
Parts of torso and lower body
Parts of  arms, torso and lower body.

Parti = i gruppi non sono pieni e ci sono più di 2 muscoli selezionati. Questa "regola di 2" è la più importante.

Esempi di output

bicipiti, tricipiti e petto: parti di braccia e torso

bicipiti, tricipiti, polpacci, bicipiti tai, tricipiti, quadricipiti, prosciutti, anca & testa a testa: Parti di braccia e parte inferiore del corpo

bicipiti, addominali, polpacci: parti di braccia, busto e parte inferiore del corpo

bicipiti, tricipiti: bicipiti e tricipiti

bicipiti, tricipiti, avambracci: braccia

petto, spalle, addominali, trapezio, dorsali, spalle posteriori, zona lombare: Torso

e così via, ci sono più varianti possibili.

Ora il mio problema è che non ho idea di come scriverlo come pulito e modulare possibile, in modo che le regole possano essere aggiunte o rimosse se necessario.

Potrei semplicemente scrivere una massiccia pila if else else che considererebbe tutti i casi, ma sai perché non lo farò.

In che modo tu affrontare questo tipo di attività?

    
posta Gerstmann 03.08.2012 - 11:33
fonte

6 risposte

4
  • Crea una classe "MuscleGroup"
  • Inizia ogni oggetto di quella classe con l'elenco dei muscoli appartenenti a quella classe e il nome del gruppo
  • aggiungi un metodo PrintRelatedParts(SetOfMuscles som) a quella classe ( SetOfMuscles dovrebbe essere un tipo di contenitore per l'input specificato)
  • il metodo dovrebbe contare quanti dei muscoli in som sono nel gruppo muscolare. Se ci sono 0, non stampa nulla, se ci sono 1 o 2, stampa i nomi dei muscoli, se ce ne sono più di 2, stampa "parti di ...", e se ci sono tutti i muscoli di quel gruppo , stampa solo il nome del gruppo.
  • aggiungi un po 'di logica per combinare l'output di tutti gli oggetti del gruppo muscolare con "and" e ","

La decisione essenziale che devi programmare qui è solo una decisione tra 4 casi, non una "massiccia pila se non altro".

    
risposta data 03.08.2012 - 12:08
fonte
1

Non penserei troppo a renderlo estensibile. I corpi umani non cambieranno in un futuro prevedibile. Modellerei le strutture dati dopo chi sono gli utenti. Sono chiropratici? Fisioterapisti? Ratti palestra? Alcuni condizionali in un posto sono spesso migliori dell'introduzione di due modelli di progettazione e una manciata di classi.

    
risposta data 03.08.2012 - 15:05
fonte
1

Odio esempi generalizzati come questo perché indovinerai sempre e la tua brillante generalizzazione potrebbe essere distrutta dalla nuova regola che hanno inventato. Quindi se possibile chiedi agli esperti del dominio se la tua astrazione ha senso per loro.

Detto questo, sembra che tu abbia una gerarchia. È in gran parte ovvio da ciò che si elenca, più il busto è un genitore del busto anteriore e posteriore. Ogni nodo nella gerarchia può descriversi con una stringa. I tuoi nodi foglia stampano semplicemente il loro nome (ad esempio "bicipiti"). La maggior parte dei tuoi nodi interni segue la tua "regola di 2" (combinando i valori stringa dei bambini o sostituendoli con un altro valore come descritto nella domanda). Il nodo busto segue fondamentalmente una "regola di 1", utilizzando l'input del bambino solo se c'è esattamente 1 bambino. Esiste un nodo radice implicito ("nodo intero corpo"?) Che gestisce la logica "and" e ",".

Potresti avere 4 diverse classi di nodi qui, o un modello di strategia, o 3 strategie, una delle quali prende il parametro "regola di X".

Questo registra un po 'più di informazioni semantiche e potrebbe generalizzare meglio - finché non scopri che un dito e un dito del piede si combinano in "cifre" anche se sono altrimenti in un ramo completamente diverso della gerarchia . Ma probabilmente lo farei con la soluzione più semplice di Doc Brown se credessi che ci sarebbero molti altri casi simili in futuro e gli esperti di dominio penseranno che si adatterebbero a questo modello.

    
risposta data 04.08.2012 - 00:30
fonte
1

EDIT Il seguente è probabilmente ciò che Doc Brown stava già suggerendo, tranne per il fatto che sono molto longevo e faccio molta confusione sull'astrazione del set.

Non sono sicuro che gli schemi orientati agli oggetti siano l'approccio giusto qui. Penso che l'astrazione principale di cui hai bisogno sia semplicemente un set.

I tuoi elementi primitivi sono "bicipiti", "tricipiti" ecc. Il tuo set "universale" è l'insieme completo di quegli elementi. "braccia" e "fronte torso" sono solo insiemi - sottoinsiemi del set universale.

Per i tuoi criteri più complessi, avrai bisogno di un ragionevole insieme di operazioni su insiemi, unione, intersezione, differenza, elementi di conteggio ecc. Ci sono schemi di progettazione che potresti usare per specificare le funzioni in base a quelle, ma penserei "Abstract Syntax Tree" piuttosto che "pattern GOF", e anche quello probabilmente è eccessivo.

Tutto ciò che serve per definire tutte le regole di classificazione è una sequenza (elenco, array, ... - forse anche un insieme) di record (o oggetti). Il test di classificazione di per sé può essere semplicemente qualsiasi rappresentazione di una funzione è più conveniente nella tua lingua - forse letteralmente una funzione o lambda, forse una sorta di delegato. Accetta l'insieme dei muscoli primitivi come parametro e restituisce vero / falso. Quella funzione è quindi solo un campo di ogni record.

Potresti definire una gerarchia di classi short grass - una classe base con un metodo di "test" puro e una classe derivata per ogni test di classificazione - ma questo mi sembra eccessivo. Se è possibile includere le funzioni (oi puntatori di funzione) nei record, è sufficiente includere un campo per trattenere la funzione. Una gerarchia di classi più complessa, che tenta di definire le relazioni tra i muscoli all'interno delle relazioni di classe, sarebbe semplicemente OOP per il proprio bene IMO - molti codici del codice da scrivere per poco o nessun beneficio.

Potresti definire un albero, ma sarebbe una struttura di dati piuttosto che una gerarchia di classi, e anche solo per l'ottimizzazione se hai a che fare con molti muscoli. Se hai davvero a che fare con più di 14 muscoli, potresti persino avere una logica automatica di costruzione degli alberi. Probabilmente si baserebbe su un ordine parziale (tipo topologico) basato sul confronto di insiemi di combinazioni di muscoli primitivi, con sottoinsieme-di-come funzione di ordinamento. E quello potrebbe essere un bel po 'di lavoro ...

  • Il risultato non formerebbe naturalmente un albero - un piccolo set potrebbe essere un sottoinsieme di molti set diversi più grandi - quindi ci sono sicuramente problemi da risolvere. O fai delle scelte arbitrarie, o costruisci un DAG (Directed Acyclic Graph) piuttosto che un albero.

  • Gli insiemi sono di combinazioni, non di muscoli. O hai a che fare con un'esplosione combinatoria, o hai bisogno di trattare le specifiche degli insiemi e dei test di classificazione in un modo più sofisticato.

IOW, non perdere tempo a farlo se non sei sicuro di averne davvero bisogno.

A seconda di come lo si utilizza con precisione, i risultati potrebbero essere anche un set: un insieme di classificazioni che si applicano piuttosto che un insieme di muscoli. Ogni membro di questo farebbe riferimento a uno dei tuoi test di classificazione - a seconda della lingua e di altri problemi, magari con una sorta di ID piuttosto che con un puntatore / riferimento al record / oggetto.

    
risposta data 04.08.2012 - 06:50
fonte
0

Qui, brevemente in Java :

if può essere sostituito dal Observable/Observer Pattern, più utile, stabile e sicuro.

Un tipo è definito in enum , ogni typeName di si riferisce a Observer , la posizione ordinal() in enum fornisce il bit del tipo [ ordinalBitOfType ].

Un muscolo ha un set di uno o più ordinalBitOfType

Il Observable ha un HashSet di ordinalBitOfType per la richiesta: enumType.ARMS.ordinal() e enumType.TORSO.ordinal()

Quindi attiva gli osservatori. Tutti interessati Observer s stampa "ciao" e può aggiornare Map o qualcos'altro in Observable , altri non fanno nulla.

Modifica
In Observable/Observer Pattern è necessario solo un if di Observer per rispondere a: is myBit contenuto nel Observable.Set ?

Se crei nuovi tipi, devi creare nuovi Oberver s, non preoccuparti di già Observer s lavori, funzioneranno bene, basta mettere i nuovi bit nei muscoli giusti.

Puoi avere Observable Pattern in Observer per% nidificato% co_de.
Fai attenzione, non puoi decidere l'ordine di if s chiamato dalla JVM.

EDIT 2: alcuni riscrittura + O'Reilly book

Il libro più chiaro su Pattern design che ho letto: "Head First Design Patterns", incoraggia a padroneggiarlo.

EDIT 3

Apro una seconda risposta perché mi rendo conto di essere stato accecato dal problema Observer , e anche perché l'istanza if/then/else è passata a un Observable che può modificarlo, è un buon modello per i parametri limitati e stabili da gestire Observer s intricato.

    
risposta data 03.08.2012 - 11:54
fonte
0

Risposta 2 :

Il vero problema è collegare gli elementi del corpo insieme.

Per questo caso abbiamo solo due tipi: muscoli e ossa [qui, raggruppati nella parte del corpo].
Quindi, se iniziamo a sviluppare un gruppo (di ossa), saremo certamente in grado di dettagliarli alla granularità delle ossa.

È ovvio che aggiungere un nuovo gruppo di nervi non sarà difficile se il modello di collegamento tra ossa e muscoli è adeguato.

Sto pensando in Java con OODB per dare esempi.
Uso OpenSource db4objects , è anche Java, possibilità di utilizzare EmbeddedObjectContainer caricato con API, nessun server, nessun problema di DBA.
Pensa come un disco con utili strumenti di database per gestire I / O complessi come Transparent Activation / Transparent Persistence e altri potenti strumenti come un gestore, ed è in grado di migrare verso un enorme OODB Versant .

Il tuo lavoro: lavora con una classe unica con tre campi:

package ElemBody;

import java.util.HashSet;
import java.util.Set;

public final class ElementBody {

    // In true life fields are 'private' with getters and setters
    public int typ; // TYP_ELEM.BONES.ordinale() Indexed
    public String name; // indexed with UniqueKey
    // HashSet (and not SortedSet for optimization) Element linked
    public Set<ElementBody> linkedElem = new HashSet<ElementBody>();

    // Print utility    
    @Override
    public final String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append(name);
        sb.append(" - ");
        for (final ElementBody el : linkedElem) {
            sb.append(el.name);
            sb.append(", ");
        }
        sb.append(linkedElem.size());
        sb.append(".");
        return sb.toString();
    }
}

I principali (noiosi) dati di caricamento / collegamento e interrogazione:

package ElemBody;

import java.io.File;
import java.util.Iterator;

import com.db4o.Db4oEmbedded;
import com.db4o.EmbeddedObjectContainer;
import com.db4o.ObjectSet;
import com.db4o.config.EmbeddedConfiguration;
import com.db4o.constraints.UniqueFieldValueConstraint;
import com.db4o.query.Query;

public class PrmBody {
    public enum TYP_ELEM {
        BONES, MUSCLES, NERVES // here, place for futur types
    }

    public static void main(final String[] args) {
        // one of possible db4o's configuration
        final EmbeddedConfiguration config = Db4oEmbedded.newConfiguration();
        // Managing index and unique key
        config.common().objectClass(ElementBody.class).objectField("type")
            .indexed(true);
        config.common().objectClass(ElementBody.class).objectField("name")
            .indexed(true);
        config.common().add(
            new UniqueFieldValueConstraint(ElementBody.class, "name"));
        // You have also an InMemory option, here the file one
        // Because of name's uniqueKey option, delete for test, in true live
        // read before create/update
        new File("Q:/tmp/ElementBody.db4o").delete();
        final EmbeddedObjectContainer eoc = Db4oEmbedded.openFile(config,
        // "ElementBody.db4o"); // in "user.home" directory
            "Q:/tmp/ElementBody.db4o");

        // Tedious option to load database.
        // Use .properties ou CSV files

        // Create BONES
        for (final String s : new String[]{"Arms", "Torso Front", "Torso Back",
            "LowerBody"}) {
            final ElementBody el = new ElementBody();
            el.typ = TYP_ELEM.BONES.ordinal();
            el.name = s;
            eoc.store(el); // save new instance
        }

        // Create MUSCLES
        final String[] muscles = {"Biceps", "Triceps", "Forearms",//
            "Abs", "Chest", "Shoulders", //
            "Trapezius", "Lats", "Rear Shoulders", "Lower Back", //
            "Quads", "Hams", "Hip & Butt", "Calves"};
        for (final String s : muscles) {
            final ElementBody el = new ElementBody();
            el.typ = TYP_ELEM.MUSCLES.ordinal();
            el.name = s;
            eoc.store(el);
        }

        // Simplified control of read type form db4o
        final Query queryA = eoc.query();
        queryA.constrain(ElementBody.class);
        queryA.descend("name").constrain("Arms").equal();
        final ElementBody arm = (ElementBody) queryA.execute().iterator()
            .next();
        for (final String s : new String[]{"Biceps", "Triceps", "Forearms",
            "Shoulders"}) {
            final Query query = eoc.query();
            query.constrain(ElementBody.class);
            query.descend("name").constrain(s).equal();
            final ObjectSet<ElementBody> musT = query.execute();
            musT.get(0).linkedElem.add(arm);
            arm.linkedElem.add(musT.get(0));
            eoc.store(musT.get(0));
        }
        eoc.store(arm);

        final Query queryB = eoc.query();
        queryB.constrain(ElementBody.class);
        queryB.descend("name").constrain("Torso Front").equal();
        final ElementBody tf = (ElementBody) queryB.execute().get(0);
        for (final String s : new String[]{"Abs", "Chest", "Shoulders"}) {
            final Query query = eoc.query();
            query.constrain(ElementBody.class);
            query.descend("name").constrain(s).equal();
            final ElementBody mus = (ElementBody) query.execute().get(0);
            mus.linkedElem.add(tf);
            tf.linkedElem.add(mus);
            eoc.store(mus);
        }
        eoc.store(tf);

        final Query queryC = eoc.query();
        queryC.constrain(ElementBody.class);
        queryC.descend("name").constrain("Torso Back").equal();
        final ElementBody tb = (ElementBody) queryC.execute().get(0);
        for (final String s : new String[]{"Trapezius", "Lats",
            "Rear Shoulders", "Lower Back", "Shoulders"}) {
            final Query query = eoc.query();
            query.constrain(ElementBody.class);
            query.descend("name").constrain(s).equal();
            final ElementBody mus = (ElementBody) query.execute().get(0);
            mus.linkedElem.add(tb);
            tb.linkedElem.add(mus);
            eoc.store(mus);
        }
        eoc.store(tb);
        eoc.commit(); // End load part // // //

        // Two samples of request
        final Query r1 = eoc.query();
        r1.constrain(ElementBody.class);
        r1.descend("typ").constrain(TYP_ELEM.BONES.ordinal()).equal();
        for (final Object obj : r1.execute()) {
            final ElementBody el = (ElementBody) obj;
            System.out.println("\n" + TYP_ELEM.BONES + "\t" + el.name);
            final Iterator<ElementBody> it = el.linkedElem.iterator();
            while (it.hasNext()) {
                System.out.println("\t" + it.next());
            }
        }

        final Query r2 = eoc.query();
        r2.constrain(ElementBody.class);
        r2.descend("typ").constrain(TYP_ELEM.MUSCLES.ordinal()).equal();
        r2.descend("name").constrain("Q").smaller();
        r2.descend("name").orderAscending();

        final ObjectSet<ElementBody> osEl = r2.execute();
        System.out.println("---------------\n" + osEl.size()
            + " muscles in db with A to P prime letter name.");
        final Iterator<ElementBody> itEl = osEl.iterator();
        int sumLink = 0;
        while (itEl.hasNext()) {
            final ElementBody elTmp = itEl.next();
            System.out.print(elTmp.name + ", ");
            sumLink += elTmp.linkedElem.size();
        }
        System.out.println("\nThose muscles have " + sumLink
            + " links to bones, LowerBody's ones excluded.");
        eoc.close();
    }
}

Ho aggiunto "Spalle" in due "Torso" per mostrare come puoi alimentare i tuoi link.

Con esempi di query (SODA), sarai in grado di selezionare tutti i gruppi necessari.

Console:

BONES   Arms
    Forearms - Arms, 1.
    Triceps - Arms, 1.
    Shoulders - Arms, Torso Back, Torso Front, 3.
    Biceps - Arms, 1.

BONES   Torso Front
    Abs - Torso Front, 1.
    Shoulders - Arms, Torso Back, Torso Front, 3.
    Chest - Torso Front, 1.

BONES   Torso Back
    Lower Back - Torso Back, 1.
    Trapezius - Torso Back, 1.
    Rear Shoulders - Torso Back, 1.
    Shoulders - Arms, Torso Back, Torso Front, 3.
    Lats - Torso Back, 1.

BONES   LowerBody
---------------
9 muscles in db with A to P prime letter name.
Abs, Biceps, Calves, Chest, Forearms, Hams, Hip & Butt, Lats, Lower Back, 
Those muscles have 6 links to bones, LowerBody's ones excluded.

Nel mio senso, non è un modello matematico, ma intuitivo:

  • qual è la vera domanda? → dove sono gli oggetti reali? (non iniziare con l'astrazione, o la soluzione tecnica, se non è necessario, e decifrare il vero problema, dietro la bella carta dei requisiti)
  • chi lavora davvero? → cosa è fatto? (prendi il posto degli oggetti, poi immagina come sei ora rappresentato: ronzio, rappresentazione non così buona no?)
  • chi e cosa è realmente necessario (uno sviluppatore pigro vuole verificare / test meno oggetti possibili e oggetti più semplici di uno "tutto fatto in casa")
  • gestiamo i dati? → è necessario un database,
  • possiamo essere più semplici? → questo richiede tempo, una notte è generalmente un buon tempo trascorso
  • è richiesto l'unico problema per l'utente finale? → giusto! guardalo mentre lavora, lui è l'unico uomo in grado di capire quello che vuole, ecco il problema: l'esperienza ti mostrerà come decifrare quelle cose "non dette ma critiche".

Se tutto [o parte] di questo è già stato fatto → usalo

A meno che non sia per uso altamente stress (o OS conosciuto solo da un genio) tutto è già scritto, e spesso ben scritto, ottimizzato e sicuro.

Qui ho usato qualcosa come 'modello di indice inverso interno' (Forse esiste con un altro nome ufficiale):
Il database ha indici sui campi (colonne per SGBDR) come tutti i database che conosci In OODB aggiungiamo lista / set / BTree / .. per collegare un'istanza di un tipo di oggetto a un'altra dello stesso tipo o ad un'altra istanza di un tipo diverso (ma sempre lo stesso tipo di istanza collegato in una lista).

Se il tuo progetto diventasse più grande e complesso, sarai in grado di utilizzare tali metodi senza difficoltà.
Nessuna complessità per caricare numerosi file CSV, nessuna vera difficoltà per aggiungere query,

  • Una classe, tre campi e metodi di base (il tuo lavoro, già fatto, aggiungi solo una query per un oggetto prima di crearlo o aggiornarlo se non vuoi eliminare ogni database per ogni tim - e altri per evitare, ascolta nel documento di riferimento)
  • Un OODB (importato) (basta configurarlo, aggiungi TA / TP, puoi inserirlo in Memoria)
  • Una classe per il file CSV (import CsvReader)
  • Una classe per caricare Csv in DB (Little job, model in PrmBody)
  • Alcune classi semplici per query dedicate (non dolorose se si capisce la query SODA)
  • Uso JSON, ma prendo lo strumento web preferito per la presentazione (potrebbe essere un mangiatore di tempo per tutti gli usi pubblici).

Sentitevi liberi di dirci se è conveniente per voi.

    
risposta data 05.08.2012 - 16:54
fonte

Leggi altre domande sui tag