numero di stringhe, quando ogni personaggio deve apparire anche volte

9

Da un po 'di tempo sto colpendo il mio cranio a questo problema, e sta davvero iniziando a frustrarmi. Il problema è:

Ho un set di caratteri, A , B , C e D . Devo dire in quanti modi è possibile creare una stringa da quei caratteri, quando la lunghezza è n e ogni carattere deve comparire anche volte.

Ad esempio, la risposta per n = 2 è 4:

AA
BB
CC
DD

La risposta per n = 4 è 40. Alcune di quelle stringhe valide sono:

AAAA
AABB
CACA
DAAD
BCCB

Sono bloccato a venire con una logica. Sento che potrebbe esserci una soluzione DP per questo. La forzatura brutale di questo è fuori discussione: il numero di soluzioni cresce rapidamente in numeri enormi.

Ho provato a disegnare su carta tutti i tipi di idee, inutilmente. Quasi tutte quelle idee che ho dovuto scartare perché la loro complessità era troppo grande. La soluzione dovrebbe essere efficiente per n = 10^4 .

Una delle mie idee era di non tenere traccia delle stringhe reali, ma solo se ogni personaggio è apparso in tempi pari o dispari. Non sono riuscito a trovare un modo per applicare questa logica.

Qualcuno può aiutarmi?

    
posta Olavi Mustanoja 09.02.2015 - 23:08
fonte

3 risposte

5

Imposta f(n,d) come una funzione che fornisce il numero di permutazioni di (pari) lunghezza n utilizzando d caratteri distinti (cioè d=4 nel tuo caso).

Chiaramente f(0,d) = 1 e f(n,1) = 1 in quanto esiste una sola disposizione quando hai un solo carattere, o zero spazi.

Ora la fase di induzione:

Per creare una stringa valida utilizzando d caratteri, prendi una stringa di lunghezza pari più corta usando d-1 caratteri e completa la lunghezza aggiungendo un multiplo pari di questo nuovo carattere. Il numero di arrangiamenti è choose(n,n_newdigits) perché puoi scegliere n_newdigit di posti fuori dalla lunghezza totale della stringa per avere la nuova cifra, e il resto viene riempito dalla stringa originale in ordine.

Per implementare questo utilizzando la ricorsione ingenua in R, ho fatto:

f <- function(n,d)
{
  if(n==0) return(1)
  if(d==1) return(1)
  retval=0
  for (i in seq(from=0, to=n, by=2)) retval=retval+f(n-i,d-1)*choose(n,i)
  return(retval)
}

f(4,4)
# 40    

f(500,4)
# 1.339386e+300 takes about 10 secs

per il tipo di numeri che ti interessa avrei pensato che sarebbe stato più efficiente memorizzare i numeri in una matrice bidimensionale, e iterare per aumentare d, ma ciò potrebbe dipendere dalla tua scelta della lingua.

    
risposta data 12.02.2015 - 13:31
fonte
4

La risposta di Miff è decisamente elegante. Dal momento che ho quasi finito il mio, comunque lo fornisco. La cosa buona è che ottengo lo stesso risultato per n = 500: -)

Sia d il numero di caratteri diversi consentiti, d = 4 nel tuo caso.

Sia n la lunghezza della stringa, in definitiva guarderai anche i valori di n.

Sia il numero di caratteri non abbinati in una stringa.

Sia N (n, d, u) il numero di stringhe di lunghezza n, ricavate da d caratteri diversi e con caratteri non accoppiati. Proviamo a calcolare N.

Ci sono alcuni casi d'angolo da osservare:

u > d ou > n = > N = 0

u < 0 = > N = 0

n% 2! = u% 2 = > N = 0.

Quando passi da n a n + 1, devi aumentare di 1 o diminuire di 1, quindi abbiamo una ricorsione secondo

N (n, d, u) = f (N (n-1, d, u-1), N (n-1, d, u + 1))

Quanti modi ci sono per ridurti di uno. Questo è facile, perché dobbiamo accoppiare uno dei tuoi u caratteri non abbinati, il che rende solo u. Quindi la seconda parte di f leggerà (u + 1) * N (n-1, d, u + 1), con l'avvertenza, naturalmente, che dobbiamo osservare che N = 0 se u + 1 > n-1 ou + 1 >. d

Una volta capito questo, è facile vedere quale sia la prima parte di f: in quanti modi possiamo aumentare u quando ci sono u-1 caratteri non abbinati. Bene, dobbiamo scegliere uno dei caratteri (k- (u-1)) che sono accoppiati.

Quindi prendendo in considerazione tutti i casi d'angolo, la formula ricorsiva per N è

N (n, d, u) = (d- (u-1)) * N (n-1, d, u-1) + (u + 1) * N (n-1, d, u +1)

Non ho intenzione di leggere in link come risolvere la ricorsione.

Invece ho scritto un codice Java. Ancora un po 'più maldestro, come lo è comunque Java a causa della sua verbosità. Ma ho avuto la motivazione di non usare la ricorsione, poiché si rompe molto presto, almeno in Java, quando lo stack trabocca a 500 o 1000 livelli di nidificazione.

Il risultato per n = 500, d = 4 e u = 0 è:

N (500, 4, 0) = 1339385758982834151185531311325002263201756014631917009304687985462938813906170153116497973519619822659493341146941433531483931607115392554498072196838958545795769042788035468026048125208904713757765805163872455056995809556627183222337328039422584942896842901774597806462162357229520744881314972303360

calcolato in 0,2 secondi, grazie alla memorizzazione dei risultati intermedi. N (40000,4,0) calcola in meno di 5 secondi. Codice anche qui: link

import java.math.BigInteger;

public class EvenPairedString2 {
  private final int nChars;  // d above, number of different chars to use
  private int count = 0;
  private Map<Task,BigInteger> memo = new HashMap<>();

  public EvenPairedString2(int nChars) {
    this.nChars = nChars;
  }
  /*+******************************************************************/
  // encodes for a fixed d the task to compute N(strlen,d,unpaired).  
  private static class Task {
    public final int strlen;
    public final int unpaired;

    Task(int strlen, int unpaired) {
      this.strlen = strlen;
      this.unpaired = unpaired;
    }
    @Override
    public int hashCode() {
      return strlen*117 ^ unpaired;
    }
    @Override
    public boolean equals(Object other) {
      if (!(other instanceof Task)) {
        return false;
      }
      Task t2 = (Task)other;
      return strlen==t2.strlen && unpaired==t2.unpaired;
    }
    @Override
    public String toString() {
      return "("+strlen+","+unpaired+")";
    }
  }
  /*+******************************************************************/
  // return corner case or memorized result or null  
  private BigInteger getMemoed(Task t) {
    if (t.strlen==0 || t.unpaired<0 || t.unpaired>t.strlen || t.unpaired>nChars
        || t.strlen%2 != t.unpaired%2) {
      return BigInteger.valueOf(0);
    }

    if (t.strlen==1) {
      return BigInteger.valueOf(nChars);
    }
    return memo.get(t);
  }

  public int getCount() {
    return count;
  }

  public BigInteger computeNDeep(Task t) {
    List<Task> stack = new ArrayList<Task>();
    BigInteger result = null;
    stack.add(t);

    while (stack.size()>0) {
      count += 1;
      t = stack.remove(stack.size()-1);
      result = getMemoed(t);
      if (result!=null) {
        continue;
      }

      Task t1 = new Task(t.strlen-1, t.unpaired+1);
      BigInteger r1 = getMemoed(t1);
      Task t2 = new Task(t.strlen-1, t.unpaired-1);
      BigInteger r2 = getMemoed(t2);
      if (r1==null) {
        stack.add(t);
        stack.add(t1);
        if (r2==null) {
          stack.add(t2);
        }
        continue;
      }
      if (r2==null) {
        stack.add(t);
        stack.add(t2);
        continue;
      }
      result = compute(t1.unpaired, r1, nChars-t2.unpaired, r2);
      memo.put(t,  result);
    }
    return result;
  }
  private BigInteger compute(int u1, BigInteger r1, int u2, BigInteger r2) {
    r1 = r1.multiply(BigInteger.valueOf(u1));
    r2 = r2.multiply(BigInteger.valueOf(u2));
    return r1.add(r2);
  }
  public static void main(String[] argv) {
    int strlen = Integer.parseInt(argv[0]);
    int nChars = Integer.parseInt(argv[1]);

    EvenPairedString2 eps = new EvenPairedString2(nChars);

    BigInteger result = eps.computeNDeep(new Task(strlen, 0));
    System.out.printf("%d: N(%d, %d, 0) = %d%n", 
                      eps.getCount(), strlen, nChars, 
                      result); 
  }
}
    
risposta data 12.02.2015 - 17:22
fonte
2

Ho provato a trovare una soluzione, ma ho fallito e ho posto la stessa domanda su Mathematics.StackExchange -even-volte-deve avvenire. Grazie a Rus maggio , ecco una soluzione, in Common Lisp:

(defun solve (n)
  (if (evenp n)
      (/ (+ (expt 4 n) (* 4 (expt 2 n))) 8)
      0))

Questo restituisce sempre 0 per valori dispari di n . Per n = 500 , ecco l'output con SBCL :

* (time (solve 500))

    Evaluation took:
      0.000 seconds of real time
      0.000000 seconds of total run time (0.000000 user, 0.000000 system)
      100.00% CPU
      51,100 processor cycles
      0 bytes consed

    1339385758982834151185531311325002263201756014631917009304687985462938813906170153116497973519619822659493341146941433531483931607115392554498072196838958545795769042788035468026048125208904713757765805163872455056995809556627183222337328039422584942896842901774597806462162357229520744881314972303360
    
risposta data 13.02.2015 - 22:09
fonte

Leggi altre domande sui tag