Generando diferentes combinaciones a partir de una lista de músculos

7

Supongamos que tenemos un modelo de un sistema muscular humano que contiene los siguientes músculos dentro de un grupo muscular específico:

  • Brazos
    • Biceps
    • tríceps
    • antebrazos
  • Frente del torso
    • Abs
    • Cofre
    • Hombros
  • Torso Back
    • Trapezius
    • Lats
    • Hombros traseros
    • espalda baja
  • Cuerpo inferior
    • Quads
    • Jamones
    • Hip & Butt
    • terneros

Ahora, dada una lista de músculos seleccionados, necesito encontrar combinaciones siguiendo las reglas dadas:

Si todos los músculos se seleccionan de un grupo:

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

Si hay grupos completos juntos:

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

Cuando se selecciona la parte delantera y trasera del torso, se puede considerar simplemente "torso".

Si hay más de 2 músculos seleccionados de un grupo pero no todos:

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

"Partes del frente del torso" y "Partes de brazos" nunca ocurren por sí solas, ya que solo tienen un total de 3 músculos, por lo tanto, esos casos pueden representarse por sus nombres: ej. "bíceps y tríceps", etc.

Combinaciones de estas "partes" antes mencionadas

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

Partes = los grupos no están llenos y hay más de 2 músculos seleccionados. Esta "regla de 2" es la más importante.

Ejemplos de salida

Bíceps, tríceps y pecho: partes de brazos y torso

bíceps, tríceps, terneros, bíceps tai, tríceps, quads, jamones, cadera y amp; trasero: partes de brazos y parte inferior del cuerpo

bíceps, abdominales, pantorrillas: partes de brazos, torso y parte inferior del cuerpo

bíceps, tríceps: bíceps y tríceps

bíceps, tríceps, antebrazos: Brazos

pecho, hombros, abdomen, trapecio, lats, hombros traseros, parte inferior de la espalda: Torso

y así sucesivamente, hay múltiples variaciones posibles.

Ahora mi problema es que no tengo idea de cómo escribir esto de la forma más limpia y modular posible, por lo que las reglas se pueden agregar o eliminar si es necesario.

Podría escribir una pila masiva de if-elseif-else que consideraría todos los casos, pero sabes por qué no lo haré.

¿Cómo usted abordará este tipo de tarea?

    
pregunta Gerstmann 03.08.2012 - 11:33

6 respuestas

4
  • Crea una clase "MuscleGroup"
  • Inicia cada objeto de esa clase con la lista de músculos que pertenecen a esa clase y el nombre del grupo
  • agregue un método PrintRelatedParts(SetOfMuscles som) a esa clase ( SetOfMuscles debería ser algún tipo de contenedor para la entrada dada)
  • el método debe contar cuántos de los músculos dados en som están en el grupo muscular. Si hay 0, no imprime nada, si hay 1 o 2, imprime los nombres de los músculos, si hay más de 2, imprime "partes de ...", y si hay todos los músculos de ese grupo , imprime solo el nombre del grupo.
  • agregue algo de lógica para combinar la salida de todos los objetos del grupo muscular con "y" y ","

La decisión esencial que debe programar aquí es solo una decisión entre 4 casos, no una "pila masiva de if-elseif-else".

    
respondido por el Doc Brown 03.08.2012 - 12:08
1

No pensaría demasiado en hacerlo extensible. Los cuerpos humanos no van a cambiar en un futuro previsible. Yo modelaría las estructuras de datos según quiénes son los usuarios. ¿Son quiroprácticos? ¿Fisioterapeutas? Ratas de gimnasio? Algunas condiciones en un solo lugar a menudo son mejores que la introducción de dos patrones de diseño y un puñado de clases.

    
respondido por el Simon Pantzare 03.08.2012 - 15:05
1

Odio generalizar ejemplos como este porque siempre estás adivinando y tu brillante generalización podría ser destruida por la nueva regla que se les ocurre. Entonces, si es posible, pregunte a los expertos en dominios si su abstracción tiene sentido para ellos.

Dicho esto, suena como si tuvieras una jerarquía. En su mayoría es obvio por lo que enumera, además de que el torso es un padre del torso delantero y el torso trasero. Cada nodo en la jerarquía puede describirse a sí mismo con una cadena. Sus nodos de hojas simplemente imprimen su nombre (por ejemplo, "bíceps"). La mayoría de los nodos interiores siguen su "regla de 2" (ya sea combinando los valores de cadena de los hijos o reemplazándolos con otro valor como se describe en la pregunta). El nodo torso básicamente sigue una "regla de 1", utilizando la entrada del niño solo si hay exactamente 1 niño. Hay un nodo raíz implícito (¿"nodo de cuerpo entero"?) Que maneja la lógica "y" y ",".

Puede tener 4 clases de nodos diferentes aquí, o un patrón de estrategia, o 3 estrategias, una de las cuales toma el parámetro "regla de X".

Esto registra un poco más de información semántica y podría generalizarse mejor, hasta que se da cuenta de que un dedo y un dedo del pie se combinan en "dígitos" aunque, de lo contrario, se encuentran en una rama completamente diferente de la jerarquía . Pero probablemente haría esto con la solución más simple de Doc Brown si creía que habría muchos más casos de este tipo en el futuro y los expertos en dominios pensaron que encajarían en este modelo.

    
respondido por el psr 04.08.2012 - 00:30
1

EDIT Lo siguiente es probablemente lo que Doc Brown ya estaba sugiriendo, a excepción de que soy adicto y tengo mucho alboroto acerca de la abstracción del conjunto.

No estoy seguro de que los patrones orientados a objetos sean el enfoque correcto aquí. Creo que la abstracción principal que necesitas es simplemente un conjunto.

Sus elementos primitivos son "bíceps", "tríceps", etc. Su conjunto "universal" es el conjunto completo de esos elementos. "brazos" y "frente del torso" son solo conjuntos, subconjuntos del conjunto universal.

Para sus criterios más complejos, necesitaría un conjunto razonable de operaciones en conjuntos: unión, intersección, diferencia, elementos de conteo, etc. Hay patrones de diseño que podría usar para especificar funciones basadas en esos, pero creo que "Árbol de sintaxis abstracto" en lugar de "patrón GOF", e incluso eso es probablemente una exageración.

Todo lo que realmente necesita para definir todas sus reglas de clasificación es una secuencia (lista, matriz, ... - quizás incluso un conjunto) de registros (u objetos). La prueba de clasificación en sí misma puede ser la representación de una función más conveniente en su idioma, tal vez literalmente una función o lambda, tal vez algún tipo de delegado. Acepte el conjunto de músculos primitivos como parámetro y devuelva verdadero / falso. Esa función es solo un campo de cada registro.

Podría definir una jerarquía de clases cortas (una clase base con un método de "prueba" puro y una clase derivada para cada prueba de clasificación), pero esto me parece una exageración. Si puede incluir funciones (o punteros de función) en los registros, solo incluya un campo para mantener la función. Una jerarquía de clases más compleja, que intenta definir las relaciones de conjunto de músculos dentro de las relaciones de clase, solo sería POO por su propio bien. OMI: gran cantidad de código para escribir con poco o ningún beneficio.

Puede definir un árbol, pero eso sería una estructura de datos en lugar de una jerarquía de clases, e incluso entonces solo para optimización si está tratando con muchos músculos. Si realmente estás lidiando con más de 14 músculos, es posible que incluso tengas cierta lógica automática de construcción de árboles. Probablemente, esto se basaría en un orden parcial (ordenamiento topológico) basado en la comparación de conjuntos de combinaciones de músculos primitivos, con la función de ordenamiento es un subconjunto de. Y eso podría ser un buen trabajo ...

  • El resultado no formaría naturalmente un árbol (un conjunto pequeño puede ser un subconjunto de muchos conjuntos grandes diferentes), por lo que definitivamente hay problemas que resolver. O haces algunas elecciones arbitrarias, o construyes un DAG (Directed Acyclic Graph) en lugar de un árbol.

  • Los conjuntos son de combinaciones, no de músculos. O se trata de una explosión combinatoria, o necesita lidiar con las especificaciones de conjuntos y pruebas de clasificación de una manera más sofisticada.

IOW, no pierdas tiempo con él a menos que estés seguro de que realmente lo necesitas.

Dependiendo de cómo use esto con precisión, los resultados también pueden ser un conjunto, un conjunto de clasificaciones que se aplican en lugar de un conjunto de músculos. Cada miembro de eso se referirá a una de sus pruebas de clasificación, dependiendo del idioma y otros problemas, tal vez por algún tipo de identificación en lugar de un puntero / referencia al registro / objeto.

    
respondido por el Steve314 04.08.2012 - 06:50
0

Aquí, brevemente en Java :

if puede ser reemplazado por el patrón Observable/Observer , más útil, estable y seguro.

Un tipo se define en enum , cada typeName de se refiere a Observer , la posición ordinal() en enum da el bit del tipo [ ordinalBitOfType ].

Un músculo tiene un conjunto de uno o más ordinalBitOfType

Los Observable tienen un HashSet de ordinalBitOfType para solicitar: enumType.ARMS.ordinal() y enumType.TORSO.ordinal()

Luego activa los observadores. Todos los involucrados Observer s imprimen "hola" y pueden actualizar Map o alguna otra cosa en el Observable , otros no hacen nada.

EDIT
En Observable/Observer Pattern solo se necesita un if por Observer para responder en: ¿está myBit contenido en el Observable.Set ?

Si crea nuevos tipos, tiene que crear nuevos Oberver s, no se preocupe por los trabajos de Observer s, funcionarán bien, solo coloque los nuevos bits en los músculos correctos.

Puedes tener Observable Pattern en un Observer para if anidado.
Tenga cuidado, no puede decidir el orden de Observer s llamado por la JVM.

EDIT 2: algo de reescritura + libro de O'Reilly

El libro más claro sobre el diseño de patrones que he leído: "Head First Design Patterns", alienta a dominarlo.

EDIT 3

Abro una segunda respuesta porque me doy cuenta de que el problema if/then/else me cegó, y también porque la instancia Observable pasó a un Observer que puede modificarlo, es un buen patrón para la administración de parámetros limitados y estables. intrincado if s.

    
respondido por el cl-r 03.08.2012 - 11:54
0

Respuesta 2 :

El problema real es vincular los elementos del cuerpo.

Para este caso solo tenemos dos tipos: músculos y huesos [aquí, agrupados en parte del cuerpo]. Por lo tanto, si comenzamos a desarrollar un grupo (de huesos), sin duda podremos detallarlos en la granularidad de los huesos.

Es obvio que agregar un nuevo grupo de nervios no será difícil si el modelo de enlace entre huesos y músculos es adecuado.

Estoy pensando en Java con OODB para dar una muestra.
Yo uso OpenSource db4objects , también es Java, posibilidad de usar EmbeddedObjectContainer cargado con la API, sin servidor, no hay problema con DBA.
Piénselo como un disco con herramientas de base de datos útiles para administrar E / S complejas como Activación transparente / Persistencia transparente, y otras herramientas poderosas como un administrador, y es capaz de migrar al gran OODB profesional Versant .

Tu trabajo: trabaja con una clase única con tres campos:

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();
    }
}

El principal, (tedioso) carga / enlace de datos y preguntas:

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();
    }
}

He agregado "Hombros" en dos 'Torso' para mostrar cómo puedes realzar tus enlaces.

Con muestras de consulta (SODA), podrá elegir todos los grupos necesarios.

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.

En mi sentido, no es un modelo matemático, sino intuitivo:

  • ¿Cuál es la verdadera pregunta? → ¿Dónde están los objetos reales? (No comience con la abstracción o la solución técnica, si no es necesaria, y descifre el problema real, detrás del hermoso documento de requisitos)
  • quien realmente trabaja? → ¿Qué se hace? (toma el lugar de los objetos, luego imagina cómo estás representado ahora: zumbido, no tan buena representación, ¿no?)
  • quién y qué es lo que realmente se necesita (un desarrollador perezoso desea verificar / probar menos objetos posibles y objetos más simples que uno 'todo hecho en casa')
  • administramos los datos? → se necesita una base de datos,
  • ¿Podemos ser más simples? → esto lleva tiempo, una noche es generalmente un buen tiempo transcurrido
  • ¿La solicitud es el único problema para el usuario final? → derecho! Míralo trabajando, él es el único hombre capaz de entender lo que quiere, aquí el problema: la experiencia te mostrará cómo descifrar esas cosas "no dichas pero críticas".

Si todo [o parte] de esto ya está hecho → utilícelo

A menos que sea para uso altamente estresante (o OS solo conocido por un genio), todo está ya escrito, ya menudo está bien escrito y optimizado, y es seguro.

Aquí he usado algo como 'modelo de índice inverso interno' (Tal vez exista bajo otro nombre oficial):
La base de datos tiene índices en los campos (columnas para SGBDR) como toda la base de datos que conoce

En OODB agregamos list / set / BTree / .. para vincular una instancia de un tipo de objeto a otra del mismo tipo, oa otra instancia de un tipo diferente (pero siempre las mismas instancias están enlazadas en una lista).

Si su proyecto se hizo más grande y más complejo, podrá utilizar dichos métodos sin dificultades.
No hay complejidad para cargar numerosos archivos CSV, no hay dificultades reales para agregar consultas,

  • Una clase, tres campos y métodos básicos (su trabajo, ya realizado, solo agregue una consulta para un objeto antes de crear o actualizar si no desea eliminar la base de datos cada vez, y otro error para evitar, escuchar en el documento de referencia)
  • Un OODB (importado) (solo configúrelo, agregue TA / TP, puede ponerlo en Memoria)
  • Una clase para archivo CSV (importar CsvReader)
  • Una clase para cargar Csv en DB (Little job, model en PrmBody)
  • Algunas clases simples para consultas dedicadas (no dolorosas si entiendes la consulta de SODA)
  • Utilizo JSON, pero tome su herramienta web preferida para la presentación (puede ser una pérdida de tiempo para todo uso público).

No dude en decirnos si es conveniente para usted.

    
respondido por el cl-r 05.08.2012 - 16:54

Lea otras preguntas en las etiquetas