13 votos

Manera rápida de conseguir un puesto de combinación (sin repeticiones)

Esta pregunta tiene una inversa: (Rápido) Obtener una combinación dada su posición en (inversa)lexicográfica del orden


¿Cuál sería la manera más eficiente a translate una combinación de $k$-tupla en sus posiciones dentro de la $\left(\!\!\binom{n}{k}\!\!\right)$ combinaciones?
Necesito esto para ser rápido para las combinaciones de $\left(\!\!\binom{70}{7}\!\!\right)$ orden de magnitud muy grande, pero no superior a 2 millones de euros (encaja en int32 valor máximo).

A continuación es un ejemplo de $\left(\!\!\binom{6}{3}\!\!\right)$, donde el objetivo es rápidamente traducir (a, d, f) tupla a valor 9, mientras que en el problema real $k$ oscila entre las 5 y 8.

$$\begin{array} {cccccc|c} a&b&c&d&e&f&^{combination}/_{sort\_order}& \\\hline x&x&x& & & &1\\ x&x& &x& & &2\\ x&x& & &x& &3\\ x&x& & & &x&4\\ x& &x&x& & &5\\ x& &x& &x& &6\\ x& &x& & &x&7\\ x& & &x&x& &8\\ x& & &x& &x&9\\ x& & & &x&x&10\\ .&.&.&.&.&.&.\\ & & &x&x&x&20\\ \end{array}$$

Sé que yo podría pre-calcular todas las combinaciones y revertir la búsqueda de diccionario. Sin embargo, el diccionario no sería eficiente en términos de uso de memoria. Por lo tanto estoy buscando, ya sea para el cálculo basado en el enfoque, o una más eficiente de la estructura de datos para realizar esta asignación.

10voto

Galaxy Puntos 105

Nos deja denotar su tupla [a b c] [1 1 1 0 0 0] y así sucesivamente.

Definir $\binom{n}{r}=0$ $n<r$

Para su tupla: $[a d f] = [1 0 0 1 0 1]$ $$P = 1\cdot \binom{0}{1}+0\cdot \binom{1}{1}+0\cdot \binom{2}{1}+1\cdot \binom{3}{2}+0\cdot\binom{4}{2}+1\cdot\binom{5}{3} + 1$$ $$P=0 + 0 +0 +3+0+10+0+1 = 14$$

Algoritmo General:

  • Calcular el valor de posición de cada dígito binario usando $\binom{n}{r}$
  • Tome $n$ como la posición de los dígitos de derecha a izquierda, de izquierda dígitos $n=0$.
  • Escribir $r$ para cada posición como el número de "unos" que cuentan de izquierda, incluyendo el de la posición actual.

Ejemplo 1: [a b c] = [1 1 1 0 0 0]

Calcular la posición de la tupla como la suma: $$P = 1\cdot \binom{0}{1}+1\cdot \binom{1}{2}+1\cdot \binom{2}{3}+0\cdot \binom{3}{3}+0\cdot\binom{4}{3}+0\cdot\binom{5}{3} + 1$$ $$P=0 + 0 +0 +0+0+0+0+1 = 1$$

Ejemplo-2: [d e f] = [0 0 0 1 1 1] $$P = 0\cdot \binom{0}{0}+0\cdot \binom{1}{0}+0\cdot \binom{2}{0}+1\cdot \binom{3}{1}+1\cdot\binom{4}{2}+1\cdot\binom{5}{3} + 1$$ $$S=0+0+0+3+6+10+1=20$$

El único de añadir UNO porque no partir de cero.

6voto

JiminyCricket Puntos 143

Voy a re-etiquetar su (a,d,f) a (1,4,6) y se denota por a$(i_1,i_2,i_3)$, por lo que podemos calcular con ella.

Inicio en el índice de $\binom nk$. Mover el $k$-ésima a la izquierda por $1$ reduce el índice de $1=\binom{n-j}0$. Mover el $(k-1)$-ésima a la izquierda de $j$ $j-1$reduce el índice de $n-j=\binom{n-j}1$. En general, moviendo la $(k-m)$-ésima a la izquierda de $j$ $j-1$reduce el índice de $\binom{n-j}m$. Así, el índice es

$$ \binom nk-\sum_{m=0}^{k-1}\;\sum_{j=i_{k-m}+1}^{n-m}\binom{n-j}m\;. $$

You can easily precalculate the inner sums so that you can look them up using $m$ and $i_{k-m}$, so you just need to add up the $k$ terms in the sum over $m$ to get the index.

P.S.: That was unnecessarily complicated; the inner sum simplifies, and the result is

$$ \binom nk-\sum_{m=0}^{k-1}\binom{n-i_{k-m}}{m+1}\;. $$

Usted probablemente puede derivar que más directamente, pero ya que estás, quizás, interesados sólo en un resultado en la práctica y no de la forma más elegante de derivados, lo voy a dejar.

4voto

ccorn Puntos 4924

Su tupla de pedidos es lexicográfica y su a-ser-calcula la posición está basada en uno, como son el símbolo de los códigos de $a,b,\ldots$ utilizado en @joriki la respuesta; pero por el bien de la simplicidad voy a utilizar inversa orden lexicográfico y a partir de cero posiciones y códigos de letra. La conversión se realiza mediante la sustitución de @joriki $(i_1,\ldots,i_k)$ con $(n-i_k,\ldots,n-i_1)$ y la sustitución de la posición resultante de la fórmula con su distancia a $\binom{n}{k}$. El resultado es, pues, coherente con @joriki de la fórmula.

He utilizado tales cálculos para la compresión de multi-índices en (inclinada)simétrica tensores; por lo tanto, voy a tomar un poco de vocabulario a partir de que dominio.

Vamos a definir un $k$-índice de estar un $k$-tupla de estrictamente creciente de números enteros no negativos. Usted puede tener que ordenar en consecuencia y a no permitir entradas duplicadas.

$k$-índices puede ser totalmente ordenado en sentido inverso-lexicográfica manera: La ordenación se realiza por el último elemento, en caso de igualdad, por el siguiente al último elemento, y así sucesivamente.

Para un $k$-índice de $I$, vamos a definir su posición (o índice comprimido) $\operatorname{ordx}(I)$ como el número de $k$-índices inversa-lexicográficamente menor que $I$.

Tenga en cuenta que $\operatorname{ordx}(I) = 0$ para los más pequeños $k$-índice de $I=(0,\ldots,k-1)$.

Necesitamos una notación para trunca tuplas. Denotando $I = (i_0,\ldots,i_{k-1})$, deje $I_m = (i_0,\ldots,i_{k-m-1})$ por entero$m$$0\leq m<k$. Que es $I$ con la última $m$ elementos cortados.

Ahora un $k$-índice de $J=(j_0,\ldots,j_{k-1})$ es inversa-lexicográficamente menor que $I$ si y sólo si

  • $j_{k-1} < i_{k-1}$; hay $\binom{i_{k-1}}{k}$ tales $k$-índices; o
  • $k>1$, e $j_{k-1} = i_{k-1}$, y $J_1$ es inversa lexicográficamente menor que $I_1$. Esta condición implica una comparación de $(k-1)$-índices.

Procediendo por inducción, llegamos a la fórmula

$$\operatorname{ordx}(I) = \sum_{r=1}^k\binom{i_{r-1}}{r}$$

It is worth noting that this formula does not depend on the upper bound $n$ para el índice de los elementos.

El coeficiente binomial valores pueden calcularse sobre la marcha mediante la inicialización y actualización de un segmento del triángulo de Pascal. Definir $$b_{j}^{(r)} = \binom{j+r}{r} = \begin{cases} 1 & \text{if %#%#% or %#%#%} \\ b_{j}^{(r-1)} + b_{j-1}^{(r)} & \text{if %#%#% and %#%#%} \end{casos}$$ Tan sólo tenemos que inicializar y actualización de una matriz $r=0$$ En la práctica, se antepone un $j=0$ a la matriz en orden a la cuenta para el caso de $r>0$ que requiere un cero coeficiente binomial.

En Python (que utiliza índices con base cero y semi-abierta rangos):

def ordx(idx):
    """
    Turns a multi-index of strictly increasing nonnegative integers
    into a 1-dimensional zero-based index.
    """
    s = 0
    b = [0] + [1] * (idx[-1] + 1 - len(idx))    # [0, 1, 1, ...]
    for r,i in enumerate(idx):      # (0,idx[0]), (1,idx[1]), ...
        for j in xrange(2, len(b)): # 2, ..., len(b)-1
            b[j] += b[j-1]          # binomial(j+r, r+1)
        s += b[i - r]
    return s

Además: Si desea permitir duplicar tupla de elementos, se puede transformar en que problema al añadir a cada elemento de a $j>0$ el sub-índice de $$B^{(r)} = \left(b_0^{(r)},\ldots,b_{i_{k-1}-k}^{(r)}\right)$ y computación ordx para la modificación de la tupla que ahora tiene estrictas incrementos. Para ese caso de uso, el código anterior se obtiene simplificado un poco:

def ords(idx):
    """
    Turns a multi-index of nondecreasing nonnegative integers
    into a 1-dimensional zero-based index.
    """
    s = 0
    b = [0] + [1] * idx[-1]         # [0, 1, 1, ..., 1]
    for i in idx:
        for j in xrange(2, len(b)): # 2, ..., len(b)-1 = idx[-1]
            b[j] += b[j-1]
        s += b[i]
    return s

Este calcula $0$$ Una función de este tipo ords podría ser utilizado para la compresión de un criterio de multi-índice totalmente simétrica tensores a un plano índice que elimina la redundancia.

Actualización: Los algoritmos anteriores son simples, pero la necesidad de actualizar una matriz de binomio los coeficientes para cada elemento de índice. En consecuencia, la ejecución de la anterior ords necesidades $i_{r-1} = r-1$ adiciones y una matriz b de duración $i_r$. Para ordx reemplace$r$$$\operatorname{ords}(I) = \sum_{r=0}^{k-1}\binom{i_r + r}{r+1}$. Se puede reducir el recuento de operación y uso de memoria de computación cada uno necesita coeficiente binomial directamente de la anterior. Esto requiere de una secuencia de multiplicaciones y divisiones en lugar de sólo agregados, pero se reduce el binomio de la contabilidad a una variable escalar y mantiene total ords de recuento de operación en $k\,i_{k-1}$. En consecuencia, ordx operación recuento $i_{k-1}+1$. Aquí es una muestra de Python implementación de la optimización de la $i_{k-1}$ (con // que denota la división entera):

def ordx_opt(idx):
    """
    Turns a multi-index of strictly increasing nonnegative integers
    into a 1-dimensional zero-based index.
    """
    s = 0
    j = 1
    b = 1
    for r,i in enumerate(idx):  # (0,idx[0]), (1,idx[1]), ...
        if i == r: continue     # skipping terms with zero binomial coeff
        # b == binomial(j+r-1, r), update to j == i - r
        while j < i - r:
            b *= j + r
            b //= j
            j += 1
        # b == binomial(i-1, r), update to binomial(i, r+1)
        b *= i
        b //= r + 1
        s += b
    return s

Y la correspondiente optimizado $i_{k-1}-k+1$:

def ords_opt(idx):
    """
    Turns a multi-index of nondecreasing nonnegative integers
    into a 1-dimensional zero-based index.
    """
    s = 0
    j = 1
    b = 1
    for r,i in enumerate(idx):
        if i == 0: continue     # skipping terms with zero binomial coeff
        # b == binomial(j+r-1, r), update to j == i
        while j < i:
            b *= j + r
            b //= j
            j += 1
        # b == binomial(i+r-1, r), update to binomial(i+r, r+1)
        b *= i + r
        b //= r + 1
        s += b
    return s

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X