2012-07-10 41 views
7

Estoy buscando crear un programa que idetifique ciertos patrones en números. No estoy seguro si esto necesita un algoritmo o solo una programación cuidadosamente pensada. No busco a alguien que me proporcione el código fuente, solo algunas ideas que invitan a la reflexión para comenzar en la dirección correcta.Programa Java para identificar patrones en números

Los números serán de 6 dígitos de 000000 a 999999. Supongo que cada número se almacenará como parte de una matriz. Entonces me gustaría probar el número contra un patrón.

Por ejemplo digamos que los 3 patrones que estoy usando son

A A A A A A - would match such examples as 111111 , 222222, 333333 etc where 
A B A B A B - would match such examples as 121212 , 454545, 919191 etc 
A (A+1) (A+2) B (B+1) (B+2) - would match such examples as 123345, 789123, 456234 

supongo que la parte que estoy atascado en es cómo asignar cada parte de la matriz entera a un valor tal que A o B

Mis pensamientos iniciales serían simplemente asignar cada parte como una letra individual. Así que si la matriz consistía en 1 3 5 4 6 8, a continuación, me gustaría crear un mapa como

A=1 
B=3 
C=5 
D=4 
E=6 
F=8 

A continuación, algunos cómo tomar el primer patrón,

AAAAAA 

y probar con algo así como si (AAAAAA = ABCDEF) AAAAAAA entonces hemos emparejado

si no, entonces intentar (ABABAB = ABCDEF), etc a través de todos mis patrones

En este escenario no hay ninguna razón por la cual el valor asignado a C no puede ser el mismo que el valor assignado a F como en el número 234874.

No estoy seguro de si esto tendrá sentido para cualquiera, pero creo que puedo refinar mi pregunta en función de los comentarios.

En resumen, estoy buscando ideas sobre cómo hacer que un programa acepte un número de 6 dígitos y devuelva a nosotros qué patrón coincide.

SOLUCIÓN

Después de los comentarios dados Me pusieron en una buena pista de abajo es lo que he creado como una solución final.

package com.doyleisgod.number.pattern.finder; 

import java.io.File; 
import java.util.ArrayList; 
import java.util.HashMap; 
import java.util.Iterator; 
import java.util.Map; 
import javax.xml.parsers.DocumentBuilder; 
import javax.xml.parsers.DocumentBuilderFactory; 
import org.w3c.dom.Document; 
import org.w3c.dom.Element; 
import org.w3c.dom.Node; 
import org.w3c.dom.NodeList; 


public class FindPattern { 
private final int[] numberArray; //Array that we will match patterns against. 
private final Document patternTree = buildPatternTree(); //patternTree containing all the patterns 
private final Map<String, Integer> patternisedNumberMap; //Map used to allocate ints in the array to a letter for pattern analysis 
private int depth = 0; //current depth of the pattern tree 

// take the int array passed to the constructor and store it in out numberArray variable then build the patternised map 
public FindPattern (int[] numberArray){ 
    this.numberArray = numberArray; 
    this.patternisedNumberMap = createPatternisedNumberMap(); 
} 

//builds a map allocating numbers to letters. map is built from left to right of array and only if the number does not exist in the map does it get added 
//with the next available letter. This enforces that the number assigned to A can never be the same as the number assigned to B etc 
private Map<String, Integer> createPatternisedNumberMap() { 
    Map<String, Integer> numberPatternMap = new HashMap<String, Integer>(); 

    ArrayList<String> patternisedListAllocations = new ArrayList<String>(); 
    patternisedListAllocations.add("A"); 
    patternisedListAllocations.add("B"); 
    patternisedListAllocations.add("C"); 
    patternisedListAllocations.add("D"); 
    Iterator<String> patternisedKeyIterator = patternisedListAllocations.iterator(); 

    for (int i = 0; i<numberArray.length; i++){ 
     if (!numberPatternMap.containsValue(numberArray[i])) { 
      numberPatternMap.put(patternisedKeyIterator.next(), numberArray[i]); 
     } 
    } 
    return numberPatternMap; 
} 

//Loads an xml file containing all the patterns. 
private Document buildPatternTree(){ 
    Document document = null; 
    try { 
    File patternsXML = new File("c:\\Users\\echrdoy\\Desktop\\ALGO.xml"); 
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 
    DocumentBuilder db = dbf.newDocumentBuilder(); 
    document = db.parse(patternsXML); 

    } catch (Exception e){ 
     e.printStackTrace(); 
     System.out.println("Error building tree pattern"); 
    } 
    return document; 
} 

//gets the rootnode of the xml pattern list then called the dfsnodesearch method to analyse the pattern against the int array. If a pattern is found a 
//patternfound exception is thorwn. if the dfsNodeSearch method returns without the exception thrown then the int array didnt match any pattern 
public void patternFinder() { 
    Node rootnode= patternTree.getFirstChild(); 
    try { 
     dfsNodeSearch(rootnode); 
     System.out.println("Pattern not found"); 
    } catch (PatternFoundException p) { 
     System.out.println(p.getPattern()); 
    } 
} 

//takes a node of the xml. the node is checked to see if it matches a pattern (this would only be true if we reached the lowest depth so must have 
//matched a pattern. if no pattern then analyse the node for an expression. if expression is found then test for a match. the int from the array to be tested 
//will be based on the current depth of the pattern tree. as each depth represent an int such as depth 0 (i.e root) represent position 0 in the int array 
//depth 1 represents position 1 in the int array etc. 
private void dfsNodeSearch (Node node) throws PatternFoundException { 
    if (node instanceof Element){ 
     Element nodeElement = (Element) node; 
     String nodeName = nodeElement.getNodeName(); 

     //As this method calls its self for each child node in the pattern tree we need a mechanism to break out when we finally reach the bottom 
     // of the tree and identify a pattern. For this reason we throw pattern found exception allowing the process to stop and no further patterns. 
     // to be checked. 
     if (nodeName.equalsIgnoreCase("pattern")){ 
      throw new PatternFoundException(nodeElement.getTextContent()); 
     } else { 
      String logic = nodeElement.getAttribute("LOGIC"); 
      String difference = nodeElement.getAttribute("DIFFERENCE"); 

      if (!logic.equalsIgnoreCase("")&&!difference.equalsIgnoreCase("")){ 
       if (matchPattern(nodeName, logic, difference)){ 
        if (node.hasChildNodes()){ 
         depth++; 
         NodeList childnodes = node.getChildNodes(); 
         for (int i = 0; i<childnodes.getLength(); i++){ 
          dfsNodeSearch(childnodes.item(i)); 
         } 
         depth--; 
        } 
       } 
      } 
     } 


    } 
} 

//for each node at a current depth a test will be performed against the pattern, logic and difference to identify if we have a match. 
private boolean matchPattern(String pattern, String logic, String difference) { 
    boolean matched = false; 
    int patternValue = patternisedNumberMap.get(pattern); 

    if (logic.equalsIgnoreCase("+")){ 
     patternValue += Integer.parseInt(difference); 
    } else if (logic.equalsIgnoreCase("-")){ 
     patternValue -= Integer.parseInt(difference); 
    } 

    if(patternValue == numberArray[depth]){ 
     matched=true; 
    } 

    return matched; 
} 


} 

la lista XML de los patrones se parece a esto

<?xml version="1.0"?> 
<A LOGIC="=" DIFFERENCE="0"> 
    <A LOGIC="=" DIFFERENCE="0"> 
     <A LOGIC="=" DIFFERENCE="0"> 
      <A LOGIC="=" DIFFERENCE="0"> 
       <pattern>(A)(A)(A)(A)</pattern> 
      </A> 
      <B LOGIC="=" DIFFERENCE="0"> 
       <pattern>(A)(A)(B)(A)</pattern> 
      </B> 
     </A> 
     <B LOGIC="=" DIFFERENCE="0"> 
      <A LOGIC="=" DIFFERENCE="0"> 
       <pattern>(A)(A)(B)(A)</pattern> 
      </A> 
      <B LOGIC="=" DIFFERENCE="0"> 
       <pattern>(A)(A)(B)(B)</pattern> 
      </B> 
     </B> 
    </A> 
    <A LOGIC="+" DIFFERENCE="2"> 
     <A LOGIC="+" DIFFERENCE="4"> 
      <A LOGIC="+" DIFFERENCE="6"> 
       <pattern>(A)(A+2)(A+4)(A+6)</pattern> 
      </A> 
     </A> 
    </A> 
    <B LOGIC="=" DIFFERENCE="0"> 
     <A LOGIC="+" DIFFERENCE="1"> 
      <B LOGIC="+" DIFFERENCE="1"> 
       <pattern>(A)(B)(A+1)(B+1)</pattern> 
      </B> 
     </A> 
     <A LOGIC="=" DIFFERENCE="0"> 
      <A LOGIC="=" DIFFERENCE="0"> 
       <pattern>(A)(B)(A)(A)</pattern> 
      </A> 
      <B LOGIC="+" DIFFERENCE="1"> 
       <pattern>(A)(B)(A)(B+1)</pattern> 
      </B> 
      <B LOGIC="=" DIFFERENCE="0"> 
       <pattern>(A)(B)(A)(B)</pattern> 
      </B> 
     </A> 
     <B LOGIC="=" DIFFERENCE="0"> 
      <A LOGIC="=" DIFFERENCE="0"> 
       <pattern>(A)(B)(B)(A)</pattern> 
      </A> 
      <B LOGIC="=" DIFFERENCE="0"> 
       <pattern>(A)(B)(B)(B)</pattern> 
      </B> 
     </B> 
     <A LOGIC="-" DIFFERENCE="1"> 
      <B LOGIC="-" DIFFERENCE="1"> 
       <pattern>(A)(B)(A-1)(B-1)</pattern> 
      </B> 
     </A> 
    </B> 
    <A LOGIC="+" DIFFERENCE="1"> 
     <A LOGIC="+" DIFFERENCE="2"> 
      <A LOGIC="+" DIFFERENCE="3"> 
       <pattern>(A)(A+1)(A+2)(A+3)</pattern> 
      </A> 
     </A> 
    </A> 
    <A LOGIC="-" DIFFERENCE="1"> 
     <A LOGIC="-" DIFFERENCE="2"> 
      <A LOGIC="-" DIFFERENCE="3"> 
       <pattern>(A)(A-1)(A-2)(A-3)</pattern> 
      </A> 
     </A> 
    </A> 
</A> 

y mi patrón que se encuentra clase de excepción es el siguiente

package com.doyleisgod.number.pattern.finder; 

public class PatternFoundException extends Exception { 
private static final long serialVersionUID = 1L; 
private final String pattern; 

public PatternFoundException(String pattern) { 
    this.pattern = pattern; 
} 

public String getPattern() { 
    return pattern; 
} 

} 

No está seguro de si alguno esto ayudará a cualquier persona con un parecido problema o si alguien tiene algún comentario sobre cómo funciona esto sería genial escucharlos.

+2

Parece una expresión regular para mí. – Bill

+0

Bueno, no codificaría una coincidencia exacta (es decir, situaciones de tipo A = 1). Probablemente quieras considerar que cada personaje diferente que se encuentra sea un dígito diferente. Las separaciones de espacio probablemente deberían ser números diferentes (no dígitos). –

+0

Esto es cosas de IA. Douglas Hofstadter hizo su portador en ese descubrimiento de patrones. –

Respuesta

2

sugeriría a construir la máquina de estados:

A.Inicialización con patrones:

  1. Normalizar todos los patrones
  2. Elaborar un árbol con la profundidad = 6, que representa a todos los patrones a partir de la raíz y todas las opciones posibles en cada profundidad.

máquina de estado B. Ejecutar


A.1. Normalización de patrones

AAAAAA => A0 A0 A0 A0 A0 A0

CACACA => A0 B0 A0 B0 A0 B0 (siempre comienzan con A, y luego B, C, D, etc.)

B B + 1 B + 2 A A + 1 A + 2 => A0 A1 A2 B0 B1 B2

Por lo tanto, siempre tiene el patrón normalizado de inicio con A0.

A.2. Elaborar un árbol

1.  A0 
    /| \ 
2. A0 B0 A1 
     | | | 
3. A0 A0 A2 
     | | | 
4. A0 B0 B0 
     | | | 
5. A0 A0 B1 
     | | | 
6. A0 B0 B2 
     | | | 
    p1 p2 p3 

máquina de estado B. Ejecutar

Uso Depth-first search algorithm utilizando la recursividad para encontrar el patrón emparejado.

¿Tiene sentido?

+0

Gracias Yuri, Esto parece haberme iniciado en la dirección correcta. el método que he tomado es crear una estructura de patrones en árbol. de modo que todos los patrones estarían contenidos en el árbol y con la técnica DFS para elimitar las ramas en el nivel más alto, eliminando así múltiples patrones. –

0

Puede dividir sus números en una matriz de bytes (o ints si lo prefiere), uno para cada personaje. Cada patrón podría definirse en términos de comparación directa entre los elementos apropiados de la matriz.

ababab=a[0]==a[2] && a[2]==a[4] && a[1]==a[3] && a[3]==a[5] && a[0]!=a[1] 
aaabbb=a[0]==a[1] && a[1]==a[2] && a[3]==a[4] && a[4]==a[5] && a[0]!=a[3] 
fedcba=(a[0]-a[1])==1 && (a[1]-a[2])==1 && (a[2]-a[3])==1 && (a[3]-a[4])==1 && (a[4]-a[5]==1) 
0

Esto es fácil con la siguiente función de emparejamiento muy rápida.

Dado que los patrones son matrices de PatternElement, donde PatternElement consiste en una letra y un número entero.

Dado que los números son matrices de dígitos.

Ahora llame a una función de coincidencia() con cada combinación de Número y Dígito (anidados para bucle).

La función de coincidencia itera sobre patrón y número de izquierda a derecha, y hace un reemplazo de los números para que coincidan con las letras en el patrón. Si se reemplaza un dígito, reemplace todos los mismos dígitos a la derecha también. Si llega a un índice que no es un dígito, porque ya está reemplazado, entonces verifique si este elemento coincide con el patrón.

Example 1 iterations. Pattern: A B A C Number 3 2 3 5 
          ^    ^
        1.       A 2 A 5 replace 3 by A. 
           ^    ^
        2.       A B A 5 replace 2 by B 
           ^    ^ 
        3.       A B A 5 Existing A matches pattern. Good 
            ^    ^
        4.       A B A C replace 5 by C. SUCCESS 

Para el (n + 2) se podría hacer lo mismo, haciendo un poco de matemática adicional durante la sustitución o adaptación.

NOTA: No es necesario que el número consista solo en dígitos. Si te gusta reemplazar cualquier char, ¡incluso puedes combinar patrones similares! Entonces A B C A B C también coincidiría con D F G D F G en la forma genérica.

0

Usted puede inferir limitaciones y luego usar retrodetección algoritmos para averiguar si determinado número coincide con el patrón (se refiere a veces como constraint satisfaction problem).

Por ejemplo, denotemos cada uno de los 6 dígitos d1, d2, d3, d4, d5 y d6, respectivamente.Entonces, el patrón A (A+1) (A+2) B (B+1) (B+2) puede ser reescrita para el siguiente conjunto de reglas/restricciones:

dx = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 
d2 = d1 + 1 
d2 = d1 + 2 
d4 = d3 + 1 
d5 = d3 + 2 

En los ejemplos que puedo ver sólo 2 tipos de reglas inferidas - uno para la igualdad de dígitos (d1 = d2 si sus posiciones en el patrón tiene el mismo carácter) y otro para las operaciones aritméticas (no sé si A y B deberían ser números diferentes, y si es así necesitarás un tipo adicional de reglas para la desigualdad). Todos los tipos pueden traducirse trivialmente en restricciones.

El árbol de posibles soluciones se puede compilar a partir de estas limitaciones. Se puede comenzar con algo como:

[A = ?] 
|-- 1 
|-- 2 
|-- 3 
| |-- [B = ?] 
|  |-- 1 
...  ... 

y luego contener restricciones sobre otros nodos.

Puede ser difícil implementar el algoritmo de retroceso correctamente por su cuenta, por lo que tiene sentido encontrar la implementación de Prolog para su plataforma (consulte la pregunta this para Java) y luego simplemente traducir las restricciones a las reglas de Prolog.

0

Para los patrones específicos que mencionó como ejemplos, se pueden escribir programas simples para verificar si las cadenas de entrada coinciden.

Si los patrones son más complicados que los que menciona, puede usar Context-free grammar y expresiones regulares para especificar sus reglas. Y hay herramientas como lex y yacc que procesarán cadenas de entrada de acuerdo con las reglas especificadas y devolverán si coinciden o no.

Cuestiones relacionadas